DEV Community

Javier Leandro Arancibia
Javier Leandro Arancibia

Posted on

How We Made Stateful MCP Servers Work in One-Shot CLI Calls

How We Made Stateful MCP Servers Work in One-Shot CLI Calls

The Problem: Stateful Servers vs Stateless CLI

Context-mode is a powerful MCP (Model Context Protocol) server that maintains a SQLite knowledge base and session history. It reduces AI context window usage by up to 98% by keeping raw tool output out of conversations and only returning relevant snippets.

But there was a problem: context-mode is stateful. It needs to stay alive between calls to maintain its knowledge base. Traditional CLI tools are stateless — each invocation is a fresh process.

When we tried to integrate context-mode into supercli (a CLI wrapper for external tools), we hit a wall:

# Call 1: Index project structure
sc mcp call --mcp-server context-mode --tool ctx_index --input-json '{"content":"..."}'

# Call 2: Search — but knowledge is gone! New process, new database.
sc mcp call --mcp-server context-mode --tool ctx_search --input-json '{"queries":["routes"]}'
Enter fullscreen mode Exit fullscreen mode

Each sc call spawned a new context-mode process. The knowledge base was lost between calls. Agents using supercli in one-shot CLI invocations couldn't benefit from context-mode's stateful features.

The Solution: MCP Daemon

We built a daemon process manager that keeps MCP servers alive persistently across CLI invocations.

Architecture

┌─────────────────────────────────────────────────────────┐
│  CLI Call (one-shot)                                     │
│  sc mcp call --mcp-server context-mode --tool ctx_search │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│  MCP Daemon (persistent background process)             │
│  Unix socket: ~/.supercli/mcp-daemon.sock               │
│  Process pool: Map<<serverName, ChildProcess>>           │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│  context-mode (long-lived process)                      │
│  SQLite knowledge base persists across CLI calls        │
└─────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Key Features

  1. Unix Socket Server: Daemon listens on ~/.supercli/mcp-daemon.sock for JSON-RPC requests
  2. Process Pool: Maintains long-lived child processes for each MCP server
  3. Auto-Start: Daemon starts automatically on first tool call if not running
  4. Stateful Flag: MCP servers can be marked as stateful: true to use the daemon
  5. Graceful Shutdown: Cleanly stops all MCP processes on daemon exit

Implementation

Daemon (cli/mcp-daemon.js):

// Unix socket server
const server = net.createServer((socket) => {
  socket.on('data', (chunk) => {
    const req = JSON.parse(chunk);
    if (req.method === 'call_tool') {
      const entry = await getOrSpawnServer(req.params.server, req.params.serverConfig);
      const result = await callTool(entry, req.params.tool, req.params.input);
      socket.write(JSON.stringify({ id: req.id, result }) + '\n');
    }
  });
});
server.listen(SOCKET_PATH);
Enter fullscreen mode Exit fullscreen mode

Client (cli/mcp-daemon-client.js):

async function callDaemonTool({ server, serverConfig, tool, input, timeout_ms }) {
  if (!await isDaemonRunning()) {
    await startDaemon(); // Auto-start if not running
  }
  return sendDaemonRequest('call_tool', { server, serverConfig, tool, input, timeout_ms });
}
Enter fullscreen mode Exit fullscreen mode

MCP Adapter (cli/adapters/mcp.js):

if (config.stateful) {
  // Route through daemon instead of spawning new process
  return callDaemonTool({ server, serverConfig, tool, input, timeout_ms });
} else {
  // Existing behavior: spawn per call
  return stdioCallToolJsonRpc({ ... });
}
Enter fullscreen mode Exit fullscreen mode

Usage

Installation

npm install -g context-mode
sc plugins install ./plugins/context-mode --on-conflict replace --json
# Automatically registers as stateful MCP server + starts daemon
Enter fullscreen mode Exit fullscreen mode

CLI Commands

# Daemon management
sc mcp daemon start     # Start daemon
sc mcp daemon status    # Check status + active servers
sc mcp daemon stop      # Stop daemon
sc mcp daemon restart   # Restart daemon

# Add stateful MCP server
sc mcp add my-server --command my-tool --stateful

