# How MCP Plugins Are Integrated with Stdio and HTTP Transports in DeepSeek-Reasonix

> Discover how DeepSeek Reasonix integrates MCP plugins with stdio and HTTP transports. Learn how it unifies communication for tools, prompts, and resources, enhancing host application interaction.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: internals
- Published: 2026-06-22

---

**Reasonix selects between stdio subprocesses and HTTP clients based on the `Spec.Type` field, then unifies both transports under a common interface that exposes tools, prompts, and resources to the host application.**

The DeepSeek-Reasonix repository implements a modular plugin system that consumes external MCP (Model Context Protocol) servers as native extensions. Whether connecting to a local analysis tool via standard input/output or a remote service over HTTP, the integration follows a consistent pattern defined in the `internal/plugin` package. This architecture allows Reasonix to discover and execute tools from diverse sources without modifying core application logic.

## The Spec Struct and Transport Selection

The integration entry point is the **`Spec`** struct defined in [`internal/plugin/plugin.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/plugin.go). This configuration object determines which transport implementation to instantiate based on the `Type` field.

When `Host.StartSingle` initializes a plugin, it evaluates the spec type through a switch statement:

```go
switch strings.ToLower(spec.Type) {
case "", "stdio":
    tr, err = newStdioTransport(ctx, spec)
case "http", "streamable-http", "sse":
    tr, err = newHTTPTransport(spec)
default:
    return fmt.Errorf("unknown MCP transport %q", spec.Type)
}

```

- **stdio**: Spawns a subprocess communicating via JSON-RPC over newline-delimited `stdin/stdout`.
- **http/streamable-http/sse**: Constructs an HTTP client for POST-based JSON-RPC or Server-Sent Events streaming.

Both implementations satisfy the internal `transport` interface, exposing `call`, `notify`, and `close` methods that the higher-level client uses to perform MCP operations.

## Stdio Transport Implementation

The stdio transport, implemented in [`internal/plugin/transport_stdio.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/transport_stdio.go), manages subprocess lifecycle and message framing for local MCP servers.

### Process Launch and Environment

The `resolveStdioExecutable` function locates the target binary by enriching the child process `PATH` through the user's login shell. The transport then constructs the command using `exec.CommandContext` with configurable `Command`, `Args`, `Env`, and `Dir` fields from the spec:

```go
cmd := exec.CommandContext(ctx, spec.Command, spec.Args...)
cmd.Dir = spec.Dir
cmd.Env = os.Environ()
for k, v := range spec.Env {
    cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}

```

Setting `spec.LowPriority = true` reduces the subprocess OS scheduling priority, suitable for background indexing tasks.

### JSON-RPC Framing and Cancellation

Message exchange follows newline-delimited JSON-RPC:

- **Writing**: Requests are serialized and written with a newline terminator to `stdin`.
- **Reading**: A dedicated goroutine scans `stdout` line-by-line, routing responses to pending call channels based on JSON-RPC `id`.
- **Timeouts**: The transport applies `defaultCallTimeout` when the caller's context lacks a deadline, preventing hung processes from blocking the host.

Errors from missing executables, startup failures, or premature EOF propagate as formatted errors with captured stderr diagnostics.

## HTTP Transport Implementation

The HTTP transport in [`internal/plugin/transport_http.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/transport_http.go) handles remote MCP servers using standard HTTP POST requests with optional SSE streaming support.

### Request Construction and Session Management

The `do` method builds requests with specific headers:

```go
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json, text/event-stream")
// Static headers from spec
for k, v := range spec.Headers {
    req.Header.Set(k, v)
}
// Session tracking
if t.sessionID != "" {
    req.Header.Set("Mcp-Session-Id", t.sessionID)
}

```

The `captureSession` function extracts the `Mcp-Session-Id` header from responses, maintaining stateful connections across subsequent calls. Session expiration (HTTP 404 with RPC error code -32001) triggers `httpSessionExpiredError`.

### Response Handling Strategies

The transport automatically detects response types:

1. **JSON Responses**: When `Content-Type` is `application/json`, `decodeRPCResult` parses the RPC result directly.
2. **SSE Streams**: For `text/event-stream`, `readSSEResponse` scans Server-Sent Events, extracting the JSON-RPC response matching the request `id` while discarding interleaved notifications.

Non-2xx status codes wrap into descriptive errors including the server name and invoked method.

## Client Integration and Host Aggregation

Once instantiated, the transport feeds into an MCP client that performs the protocol handshake (`initialize`) and capability discovery (`tools/list`, `prompts/list`, `resources/list`).

The client stores discovered capabilities in the `Host` manager:

- **Tool Registration**: Tools are namespaced with the `mcp__` prefix and registered in [`internal/tool/registry.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/registry.go), appearing alongside built-in Reasonix tools.
- **Prompt/Resource Exposure**: `Host.Prompts()` and `Host.Resources()` aggregate items from all active clients, making them available to the UI as slash commands and `@` references.
- **Failure Tracking**: Startup errors populate `Host.failures`, allowing graceful degradation when individual plugins fail to initialize.

