How to Implement Custom Memory Types Beyond the Built-in Ontology in MCP Memory Service

You can extend the memory taxonomy at runtime by setting the MCP_CUSTOM_MEMORY_TYPES environment variable with a JSON payload defining new base types and subtypes, which the ontology module automatically merges with the built-in taxonomy without modifying source code.

MCP Memory Service uses a formal ontology to classify every stored memory, but you are not limited to the default taxonomy. The doobidoo/mcp-memory-service repository provides a configuration-driven mechanism that lets you define custom memory types for domain-specific needs—such as legal contracts, sales opportunities, or HR records—while maintaining full compatibility with the validation and query APIs.

Understanding the Memory Type Ontology

The service maintains its core taxonomy in src/mcp_memory_service/models/ontology.py. This file defines the static TAXONOMY dictionary that organizes memories into hierarchical base types (like episodic, semantic, or procedural) and their subtypes. When you store or retrieve memories, the system validates the memory_type field against this taxonomy to ensure data consistency and enable filtered queries.

Rather than editing ontology.py directly, the architecture supports runtime extension through environment-based configuration. All downstream components—including the REST API at /api/memories, the Python client, and the internal MemoryService—query a merged view of the taxonomy through cached helper functions like get_all_types() and validate_memory_type(). This means custom types become first-class citizens immediately after configuration.

Configuring Custom Memory Types via Environment Variables

The extension point is the MCP_CUSTOM_MEMORY_TYPES environment variable. Before starting the server, export a JSON object where keys are base type names and values are lists of subtypes.

JSON Structure for Custom Types

The payload must follow the schema {base_type: [subtype, ...]}. You can introduce entirely new base types or extend existing ones by adding subtypes to a base that already exists in the built-in taxonomy.

export MCP_CUSTOM_MEMORY_TYPES='{
  "legal": ["contract", "clause", "obligation"],
  "sales": ["opportunity", "objection", "competitor"],
  "meeting": ["board_meeting"]
}'

In this example, legal and sales are new base types, while meeting extends an existing base with an additional subtype. The system validates the JSON at startup; invalid entries are logged and ignored, ensuring the service always falls back to the safe, built-in ontology.

Runtime Loading and Validation Architecture

The ontology module implements a sophisticated loading and caching system to handle custom types efficiently.

Loading Custom Types from Configuration

The private method _load_custom_types_from_config() in src/mcp_memory_service/models/ontology.py (lines 196–252) reads the MCP_CUSTOM_MEMORY_TYPES environment variable, parses the JSON, and returns a dictionary mapping base types to their subtype lists. This method includes error handling that logs malformed entries without crashing the service, preserving operational stability.

Merging with the Built-in Taxonomy

The method _get_merged_taxonomy() (lines 555–592) starts from the static TAXONOMY dictionary and overlays the custom types. If a custom base type already exists, its subtypes are appended; if it is new, it is added to the hierarchy. The result is cached in memory for fast lookups by validation functions. You can inspect whether custom types are active by checking the startup logs generated by src/mcp_memory_service/config.py (lines 1087–1101), which explicitly reports the presence of custom configuration.

Cache Invalidation Strategies

During testing or dynamic reconfiguration scenarios, you can force a reload by calling clear_ontology_caches() (lines 28–34). This removes the in-memory caches, causing the next validation call to re-execute the merge logic and pick up any changes to the environment variable. The test suite in tests/test_ontology.py uses this mechanism to guarantee test isolation.

Implementation Examples

Environment Setup and Server Startup

Configure the environment variable and start the HTTP server. The custom taxonomy merges automatically during initialization.

export MCP_CUSTOM_MEMORY_TYPES='{
  "legal": ["contract", "clause", "obligation"],
  "sales": ["opportunity", "objection", "competitor"]
}'

# Optional: clear caches if reloading in a running Python session

python -c "from mcp_memory_service.models import ontology; ontology.clear_ontology_caches()"

# Start the service

memory server --http

Validating Types in Python

Use the MemoryTypeOntology class to verify that custom types are recognized before storing memories.

from mcp_memory_service.models.ontology import MemoryTypeOntology

# Validate custom base types and subtypes

assert MemoryTypeOntology.validate_memory_type("legal") is True
assert MemoryTypeOntology.validate_memory_type("contract") is True

# Verify hierarchy relationships

assert MemoryTypeOntology.get_parent_type("contract") == "legal"

# Retrieve all available types including custom ones

all_types = MemoryTypeOntology.get_all_types()

Querying via REST API

Once configured, you can store and filter memories using custom types through the HTTP interface.

import httpx

async def store_legal_memory():
    async with httpx.AsyncClient() as client:
        # Store a contract memory

        await client.post(
            "http://localhost:8000/api/memories",
            json={
                "content": "NDA signed with Acme Corp.",
                "memory_type": "contract"
            },
            headers={"X-Agent-ID": "legal-team"}
        )
        
        # Query by custom base type

        response = await client.get(
            "http://localhost:8000/api/memories?memory_type=legal"
        )
        return response.json()
GET /api/memories?memory_type=legal HTTP/1.1
Host: localhost:8000
Authorization: Bearer <token>

Summary

  • Configure via environment: Set MCP_CUSTOM_MEMORY_TYPES with a JSON object to define new base types and subtypes without editing source code.
  • Automatic merging: The ontology module merges custom types with the built-in TAXONOMY at startup, caching results for performance (see _get_merged_taxonomy() in src/mcp_memory_service/models/ontology.py).
  • Validation support: All APIs recognize custom types immediately because they use MemoryTypeOntology.validate_memory_type() and other cached helpers that query the merged view.
  • Safe fallback: Invalid JSON in the environment variable is logged and ignored, ensuring the service remains operational using the default ontology.
  • Cache control: Call clear_ontology_caches() to force a reload during testing or dynamic reconfiguration scenarios.

Frequently Asked Questions

What happens if I provide invalid JSON in MCP_CUSTOM_MEMORY_TYPES?

The _load_custom_types_from_config() method catches parsing errors and schema violations, logs a warning message, and returns an empty dictionary. The service continues operating with only the built-in taxonomy, ensuring high availability even with configuration mistakes.

Can I extend existing base types like "episodic" or "semantic"?

Yes. When you include a base type name that already exists in the static TAXONOMY, the merger appends your custom subtypes to the existing list. For example, adding "meeting": ["board_meeting"] extends the meeting base type with a new subtype while preserving all original entries.

Do I need to restart the server to add new custom types?

Yes. The environment variable is read once at startup by the ontology initialization logic. To apply new custom types, you must restart the service process. However, if you are using the Python client in a long-running interactive session, you can call clear_ontology_caches() and re-import the module to pick up changes, though a full restart is recommended for production consistency.

How can I verify that my custom types loaded correctly?

Check the startup logs generated by src/mcp_memory_service/config.py (lines 1087–1101), which reports whether custom memory types are present. Additionally, you can programmatically verify by calling MemoryTypeOntology.get_all_types() and confirming your custom base types and subtypes appear in the returned dictionary.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →