# How to Create Custom MCP Server Plugins in Reasonix: A Complete Guide for Developers

> Learn to create custom MCP server plugins in Reasonix. Developers can add tools and prompts by implementing an MCP-compliant server and registering skills. Unlock AI capabilities.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: how-to-guide
- Published: 2026-08-11

---

**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.

| Stage | What Happens | Key Source File |
|-------|--------------|-----------------|
| 1. Configuration | Add `[[plugins]]` entry with name, type, and launch command | [[`internal/config/plugin_entry.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/config/plugin_entry.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/config/plugin_entry.go) |
| 2. Server Implementation | Write MCP-compliant JSON-RPC server with `initialize`, tool handlers | [[`sdk/go/examples/fullsidecar/main.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/sdk/go/examples/fullsidecar/main.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/sdk/go/examples/fullsidecar/main.go) |
| 3. Tool Registration | Register tools with isolated namespace `mcp__plugin_<id>_<name>__` | [[`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/tool/tool.go) |
| 4. Skill Definition | Add markdown files with front-matter declaring allowed tools | [[`internal/skill/skill.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/skill/skill.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/skill/skill.go) |
| 5. Startup Wiring | Controller launches servers and binds capabilities to session | [[`internal/control/controller.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/control/controller.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/control/controller.go) |
| 6. Runtime Invocation | AI requests tools by prefixed name or invokes skills that grant tool access | [[`internal/skill/skill_test.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/skill/skill_test.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/skill/skill_test.go) |

---

## Step 1: Configure Your Plugin Entry

Every plugin starts with a configuration block in [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml) or `~/.config/reasonix/config.toml`. The `[[plugins]]` table defines how Reasonix launches and identifies your server.

```toml
[[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/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

```go
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/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.

```go
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/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.

```markdown
---
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 UI
- **`allowedTools`**: 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/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/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/internal/control/controller.go)](https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/control/controller.go), it executes:

1. **`AddMCPServer`**: Parses configuration entries and prepares launch parameters
2. **`mcpSpec`**: Builds a `plugin.Spec` defining the server's capabilities
3. **`WireCapabilityRouting`**: 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:

```go
// 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:

```go
// 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/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`, and `WireCapabilityRouting`

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/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.