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

> Learn how to implement custom memory types in MCP Memory Service by extending the ontology at runtime. Set the MCP_CUSTOM_MEMORY_TYPES variable with a JSON payload to add new types without code changes.

- Repository: [Henry/mcp-memory-service](https://github.com/doobidoo/mcp-memory-service)
- Tags: how-to-guide
- Published: 2026-02-28

---

**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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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.

```bash
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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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.

```bash
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.

```python
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.

```python
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()

```

```http
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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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`](https://github.com/doobidoo/mcp-memory-service/blob/main/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.