# How to Extend ChocolateLMLite with External Tools Using MCP

> Extend ChocolateLMLite with external tools via MCP. Enable EnableMcpTools, configure mcp.json with your servers, and automatically register external tools.

- Repository: [Segment (gpsnmeajp)/chocolatelmlite](https://github.com/gpsnmeajp/chocolatelmlite)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Enable `EnableMcpTools` in the general settings, create an [`mcp.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/mcp.json) file defining your servers, and ChocolateLMLite automatically discovers and registers those external tools via the `Tools` class.**

ChocolateLMLite supports the Model Context Protocol (MCP) to integrate external programs and services directly into the LLM's execution context. By toggling a single configuration flag and providing a JSON server manifest, you can extend ChocolateLMLite with external tools using MCP without modifying the core C# engine. This guide explains the bootstrap flow, transport options, and tool discovery mechanism based on the source code in `gpsnmeajp/chocolatelite`.

## Enable MCP Support in Settings

Before the runtime initializes external connections, you must activate the MCP subsystem globally. In [`src/Persona.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Persona.cs) (lines 245‑246), the framework deserializes the user-editable flag `EnableMcpTools` from the persona settings JSON.

When the application starts, `Tools.InitToolsAsync()` checks this flag before bootstrapping the MCP infrastructure:

```csharp
// Tools.cs – MCP bootstrap gate
if (_fileManager.generalSettings.EnableMcpTools)   // 【line 47】 
{
    await InitMcpToolsAsync();                    // 【line 52】
}

```

Set `EnableMcpTools` to `true` in your [`settings.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.json) or via the Settings UI to trigger the initialization sequence.

## Configure the mcp.json Server Manifest

The runtime expects a server manifest named [`mcp.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/mcp.json) in the working directory. The `FileManager` class handles persistence; if the file is missing, it writes a blank template (see [`src/FileManager.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/FileManager.cs), lines 1538‑1558).

The configuration schema defines a `McpServers` object where each key represents a named server:

```json
{
  "McpServers": {
    "my-local-tool": {
      "Transport": "Stdio",
      "Command": "python my_tool.py"
    },
    "remote-service": {
      "Transport": "Http",
      "Url": "http://localhost:8000/mcp"
    }
  }
}

```

- **Stdio**: Launches a local command and communicates over standard input/output.
- **Http**: Connects to a remote endpoint exposing the MCP protocol over HTTP.

## Initialize MCP Clients and Transports

