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:
newStdioTransportininternal/plugin/transport_stdio.golaunches subprocesses usingexec.CommandContextand manages line-delimited JSON-RPC message exchange.- HTTP transport in
internal/plugin/transport_http.gohandles POST-based JSON-RPC requests and manages "auxiliary clients" for streaming prompts and resources.
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.
- Concurrency Control: The function spawns goroutines per server spec, capped by
StartPolicy.Concurrencyto prevent resource exhaustion. - Transport Initialization: For each spec, the internal
starthelper invokesnewTransportto instantiate the appropriate transport based onSpec.Type("stdio" or "http"). - MCP Handshake: The client executes
initializeto negotiate protocol capabilities with the remote server. - Tool Discovery: The client calls
tools/listviaClient.listTools, parsing responses intoremoteToolinstances. 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/listand exposed to the UI as slash-commands. - Resources: Discovered via
resources/listfor 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
Clientfor execution - Read-only hints: The
ReadOnlyproperty mirrors the MCPreadOnlyHintannotation, optionally overridden bySpec.ReadOnlyToolNamesfor 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 asStartAll. It acquires thespawningMulock and checksspawnAttemptguards 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:
- Cancels pending startup contexts to abort in-progress handshakes.
- Waits for background writers to flush.
- 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
Hostininternal/plugin/plugin.gomanages multipleClientinstances, each representing a single MCP server connection over JSON-RPC 2.0. - Dual Transport Support: Implementations in
transport_stdio.goandtransport_http.gohandle subprocess and HTTP-based servers respectively, with automatic session recovery. - Strict Namespacing: All MCP tools receive the
mcp__<server>__prefix defined ininternal/tool/tool.goto 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.AddandHost.Removewith 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →