How to Create Custom MCP Server Plugins in Reasonix: A Complete Guide for Developers
Developers create custom MCP server plugins in Reasonix by adding a configuration entry, implementing an MCP-compliant server, registering tools, and placing skill markdown files—enabling the AI to invoke custom functions and prompts.
Reasonix uses the Multi-Channel Protocol (MCP) to load external capabilities as plugins. These plugins extend the AI with tools (callable functions) and skills (prompt templates) that integrate seamlessly into the reasoning engine. This guide walks through the complete implementation workflow based on the esengine/DeepSeek-Reasonix source code.
MCP Plugin Architecture Overview
Reasonix treats every plugin as an MCP server—either a local executable or remote HTTP endpoint. The system follows a six-stage lifecycle from configuration to runtime invocation.
Step 1: Configure Your Plugin Entry
Every plugin starts with a configuration block in reasonix.toml or ~/.config/reasonix/config.toml. The [[plugins]] table defines how Reasonix launches and identifies your server.
[[plugins]]
name = "myplugin" # Stable identifier—used in all tool/skill names
type = "command" # "command" for local executable, "http" for remote
command = "./plugins/myplugin/main" # Path to binary (absolute or relative)
tier = "eager" # "eager" (startup), "lazy" (first use), or "on-demand"
The name field is critical—Reasonix uses it to generate isolated namespaces for your tools. According to [internal/config/plugin_entry.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/config/plugin_entry.go), this structure supports both command (stdio-based) and http (network-based) server types.
Step 2: Implement the MCP Server
Your server must implement the MCP JSON-RPC protocol. The Initialize method negotiates capabilities, while handler methods process tool calls and prompt interception.
Minimal Server Skeleton
package main
import (
"context"
"encoding/json"
"github.com/esengine/DeepSeek-Reasonix/sdk/go/extension"
)
type plugin struct {
id string
}
func (p *plugin) Initialize(
_ context.Context,
params extension.InitializeParams,
) (*extension.InitializeResult, error) {
return &extension.InitializeResult{
Provider: p.id + "/demo", // e.g., "plugin/myplugin/demo"
Model: "demo-1",
Tools: true, // Advertise tool support
}, nil
}
// Implement other required handlers:
// - InterceptInput: modify user messages
// - InterceptTool: handle tool execution
// - InterceptSystemPrompt: modify system prompts
The full reference implementation lives in [sdk/go/examples/fullsidecar/main.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/sdk/go/examples/fullsidecar/main.go). This example demonstrates proper request/response streaming and graceful shutdown handling—copy it as your starting template.
Step 3: Register Tools with Namespaced Identifiers
Tools must be registered through the Reasonix SDK. The runtime automatically prefixes tool names to prevent collisions between plugins.
import "github.com/esengine/DeepSeek-Reasonix/sdk/go/tool"
func (p *plugin) registerTools() {
tool.Register(&tool.Tool{
Name: "search",
Run: func(
ctx context.Context,
args json.RawMessage,
) (json.RawMessage, error) {
// Parse args, execute search, return results
var req SearchRequest
if err := json.Unmarshal(args, &req); err != nil {
return nil, err
}
result := performSearch(req.Query)
return json.Marshal(result)
},
})
}
After registration, this tool becomes available as:
mcp__plugin_myplugin_myplugin__search
The naming convention follows the pattern mcp__plugin_<plugin-id>_<plugin-name>__<tool-name>. As implemented in [internal/tool/tool.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/tool.go), this guarantees plugin isolation—no plugin can accidentally or maliciously invoke another plugin's tools.
Step 4: Create Skill Markdown Files
Skills bundle prompts with metadata about which tools they may use. Place these in your plugin's skills/ directory.
---
description: Summarize a document using the plugin's search tool
allowedTools:
- "mcp__plugin_myplugin_myplugin__search"
---
You are a research assistant. Your task is to summarize the following text.
Use the search tool to verify any factual claims before including them in your summary.
The front-matter fields control skill behavior:
description: Shown in skill selection UIallowedTools: List of tool name patterns the skill may invoke
Reasonix scans plugin directories at startup and loads skills into the prompt library, per [internal/skill/skill.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/skill/skill.go). The allowedTools pattern matching is tested in [internal/skill/skill_test.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/skill/skill_test.go), which includes examples of skills calling plugin-owned tools.
Step 5: Controller Wires Everything at Startup
When Reasonix boots, the controller orchestrates plugin initialization. Located in [internal/control/controller.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/control/controller.go), it executes:
AddMCPServer: Parses configuration entries and prepares launch parametersmcpSpec: Builds aplugin.Specdefining the server's capabilitiesWireCapabilityRouting: Connects tools and skills to the session's request router
The controller respects the tier setting:
- eager: Server starts immediately with Reasonix
- lazy: Server starts on first tool invocation
- on-demand: Server starts only when explicitly requested by user or skill
Step 6: Invoke Plugin Tools and Skills
After startup, the AI accesses your plugin through two mechanisms:
Direct Tool Calling
The model requests tools by their fully-qualified name:
// AI generates this tool call:
request := extension.ToolCall{
Name: "mcp__plugin_myplugin_myplugin__search",
Arguments: json.RawMessage(`{"query": "quantum computing"}`),
}
Skill-Based Invocation
Skills grant implicit tool access when invoked:
// User or system selects the "summarize" skill
request := extension.Request{
ProviderRef: "plugin/myplugin/demo",
Model: "demo-1",
Prompt: "Summarize the article about quantum computing.",
// The skill's allowedTools permits the AI to call search automatically
}
Complete Plugin Directory Structure
plugins/
└─ myplugin/
├─ main.go # MCP server entry point
├─ tools/
│ └─ search.go # Tool implementations (optional organization)
├─ skills/
│ └─ summarize.md # Skill definition with front-matter
└─ go.mod # Module dependencies
Debugging and Verification
The Reasonix desktop client provides visibility into MCP server status. Reference [desktop/plugin_mcp_server_view.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/desktop/plugin_mcp_server_view.go) to understand:
- Connection state indicators
- Tool registration confirmation
- Skill indexing status
- Runtime error logs
Summary
- Configure plugins via
[[plugins]]entries in TOML with stable names and launch commands - Implement MCP servers following the JSON-RPC spec—start from the full-sidecar example
- Register tools through the SDK; receive automatic
mcp__plugin_<id>_<name>__prefixing - Define skills as markdown with front-matter declaring
allowedTools - Restart Reasonix to trigger controller wiring via
AddMCPServer,mcpSpec, andWireCapabilityRouting
The full implementation examples and core logic are available in the esengine/DeepSeek-Reasonix repository under sdk/go/examples/ and internal/control/.
Frequently Asked Questions
What programming languages can I use to write MCP server plugins?
Reasonix communicates via JSON-RPC over stdin/stdout or HTTP, so any language works. The Go SDK in sdk/go/ provides convenience wrappers, but you can implement the protocol in Python, TypeScript, Rust, or others. The fullsidecar example demonstrates the message format you must support.
How do I prevent tool name collisions between plugins?
The runtime handles this automatically. As defined in [internal/tool/tool.go](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/tool.go), every tool is prefixed with mcp__plugin_<plugin-id>_<plugin-name>__. This namespace isolation ensures that a tool named "search" in "myplugin" becomes distinct from "search" in "otherplugin".
Can I update plugin tools without restarting Reasonix?
Currently, tool registration and skill loading occur during controller initialization at startup. The tier setting controls when servers launch, but runtime re-registration requires a restart. For development, use tier = "lazy" or tier = "on-demand" to minimize restart overhead.
How do skills differ from direct tool calling?
Skills are high-level prompt templates that bundle:
- Pre-written instructions for the AI
- Declared tool permissions (
allowedTools) - Metadata for discovery and UI display
Direct tool calling gives the model raw function access without the structured prompting that skills provide. Skills are preferred for complex workflows where the AI needs guided reasoning.
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 →