How Remote Tools Are Adapted to the Tool Interface in DeepSeek-Reasonix
DeepSeek-Reasonix adapts remote Model-Client-Protocol (MCP) tools to the Tool interface using a lightweight proxy that prefixes names with mcp__, forwards schema metadata, and executes RPC calls over the network.
DeepSeek-Reasonix treats every capability that the model can invoke as a first-class Tool, including remote capabilities provided by external MCP servers. The adaptation layer implemented in internal/tool/tool.go creates proxy objects that satisfy the local interface while forwarding execution to remote endpoints. This design allows the planner, preview subsystem, and execution loop to handle remote and built-in tools identically without protocol-specific branching.
The Tool Interface Abstraction
Every tool in DeepSeek-Reasonix implements the same interface, whether it is a built-in file writer or a remote search capability. The interface requires five methods: Name() for identification, Description() for the model's context, Schema() for JSON-Schema validation, Execute(ctx, args) for invocation, and ReadOnly() to indicate side effects. Because remote tools implement this exact contract, the engine treats them as native capabilities without modification to the planning or execution logic.
Namespace Prefixing for Remote Tools
To prevent collisions between built-in and remote tools, DeepSeek-Reasonix applies a strict naming convention defined in internal/tool/tool.go.
The MCPNamePrefix Constant
In internal/tool/tool.go (lines 33-37), the system defines MCPNamePrefix = "mcp__". Every remote tool receives a canonical name following the pattern mcp__<server>__<tool>, ensuring unique identification across the registry.
Name Splitting and Routing
The helper function SplitMCPName (lines 40-49 in the same file) parses prefixed names to extract the server identifier and original tool name. It returns ok = false for non-MCP names, allowing the adapter to route calls to the correct remote endpoint or fall back to local execution.
Remote Tool Proxy Implementation
When an MCP connection is established, the client builds a proxy struct that implements the Tool interface using the remote server's metadata.
Proxy Method Behaviors
- Name() returns the prefixed identifier (
mcp__<server>__<tool>). - Description() forwards the string provided by the remote server unchanged.
- Schema() returns the JSON-Schema stored verbatim from the remote server's tool definition.
- Execute(ctx, args) marshals arguments and sends an RPC request to the remote MCP server, returning the server's JSON result as a string.
- ReadOnly() always returns false because the client cannot statically infer whether remote operations have side effects.
Registry Lifecycle and Schema Canonicalization
Remote tools enter the system through the Registry type, which manages both discovery and cleanup.
Schema Canonicalization
When Registry.Add inserts a proxy tool, provider.CanonicalizeSchema processes the schema once (lines 13-16 in internal/tool/tool.go). This guarantees that the registry exposes a stable, normalized list of tool definitions to the LLM, regardless of the source server.
Dynamic Removal and Suspension
The registry supports lifecycle methods for volatile connections:
Registry.RemovePrefix(prefix)deletes every tool whose name starts with a given prefix, invoked when an MCP server disconnects.Registry.SuspendPrefixandRegistry.ResumePrefix(lines 52-78) temporarily block tools from being re-added while a session tears down, preventing race conditions during reconnection.
Implementation Examples
The following patterns demonstrate how remote tools materialize in the codebase.
Creating a Remote Tool Proxy
func newMCPTool(server, name, desc string, schema json.RawMessage) tool.Tool {
return &mcpTool{
server: server,
toolName: name,
description: desc,
schema: schema,
}
}
type mcpTool struct {
server, toolName, description string
schema json.RawMessage
}
func (t *mcpTool) Name() string { return tool.MCPNamePrefix + t.server + "__" + t.toolName }
func (t *mcpTool) Description() string { return t.description }
func (t *mcpTool) Schema() json.RawMessage { return t.schema }
func (t *mcpTool) ReadOnly() bool { return false }
func (t *mcpTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
resp, err := mcpClient.Call(ctx, t.server, t.toolName, args)
if err != nil {
return "", err
}
return string(resp), nil
}
Registering Remote Tools
reg := tool.NewRegistry()
reg.Add(newMCPTool("my-server", "search", "Search the web", rawSchema))
Executing Through the Generic Interface
if t, ok := reg.Get("mcp__my-server__search"); ok {
result, _ := t.Execute(ctx, json.RawMessage(`{"query":"golang interface"}`))
fmt.Println(result)
}
Summary
- Remote MCP tools are adapted to the local
Toolinterface via proxy objects ininternal/tool/tool.go. - Names are prefixed with
mcp__<server>__<tool>to ensure uniqueness and enable routing viaSplitMCPName. - The proxy implements all five interface methods, with
Executeforwarding to the remote server andReadOnlyalways returning false. - Schema canonicalization happens once during
Registry.Add, preparing stable definitions for the LLM. - Lifecycle methods
RemovePrefix,SuspendPrefix, andResumePrefixhandle dynamic server connections.
Frequently Asked Questions
How does the naming convention prevent conflicts between remote and local tools?
DeepSeek-Reasonix enforces the MCPNamePrefix constant defined in internal/tool/tool.go (lines 33-37). All remote tool names follow the pattern mcp__<server>__<tool>, while built-in tools omit this prefix, guaranteeing unique identifiers in the registry.
Why does the ReadOnly method always return false for remote tools?
The ReadOnly method signals whether a tool modifies state. For remote tools, this always returns false because the client cannot statically verify side effects on the remote server, ensuring the planner treats all remote calls as potentially mutating.
What happens to remote tools when an MCP server disconnects?
The Registry.RemovePrefix method removes all tools matching the disconnected server's prefix. Additionally, SuspendPrefix and ResumePrefix (lines 52-78 in internal/tool/tool.go) manage temporary blocking during session teardown to prevent stale tool registrations from reappearing.
Is schema canonicalization necessary for every tool addition?
Yes. When Registry.Add is called, provider.CanonicalizeSchema processes the JSON-Schema once (as implemented in lines 13-16 of internal/tool/tool.go). This ensures the LLM receives consistent, normalized tool definitions regardless of variations in remote server implementations.
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 →