# Call context-mode tools (knowledge persists across calls)
sc mcp call --mcp-server context-mode --tool ctx_batch_execute --input-json '{
  "commands": [
    {"label": "Package", "command": "cat package.json"},
    {"label": "Source tree", "command": "find src -name \"*.js\" | head -40"}
  ],
  "queries": ["entry point", "dependencies"]
}'
Enter fullscreen mode Exit fullscreen mode

Benefits

1. Knowledge Base Persistence

# Call 1: Index docs
sc mcp call --mcp-server context-mode --tool ctx_index --input-json '{
  "content": "...large documentation...",
  "source": "api-docs"
}'

# Call 2 (new shell, new process): Knowledge persists!
sc mcp call --mcp-server context-mode --tool ctx_search --input-json '{
  "queries": ["authentication", "token expiry"]
}'
Enter fullscreen mode Exit fullscreen mode

2. Context Window Reduction

ctx_batch_execute runs commands, auto-indexes output, and searches in one call — returning only matched snippets, not raw output:

Executed 3 commands (57 lines, 1.4KB). Indexed 3 sections. Searched 4 queries.

## dependencies (only relevant snippets returned)

### Package
{
  "dependencies": {
    "dotenv": "^17.3.1",
    "ejs": "^3.1.9",
    "express": "^4.18.2",
    "mongodb": "^6.3.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Agent-Friendly

Agents call supercli as one-shot commands. The daemon ensures knowledge base persistence:

# Agent call 1
sc mcp call --mcp-server context-mode --tool ctx_batch_execute ...

# Agent call 2 (different process, different shell)
sc mcp call --mcp-server context-mode --tool ctx_search ...
# Knowledge base still available via daemon
Enter fullscreen mode Exit fullscreen mode

4. Zero Setup for Agents

The daemon auto-starts on first tool call. Agents don't need to manage it:

# First call: daemon auto-starts
sc mcp call --mcp-server context-mode --tool ctx_doctor --input-json '{}'

# Subsequent calls: daemon already running
sc mcp call --mcp-server context-mode --tool ctx_search ...
Enter fullscreen mode Exit fullscreen mode

Technical Details

Protocol

Daemon uses JSON over Unix socket:

// Request
{"id": 1, "method": "call_tool", "params": {"server": "context-mode", "tool": "ctx_search", "input": {...}}}

// Response
{"id": 1, "result": {...}}
Enter fullscreen mode Exit fullscreen mode

Process Lifecycle

  1. Daemon starts (detached process, no TTY)
  2. First tool call → daemon spawns context-mode process
  3. JSON-RPC handshake (initialize → notifications/initialized)
  4. Tool calls routed through daemon
  5. Process stays alive for subsequent calls
  6. Daemon shutdown → graceful termination of all MCP processes

Wire Mode

We discovered context-mode uses JSONL (newline-delimited JSON) not LSP framing. The daemon defaults to JSONL for stdio MCP servers:

const entry = {
  wireMode: serverConfig.wire_mode || "jsonl", // Default to JSONL
  ...
};
Enter fullscreen mode Exit fullscreen mode

Future Directions

Now that we have the daemon infrastructure, we can explore:

  1. Automatic output reduction: Process adapter could pipe output through context-mode automatically
  2. Ask command caching: LLM suggestions cached in knowledge base
  3. Workflow context: Cross-step knowledge indexing
  4. Error learning: Pattern-based error solutions
  5. Server mode shared KB: Collective intelligence across API clients

Conclusion

The MCP daemon enables stateful MCP servers like context-mode to work seamlessly in one-shot CLI environments. Agents get full context-mode benefits (98% context reduction, knowledge base persistence) while supercli remains a clean, stateless CLI wrapper.

The architecture is extensible — any stateful MCP server can now integrate with supercli via the daemon, opening possibilities for persistent tool sessions across CLI invocations.


Try it out:

npm install -g context-mode
sc plugins install ./plugins/context-mode --on-conflict replace --json
sc mcp daemon status
sc mcp call --mcp-server context-mode --tool ctx_doctor --input-json '{}'
Enter fullscreen mode Exit fullscreen mode

Top comments (0)