Skip to content

Commit 96c2b7b

Browse files
committed
v0.12.0: MCP server — use kode tools from Claude Code, Cursor, etc.
- New internal/mcp package: zero-dep MCP server over stdio transport - New kode mcp command: exposes shell, read/write files, search, patch, browser via the Model Context Protocol (JSON-RPC 2.0) - Supports initialize, tools/list, tools/call, ping, initialized - Excludes delegate_tasks and memory (kode-agent-specific) - Docs/MCP.md: setup instructions for Claude Code + Cursor - 8 tests: initialization, tool listing, tool calls, unknown tools/methods, ping, pre-init guard - Security: shared DangerousConfig/approver system, non_interactive fallback 14 packages, 867 tests, race detector clean.
1 parent db7950e commit 96c2b7b

8 files changed

Lines changed: 787 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ kode repl
140140
| [Sub-Agents](docs/SUBAGENTS.md) | Task decomposition, delegation tool, subagent protocol |
141141
| [Web UI](docs/WEBUI.md) | `kode serve`, WebSocket protocol, `@` resource resolution |
142142
| [Skills](docs/CLI.md#skills) | Trigger-matched skills, learning, import, curation |
143+
| [MCP](docs/MCP.md) | MCP server over stdio — use kode tools from Claude Code |
143144
| [Development](docs/DEVELOPMENT.md) | Building, testing, contributing, project structure |
144145

145146
---

cmd/kode/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ func main() {
133133
})
134134
os.Exit(3)
135135
}
136+
case "mcp":
137+
if err := mcpCmd(os.Args[2:]); err != nil {
138+
fmt.Fprintf(os.Stderr, "kode: %v\n", err)
139+
os.Exit(1)
140+
}
136141
default:
137142
fmt.Fprintf(os.Stderr, "kode: unknown command %q\n", os.Args[1])
138143
printUsage()
@@ -340,6 +345,7 @@ func printUsage() {
340345
kode subagent --goal <string> [--context <string>] [flags]
341346
kode init [--global | -g] [--force | -f]
342347
kode skill <list|view|save|delete|import|curate>
348+
kode mcp [--sandbox]
343349
kode version
344350
345351
Commands:
@@ -358,6 +364,8 @@ Commands:
358364
Accepts --goal, --context, --task, --timeout, --max-iter.
359365
session Manage sessions: list, show, delete, trim, cleanup
360366
skill Manage skills: list, view, save, delete, import, curate
367+
mcp Start MCP server (Model Context Protocol) over stdio
368+
Exposes all built-in tools for Claude Code, Cursor, etc.
361369
init Create a config file (default: ./kode.json)
362370
version Print version and exit
363371

cmd/kode/mcp.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/BackendStack21/kode/internal/config"
9+
"github.com/BackendStack21/kode/internal/mcp"
10+
"github.com/BackendStack21/kode/internal/skills"
11+
)
12+
13+
// ── MCP Command ────────────────────────────────────────────────────────
14+
15+
// mcpCmd starts kode as an MCP server over stdio.
16+
// This allows MCP clients (Claude Code, Cursor, etc.) to use kode's tools.
17+
//
18+
// Usage:
19+
//
20+
// kode mcp [--sandbox]
21+
//
22+
// The server reads JSON-RPC 2.0 requests from stdin and writes responses
23+
// to stdout. Stderr is used for logging. The server exposes all kode
24+
// built-in tools (shell, read_file, write_file, search_files, patch,
25+
// browser) via the tools/list and tools/call MCP methods.
26+
func mcpCmd(args []string) error {
27+
// Parse CLI flags
28+
cliFlags := config.CLIFlags{}
29+
for i := 0; i < len(args); i++ {
30+
switch args[i] {
31+
case "--sandbox":
32+
cliFlags.Sandbox = boolPtr(true)
33+
case "--help", "-h":
34+
fmt.Println(`Usage: kode mcp [flags]
35+
36+
Start kode as an MCP server over stdio.
37+
38+
kode exposes all its built-in tools (shell, read/write files, search,
39+
patch, browser) via the Model Context Protocol. Connect any MCP client
40+
(Claude Code, Cursor, etc.) to use kode's tools.
41+
42+
Flags:
43+
--sandbox Run shell commands inside Docker sandbox
44+
--help, -h Show this help`)
45+
return nil
46+
default:
47+
return fmt.Errorf("unknown flag %q for mcp", args[i])
48+
}
49+
}
50+
51+
// Load config
52+
resolved := config.LoadConfig(cliFlags)
53+
54+
// Resolve system message
55+
systemMessage := resolved.System
56+
if systemMessage == "" {
57+
systemMessage = defaultSystem
58+
}
59+
60+
// Build skills manager (for skill tools)
61+
var sm *skills.SkillManager
62+
if resolved.Skills.Learn {
63+
sm = skills.NewSkillManager(
64+
expandHome("~/.kode/skills"),
65+
"./.kode/skills",
66+
)
67+
}
68+
69+
// Build tools
70+
toolSet := builtinTools(resolved.Dangerous, sm, nil)
71+
72+
// Convert to MCP NativeTool slice
73+
var callers []mcp.ToolCaller
74+
for _, t := range toolSet {
75+
callers = append(callers, t)
76+
}
77+
nativeTools := mcp.BuildNativeTools(callers)
78+
79+
// Create and run the MCP server
80+
version := getVersion()
81+
server := mcp.NewServer(version, nativeTools, os.Stdin, os.Stdout)
82+
83+
// Log startup to stderr (stdin/stdout are for MCP protocol)
84+
fmt.Fprintf(os.Stderr, "kode mcp ⚡ MCP server starting (v%s)\n", version)
85+
fmt.Fprint(os.Stderr, " Tools: ")
86+
for i, t := range nativeTools {
87+
if i > 0 {
88+
fmt.Fprint(os.Stderr, ", ")
89+
}
90+
fmt.Fprint(os.Stderr, t.Name)
91+
}
92+
fmt.Fprintln(os.Stderr)
93+
94+
return server.Run(context.Background())
95+
}

