DeepSeek-Reasonix MCP Client Implementation: Plugin System Architecture

The DeepSeek-Reasonix MCP client implements a plugin system that treats external Model Context Protocol servers as discoverable tool providers, using a JSON-RPC 2.0 transport layer, automatic namespacing with the mcp__ prefix, and a thread-safe Host-Client architecture supporting hot-reloading.

DeepSeek-Reasonix extends AI agent capabilities through a robust MCP (Model-Client-Plugin) client implementation that bridges external services with the core reasoning engine. This system, located in the reasonix/internal/plugin package, enables seamless integration of tools, prompts, and resources from any MCP-compliant server using standardized JSON-RPC communication.

Core Architecture Components

The Host and Client Relationship

The architecture centers on two primary types defined in internal/plugin/plugin.go: the Host and the Client.

The Host acts as a registry managing multiple concurrent MCP server connections. It maintains a collection of active *Client instances and aggregates their capabilities—tools, prompts, resources, and connection failures—behind thread-safe APIs. The Host provides methods for listing available tools, adding or removing servers dynamically, and reading resources across all connected plugins.

Each Client represents a single MCP server connection. It owns a specific transport implementation (stdio or HTTP) and manages the MCP protocol handshake via initialize, discovers available capabilities through tools/list, and optionally surfaces prompts and resources via prompts/list and resources/list.

Transport Abstraction Layer

