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.SuspendPrefix and Registry.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 Tool interface via proxy objects in internal/tool/tool.go.
  • Names are prefixed with mcp__<server>__<tool> to ensure uniqueness and enable routing via SplitMCPName.
  • The proxy implements all five interface methods, with Execute forwarding to the remote server and ReadOnly always returning false.
  • Schema canonicalization happens once during Registry.Add, preparing stable definitions for the LLM.
  • Lifecycle methods RemovePrefix, SuspendPrefix, and ResumePrefix handle 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:

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 →