docs/CLI.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
| `kode serve [--addr :8080] [--open]` | Web UI server with WebSocket streaming, `@` resource completion, session history |
2323
| `kode subagent --goal <string> [flags]` | Run a focused sub-task; outputs JSON on stdout. Spawned by `delegate_tasks` tool |
2424
| `kode init [--global] [--force]` | Create a config file template |
25+
| `kode mcp [--sandbox]` | Start MCP server over stdio — exposes tools for Claude Code |
2526
| `kode version` | Print version and exit |
2627

2728
## Run flags

docs/DEVELOPMENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ docs/ Documentation
8787
SUBAGENTS.md Task decomposition + sub-agents + delegate_tasks tool
8888
SECURITY.md Prompt injection, security model
8989
SANDBOXING.md Sandbox configuration
90+
MCP.md MCP server over stdio (Model Context Protocol)
9091
DEVELOPMENT.md This file
9192
```
9293

docs/MCP.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# MCP Server (`kode mcp`)
2+
3+
kode implements the [Model Context Protocol](https://modelcontextprotocol.io) — a
4+
standard interface for AI agents to discover and invoke tools. This allows Claude
5+
Code, Cursor, and other MCP-compatible clients to use kode's built-in tools.
6+
7+
## Quick start
8+
9+
```bash
10+
# Start kode MCP server
11+
kode mcp
12+
```
13+
14+
Then configure your MCP client to connect. For **Claude Code**, add this to
15+
`~/.claude/claude_dotfiles/claude.json` or your project's `.claude/settings.json`:
16+
17+
```json
18+
{
19+
"mcpServers": {
20+
"kode": {
21+
"command": "kode",
22+
"args": ["mcp"]
23+
}
24+
}
25+
}
26+
```
27+
28+
For **Cursor**, add a similar entry in Cursor Settings → MCP Servers.
29+
30+
## What tools are exposed
31+
32+
| Tool | Description |
33+
|------|-------------|
34+
| `shell` | Run shell commands (with security classification) |
35+
| `read_file` | Read files with line numbers and pagination |
36+
| `write_file` | Write content to files (creates directories) |
37+
| `search_files` | Search file contents or find files by name |
38+
| `patch` | Find-and-replace edits with fuzzy matching |
39+
| `browser` | Navigate web pages, take snapshots, click elements |
40+
41+
The `delegate_tasks` and `memory` tools are **not** exposed via MCP — they are
42+
specific to kode's own agent loop and don't translate to the MCP tool model.
43+
44+
## Security
45+
46+
The MCP server uses the same `DangerousConfig` and security classification as
47+
`kode run`. There's no TTY in MCP mode, so the `non_interactive` fallback applies:
48+
49+
- **Default**: `"allow"` — all commands run without prompting
50+
- Configure `"non_interactive": "deny"` in your kode config to block prompted
51+
operations in MCP mode
52+
- Configure per-class overrides (e.g., `"network_egress": "deny"`) for
53+
fine-grained control
54+
55+
```json
56+
{
57+
"dangerous": {
58+
"non_interactive": "deny",
59+
"classes": {
60+
"network_egress": "deny",
61+
"code_execution": "prompt"
62+
}
63+
}
64+
}
65+
```
66+
67+
See [SECURITY.md](SECURITY.md) for details.
68+
69+
## Protocol
70+
71+
The server uses **stdio transport** — JSON-RPC 2.0 messages over stdin/stdout:
72+
73+
- `initialize` — protocol handshake (`protocolVersion: "2025-03-26"`)
74+
- `tools/list` — returns all available tools with schemas
75+
- `tools/call` — invokes a tool with the given arguments
76+
- `ping` — health check (returns empty object)
77+
- `initialized` — notification (no response expected)
78+
79+
Standard MCP capabilities (`tools`, `resources`, `prompts`) — currently only
80+
`tools` are implemented.
81+
82+
## Logging
83+
84+
Startup info and errors are written to stderr. stdin/stdout are reserved for
85+
the MCP protocol. Claude Code captures stderr automatically and shows it in
86+
its logs.

0 commit comments

Comments
 (0)