The system abstracts low-level communication through a **transport interface`. Concrete implementations handle protocol-specific details:

Both implementations provide call (request/response) and notify (fire-and-forget) methods, with automatic session re-initialization on expired HTTP sessions.

Connection Lifecycle and Handshake

Startup Flow (Phase A)

The boot sequence begins with plugin.StartAll or StartAvailable, which accepts a slice of Spec configurations and initializes a new Host.

  1. Concurrency Control: The function spawns goroutines per server spec, capped by StartPolicy.Concurrency to prevent resource exhaustion.
  2. Transport Initialization: For each spec, the internal start helper invokes newTransport to instantiate the appropriate transport based on Spec.Type ("stdio" or "http").
  3. MCP Handshake: The client executes initialize to negotiate protocol capabilities with the remote server.
  4. Tool Discovery: The client calls tools/list via Client.listTools, parsing responses into remoteTool instances. Each tool receives a namespaced identifier and is registered in the global tool registry.

Surface Discovery (Phase B)

After initial tool discovery, Host.StartPhaseB launches asynchronous discovery of optional surfaces:

  • Prompts: Discovered via prompts/list and exposed to the UI as slash-commands.
  • Resources: Discovered via resources/list for file-like access patterns.

Each surface streams results independently and fires MCPSurfaceReady events to update the UI without blocking the main agent loop.

Tool Namespacing and Registry Integration

The mcp__ Prefix Convention

To prevent collisions between built-in tools and external MCP tools, DeepSeek-Reasonix implements strict namespacing defined in internal/tool/tool.go.

The constant MCPNamePrefix defines the string mcp__. The utility function toolName(server, raw) constructs public tool identifiers following the pattern mcp__<server>__<tool>, normalizing illegal characters to ensure valid identifiers. The companion function SplitMCPName parses these identifiers to extract the server and tool components.

This namespacing ensures that a tool named search from a server named codegraph becomes mcp__codegraph__search in the agent's tool registry.

Registry Integration

The remoteTool type implements the internal Tool interface, allowing MCP tools to integrate seamlessly with the permission layer and planner. Each remoteTool encapsulates:

  • The namespaced tool name
  • A reference to its parent Client for execution
  • Read-only hints: The ReadOnly property mirrors the MCP readOnlyHint annotation, optionally overridden by Spec.ReadOnlyToolNames for security policy enforcement.

Transport Implementations

StdIO Transport

The stdio transport in transport_stdio.go manages subprocess lifecycle:

// Conceptual usage of stdio transport internals
transport := newStdioTransport(ctx, command, args, env, workDir)
// Spawns: exec.CommandContext(ctx, command, args...)
// Communicates via stdin/stdout with line-delimited JSON-RPC

This transport is ideal for local CLI tools and language servers that expose MCP capabilities over standard streams.

HTTP and Streamable-HTTP Transport

The HTTP transport supports remote MCP servers using POST requests with MCP-specific headers. It maintains session state and automatically re-initializes connections when receiving session-expired responses. For resource-heavy operations, the transport spawns auxiliary clients to prevent blocking the main tool execution channel.

Runtime Management and Hot-Reloading

Dynamic Server Management

The Host supports modifying the plugin set at runtime without restarting the agent:

  • Host.Add: Hot-adds a new server using the same initialization logic as StartAll. It acquires the spawningMu lock and checks spawnAttempt guards to prevent duplicate connection attempts.
  • Host.Remove: Gracefully closes a specific client connection and removes its tools from the registry.

Graceful Shutdown

The Host.Close method implements ordered shutdown:

  1. Cancels pending startup contexts to abort in-progress handshakes.
  2. Waits for background writers to flush.
  3. Closes each transport connection sequentially.

Connection failures are recorded in Host.failures, accessible via Host.Failures() for diagnostic visibility.

Practical Implementation Examples

Starting Configured MCP Servers at Boot

ctx := context.Background()
specs := []plugin.Spec{
    {Name: "codegraph", Type: "stdio", Command: "codegraph", Args: []string{"--serve"}},
    {Name: "my-http-svc", Type: "http", URL: "https://example.com/mcp"},
}
host, tools, err := plugin.StartAll(ctx, specs)
if err != nil {
    log.Fatalf("MCP start failed: %v", err)
}
// tools contains namespaced remoteTool instances ready for the agent

Hot-Adding a Server During Runtime

newSpec := plugin.Spec{
    Name:    "dynamic-svc",
    Type:    "stdio",
    Command: "dynamic-svc",
    Args:    []string{"--listen"},
}
addedTools, err := host.Add(ctx, newSpec)
if err != nil {
    log.Printf("Failed to add server: %v", err)
}
for _, t := range addedTools {
    fmt.Println("New tool:", t.Name())
}

Executing a Namespaced MCP Tool

toolName := "mcp__codegraph__search"
t, err := tool.Registry.Get(toolName)
if err != nil {
    log.Fatal(err)
}
result, err := t.Execute(ctx, json.RawMessage(`{"query":"User"}`))
if err != nil {
    log.Fatalf("Tool error: %v", err)
}
fmt.Println("Tool output:", result)

Reading MCP Resources

content, err := host.ReadResource(ctx, "codegraph", "repo://myproj/main.go")
if err != nil {
    log.Fatalf("ReadResource failed: %v", err)
}
fmt.Println(content)

Summary

  • Host-Client Architecture: The Host in internal/plugin/plugin.go manages multiple Client instances, each representing a single MCP server connection over JSON-RPC 2.0.
  • Dual Transport Support: Implementations in transport_stdio.go and transport_http.go handle subprocess and HTTP-based servers respectively, with automatic session recovery.
  • Strict Namespacing: All MCP tools receive the mcp__<server>__ prefix defined in internal/tool/tool.go to prevent collisions with built-in tools.
  • Phased Discovery: Tools are discovered immediately during startup (Phase A), while prompts and resources load asynchronously (Phase B) via StartPhaseB.
  • Hot-Reloading: The system supports runtime addition and removal of servers via Host.Add and Host.Remove with thread-safe concurrency controls.

Frequently Asked Questions

How does DeepSeek-Reasonix prevent tool name collisions between different MCP servers?

The system enforces automatic namespacing using the mcp__ prefix defined in internal/tool/tool.go. When a server named codegraph exposes a tool called search, the Host registers it as mcp__codegraph__search in the global registry. The toolName function constructs these identifiers by normalizing illegal characters, while SplitMCPName parses them during execution to route calls to the correct Client instance.

Can MCP servers be added without restarting the DeepSeek-Reasonix agent?

Yes. The Host.Add method in internal/plugin/plugin.go supports hot-adding servers during active sessions. It performs the full initialization handshake—including transport creation, initialize negotiation, and tools/list discovery—while holding the spawningMu lock to prevent race conditions. Successfully added tools immediately become available to the planner and permission layer.

What transport protocols does the MCP client support?

The implementation supports two primary transports: StdIO for local subprocess communication (implemented in transport_stdio.go) and HTTP/Streamable-HTTP for remote servers (implemented in transport_http.go). Both implement the internal transport interface with call and notify methods. The HTTP transport includes automatic re-initialization logic when sessions expire and spawns auxiliary clients for resource-intensive operations.

How does the system handle MCP servers that expose read-only versus write-capable tools?

During tool discovery in Client.listTools, the system reads the MCP readOnlyHint annotation for each tool. The remoteTool struct stores this as a ReadOnly boolean, which the permission layer evaluates before execution. Additionally, administrators can override these hints by specifying ReadOnlyToolNames in the Spec configuration when adding a server, enforcing stricter security policies than the server itself declares.

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 →