# Adding Custom MCP Tools to the MemPalace Server: A Complete Developer Guide

> Learn to add custom MCP tools to your MemPalace server. This guide details defining Python handlers, creating JSON Schemas, and registering your tools for seamless integration.

- Repository: [MemPalace/mempalace](https://github.com/MemPalace/mempalace)
- Tags: how-to-guide
- Published: 2026-06-07

---

**You add custom MCP tools to the MemPalace server by defining a Python handler function, creating a JSON Schema for input validation, and registering both in the `TOOLS` dictionary inside [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py) with the required `mempalace_` prefix.**

The MemPalace MCP server provides a JSON-RPC interface that exposes callable operations known as *tools*—functions that read, write, or maintain the palace state. According to the MemPalace source code, extending the server requires understanding the central registry where all tools are defined and how the dispatch loop validates and executes incoming requests.

## Understanding the TOOLS Registry Architecture

All MCP tools are defined in the **`TOOLS`** dictionary located in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py) (lines 2269-2275). This registry maps method names to their implementations using a strict structure that the dispatch loop depends on.

Each tool entry must contain three fields:

- **`description`** — Human-readable text displayed in protocol documentation and client UIs.
- **`input_schema`** — A JSON-Schema object describing expected arguments, types, and required fields.
- **`handler`** — The Python callable that implements the tool’s logic.

When the MCP server receives a JSON-RPC request, the dispatch loop (around lines 2800-2810) looks up the method name in `TOOLS`, validates supplied parameters against `input_schema`, and executes the `handler` function. The server only exposes methods that begin with the **`mempalace_`** prefix; any entry lacking this prefix will not be accessible to clients.

## The Three-Step Implementation Process

Adding a custom tool requires completing three distinct steps before restarting the server.

### Step 1: Write the Handler Function

The handler function must accept keyword arguments that match the keys defined in your JSON Schema. It should return a JSON-serializable Python object—typically a dictionary—and must never perform side effects outside the palace context (such as unauthorized network calls).

Place your handler function anywhere in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py), typically alongside other tool implementations. Ensure it performs input validation and returns explicit error messages for invalid inputs.

### Step 2: Define the JSON Input Schema

Create a JSON Schema that describes your tool’s expected parameters. This schema is used both for client-side validation and for generating help text. Keep schemas simple, reuse existing types where possible, and explicitly mark required fields.

### Step 3: Register in the TOOLS Dictionary

Insert your tool into the `TOOLS` dictionary using `TOOLS.update()` to avoid disturbing existing entries. The registration must occur before the server’s dispatch loop initializes—place it immediately after existing tool definitions around line 2270.

```python
TOOLS.update(
    {
        "mempalace_custom_tool": {
            "description": "Human-readable description of what this tool does.",
            "input_schema": YOUR_SCHEMA,
            "handler": your_handler_function,
        }
    }
)

```

## Complete Implementation Example

Below is a minimal, complete example that adds a tool named `mempalace_echo`. This tool accepts a message string and returns it converted to uppercase, demonstrating the handler, schema, and registration pattern.

```python

# --- Handler Function --------------------------------------------------------

def tool_echo(message: str) -> dict:
    """
    Return the supplied `message` converted to uppercase.
    """
    if not isinstance(message, str):
        return {"error": "`message` must be a string"}
    return {"result": message.upper()}


# --- JSON Schema -------------------------------------------------------------

ECHO_SCHEMA = {
    "type": "object",
    "properties": {
        "message": {
            "type": "string",
            "description": "Text to be echoed back in uppercase",
        }
    },
    "required": ["message"],
    "additionalProperties": False,
}


# --- Registration (place inside TOOLS dict around line 2270) ------------------

TOOLS.update(
    {
        "mempalace_echo": {
            "description": "Echo a message back in uppercase – useful for testing the MCP pipeline.",
            "input_schema": ECHO_SCHEMA,
            "handler": tool_echo,
        }
    }
)

```

## Testing and Validation

After editing [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py), restart the server by running `python -m mempalace.mcp_server …` to load the new registration.

Test the tool using a standard JSON-RPC client. The request format follows the MCP protocol specification:

```json
{
  "jsonrpc": "2.0",
  "method": "mempalace_echo",
  "params": {"message": "hello world"},
  "id": 1
}

```

The server responds with:

```json
{
  "jsonrpc": "2.0",
  "result": {"result": "HELLO WORLD"},
  "id": 1
}

```

## Safety, Logging, and Performance Considerations

### Stdio Protection and Structured Logging