Inside `Tools.InitMcpToolsAsync()` ([`src/Tools.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Tools.cs)), the framework deserializes the JSON manifest (lines 314‑319) and iterates over each entry in `McpServers` (line 356). For each server, it instantiates the appropriate transport:

```csharp
if (serverConfig.Transport == "Stdio")
{
    clientTransport = new StdioMcpTransport(serverConfig.Command);
    MyLog.LogWrite($"MCPサーバーを起動中(Stdio): {serverName} コマンド={serverConfig.Command}"); // 【line 365】
}
else if (serverConfig.Transport == "Http")
{
    clientTransport = new HttpMcpTransport(serverConfig.Url);
    MyLog.LogWrite($"MCPサーバーに接続中(HTTP): {serverName} URL={serverConfig.Url}"); // 【line 384】
}

```

An `McpClient` is then created asynchronously and stored for lifecycle management:

```csharp
client = await McpClient.CreateAsync(clientTransport); // 【line 379】 or 【line 392】
_mcpClients.Add(client);

```

## Discover and Register Tools

After establishing connections, `InitMcpToolsAsync()` queries each client for its advertised capabilities. It calls `ListToolsAsync()` and aggregates the results into the internal `_mcpTools` collection:

```csharp
var mcpTools = await client.ListToolsAsync();
foreach (var tool in mcpTools)
{
    lock (_mcpTools)
    {
        _mcpTools.Add(tool); // 【line 63】
    }
}
MyLog.LogWrite($"MCPツールを追加: {string.Join(",", mcpTools.Select(t => t.Name))}"); // 【line 404】

```

When the LLM requests available functions, `Tools.GetAvailableTools()` merges native utilities with the MCP-derived tools:

```csharp
// Combine native and external tools
if (_fileManager.generalSettings.EnableMcpTools)
{
    tools.AddRange(_mcpTools); // 【lines 114‑115】
}

```

Each `AITool` object contains the **name**, **description**, **parameter schema**, and a handler that forwards execution to the remote server.

## Resource Lifecycle and Cleanup

The `Tools` class implements `IAsyncDisposable` to ensure graceful shutdown. When the application exits, it disposes every `McpClient` instance (lines 28‑34), terminating child processes and closing HTTP connections:

```csharp
public async ValueTask DisposeAsync()
{
    foreach (var client in _mcpClients)
    {
        await client.DisposeAsync();
    }
}

```

## Complete Implementation Example

The following example demonstrates wiring a local Python calculator and a remote weather service into ChocolateLMLite.

### 1. Create mcp.json

Place this file next to the executable:

```json
{
  "McpServers": {
    "calc": {
      "Transport": "Stdio",
      "Command": "python tools/calc_tool.py"
    },
    "weather": {
      "Transport": "Http",
      "Url": "http://localhost:5000/mcp"
    }
  }
}

```

### 2. Enable MCP in settings.json

```json
{
  "EnableMcpTools": true
}

```

### 3. Implement a Stdio MCP Server (Python)

Create [`tools/calc_tool.py`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/tools/calc_tool.py):

```python
import sys, json

def add(a, b):
    return a + b

def handle(request):
    data = json.loads(request)
    if data["name"] == "add":
        result = add(**data["arguments"])
        return json.dumps({"result": result})
    return json.dumps({"error": "unknown tool"})

for line in sys.stdin:
    print(handle(line))
    sys.stdout.flush()

```

For HTTP servers, expose a POST endpoint at `/mcp` returning the same JSON schema.

### 4. Use Tools in a Prompt

When you call the LLM, the framework automatically includes the discovered tools in the context:

```csharp
var prompt = @"You have access to:
- calc.add(a:int, b:int)
- weather.get(city:string)

What is 7 + 5? Also, what is the weather in Tokyo?";

var response = await llm.ChatAsync(prompt);

```

The LLM selects the appropriate tool, and ChocolateLMLite routes the call to the corresponding MCP client.

## Summary

- **Enable the feature** by setting `EnableMcpTools` to `true` in your settings; the flag is parsed in [`src/Persona.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Persona.cs) (lines 245‑246) and checked in [`src/Tools.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Tools.cs) (line 47).
- **Define servers** in [`mcp.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/mcp.json) using either `Stdio` for local commands or `Http` for remote endpoints; `FileManager` handles I/O (lines 1538‑1558).
- **Bootstrap clients** via `Tools.InitMcpToolsAsync()`, which creates `StdioMcpTransport` or `HttpMcpTransport` instances and initializes `McpClient` objects (lines 314‑392).
- **Aggregate tools** by calling `ListToolsAsync()` on each client and merging results into `_mcpTools` (lines 56‑66), exposed through `GetAvailableTools()` (lines 114‑115).
- **Cleanup resources** through `IAsyncDisposable` disposal of client connections (lines 28‑34).

## Frequently Asked Questions

### What file format does ChocolateLMLite use for MCP configuration?

ChocolateLMLite expects a JSON file named [`mcp.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/mcp.json) in the application working directory. The root object must contain a `McpServers` dictionary where each server specifies a `Transport` (`Stdio` or `Http`) and the corresponding connection details (`Command` or `Url`).

### Can I use both local command-line tools and HTTP APIs simultaneously?

Yes. The `Tools.InitMcpToolsAsync()` method iterates over all entries in the `McpServers` configuration (line 356) and creates the appropriate transport for each. You can mix `Stdio` servers for local scripts and `Http` servers for remote microservices in the same [`mcp.json`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/mcp.json) file.

### Where does ChocolateLMLite store the MCP enable flag?

The boolean flag `EnableMcpTools` is stored in the general settings object managed by `FileManager` and deserialized in [`src/Persona.cs`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/src/Persona.cs) (lines 245‑246). The `Tools` class checks this flag at line 47 before initializing any MCP clients.

### How does the LLM know which external tools are available?

When MCP is enabled, `Tools.GetAvailableTools()` (lines 114‑115) merges the native toolset with the `_mcpTools` collection populated during `InitMcpToolsAsync()`. Each discovered tool exposes its name, description, and JSON schema parameter definition, allowing the LLM to generate valid invocations that the framework routes to the correct `McpClient`.