All tool invocations route through `transport.call` for requests or `transport.notify` for one-way messages, abstracting the underlying communication mechanism from the business logic.

## Practical Configuration Examples

These snippets demonstrate real-world plugin configurations.

**Stdio plugin with Node.js server:**

```go
spec := plugin.Spec{
    Name:    "mcp__mycodegraph",
    Type:    "stdio",
    Command: "node",
    Args:    []string{"./codegraph.js"},
    Env:     map[string]string{"NODE_ENV": "production"},
    Dir:     "/path/to/project",
}

if err := host.StartSingle(context.Background(), spec); err != nil {
    log.Fatalf("failed to start stdio MCP: %v", err)
}

result, err := host.CallTool(ctx, "mycodegraph__search", map[string]any{
    "query": "authentication",
})

```

**HTTP plugin with authentication:**

```go
spec := plugin.Spec{
    Name:    "mcp__searchsvc",
    Type:    "http",
    URL:     "https://search.example.com/mcp",
    Headers: map[string]string{
        "Authorization": "Bearer <token>",
    },
}

if err := host.StartSingle(context.Background(), spec); err != nil {
    log.Fatalf("failed to start HTTP MCP: %v", err)
}

out, err := host.CallTool(ctx, "searchsvc__lookup", map[string]any{
    "term": "golang",
})

```

**Low-priority background indexer:**

```go
spec.LowPriority = true // Reduces OS scheduling priority

```

**Streamable HTTP with SSE:**

```go
spec.Type = "streamable-http"
spec.URL  = "https://graph.example.com/mcp"
// transport_http.go automatically handles SSE parsing

```

## Summary

- **Transport Selection**: Reasonix uses the `Spec.Type` field to instantiate either `newStdioTransport` or `newHTTPTransport`, with stdio serving as the default.
- **Stdio Mechanics**: The stdio transport manages subprocess lifecycle through `exec.CommandContext`, implements newline-delimited JSON-RPC framing, and supports cancellation via context deadlines.
- **HTTP Mechanics**: The HTTP transport handles POST-based JSON-RPC, maintains sessions via the `Mcp-Session-Id` header, and transparently parses both single JSON responses and SSE streams.
- **Unified Interface**: Both transports implement the `transport` interface (`call`, `notify`, `close`), allowing the `Host` manager to aggregate tools, prompts, and resources without transport-specific logic.
- **Key Files**: [`internal/plugin/plugin.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/plugin.go) defines the spec and host; [`internal/plugin/transport_stdio.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/transport_stdio.go) and [`internal/plugin/transport_http.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/plugin/transport_http.go) contain transport implementations; [`internal/tool/registry.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/registry.go) registers discovered capabilities.

## Frequently Asked Questions

### What happens if the transport type is unspecified in the plugin spec?

When `spec.Type` is an empty string, Reasonix defaults to the stdio transport. The switch statement in the host initialization code treats empty strings and explicit `"stdio"` values identically, launching a subprocess using the command-line configuration provided in the spec.

### How does Reasonix authenticate with HTTP-based MCP servers?

Authentication credentials pass through the `Headers` field of the `Spec` struct. The HTTP transport copies these static headers into every request via the `do` method in [`transport_http.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/transport_http.go), allowing Bearer tokens, API keys, or custom authentication schemes to reach the remote endpoint.

### Can stdio MCP plugins run with reduced system priority?

Yes. Setting `spec.LowPriority = true` configures the subprocess to run with reduced OS scheduling priority. This is implemented in the stdio transport startup logic and is particularly useful for background tasks like code indexing that should not interfere with interactive performance.

### How does the HTTP transport distinguish between standard JSON responses and SSE streams?

The transport inspects the `Content-Type` response header. If it matches `application/json`, the transport calls `decodeRPCResult` to parse the body directly. If it is `text/event-stream`, the transport invokes `readSSEResponse` to scan the event stream and extract the JSON-RPC response matching the request ID, discarding interleaved notification events.