The MemPalace MCP server deliberately redirects `stdout` to `stderr` (see lines 26-33 in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py)) to prevent stray print statements from breaking the JSON-RPC stream. **Never use standard `print()`** in your handler functions.

Instead, use the dedicated logger instance:

```python
logger = logging.getLogger("mempalace_mcp")
logger.info("Processing echo request")
logger.error("Invalid input received")

```

This logger writes to `stderr` by default, maintaining protocol integrity.

### Leveraging Cached Resources

Built-in tools rely on cached ChromaDB clients stored in **`_client_cache`** and **`_collection_cache`**. If your custom tool interacts with the vector store, reuse the helper functions **`_get_collection()`** or **`_get_client()`** to maintain cache coherency and avoid connection overhead.

For knowledge graph operations, import and call existing tools like `tool_kg_add` or `tool_kg_query` directly from your handler to ensure consistent data validation and storage.

## Advanced Pattern: Wrapping Built-in Tools

You can create custom tools that modify behavior of existing functionality before delegating to the original handler. The following example creates `mempalace_my_search`, which wraps the built-in search tool but enforces a hard limit of 10 results:

```python
def tool_my_search(query: str, limit: int = 5) -> dict:
    """Thin wrapper around the built-in search that caps results at 10."""
    limit = min(limit, 10)  # enforce ceiling

    return tool_search(query=query, limit=limit)

TOOLS.update(
    {
        "mempalace_my_search": {
            "description": "Custom search that caps results at 10.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "limit": {"type": "integer", "minimum": 1}
                },
                "required": ["query"]
            },
            "handler": tool_my_search,
        }
    }
)

```

## Key Files for Custom Tool Development

Understanding these source files ensures you follow established patterns when adding custom MCP tools to the MemPalace server:

- **[`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py)** — Core MCP server implementation containing the `TOOLS` dictionary, stdio protection, request dispatch loop, and helper functions for collection and knowledge-graph access (lines 2269-2275 and 2800-2810).
- **[`mempalace/searcher.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/searcher.py)** — Implements `search_memories`; reference this when building custom search-related tools.
- **[`mempalace/knowledge_graph.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/knowledge_graph.py)** — Provides the `KnowledgeGraph` API used by tools like `tool_kg_add` and `tool_kg_query`.
- **[`mempalace/backends/chroma.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/backends/chroma.py)** — Low-level ChromaDB wrapper for direct vector-store operations.
- **[`mempalace/config.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/config.py)** — Central configuration class `MempalaceConfig()` for accessing palace paths and collection names consistently.

## Summary

- **Register** custom tools in the `TOOLS` dictionary inside [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py) before the dispatch loop starts.
- **Prefix** all custom method names with `mempalace_` to expose them via the JSON-RPC interface.
- **Provide** three components: a handler function, a JSON Schema, and a human-readable description.
- **Restart** the server after editing [`mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mcp_server.py) to load new tool registrations.
- **Reuse** built-in helpers like `_get_collection()` and existing tool handlers to maintain cache consistency and data integrity.
- **Avoid** using `print()`; instead use the `mempalace_mcp` logger to prevent breaking the JSON-RPC stream.

## Frequently Asked Questions

### What file do I edit to add custom MCP tools to MemPalace?

You edit **[`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py)**. This file contains the `TOOLS` dictionary (lines 2269-2275) where all tool metadata, schemas, and handlers are registered, and the dispatch loop (lines 2800-2810) that routes JSON-RPC requests to your custom implementations.

### Why must custom tool names use the `mempalace_` prefix?

The dispatch loop in [`mempalace/mcp_server.py`](https://github.com/MemPalace/mempalace/blob/main/mempalace/mcp_server.py) explicitly filters the `TOOLS` dictionary to only expose methods beginning with **`mempalace_`**. This namespace protection prevents accidental exposure of internal helper functions and ensures compatibility with the MCP protocol expectations.

### How do I access the ChromaDB vector store in my custom tool?

Import and call the helper functions **`_get_collection()`** or **`_get_client()`** from within your handler. These functions manage the internal `_client_cache` and `_collection_cache`, ensuring you reuse existing connections and respect cache invalidation rules established by the core server.

### Can I modify existing built-in tools when adding custom functionality?

You should not modify existing tool definitions directly. Instead, write a wrapper handler that calls the original tool function (such as `tool_search` or `tool_kg_add`) and performs your custom logic (validation, transformation, or filtering) before or after the delegated call. This preserves the integrity of the core MemPalace API.