MCP integration across the Koder Stack
Abstract
Model Context Protocol (MCP) is Anthropic's JSON-RPC-2.0-based standard for LLMs to talk to tools (callable functions), resources (readable contexts), and prompts (parametric templates). Originally stdio-only; the 2025-06-18 spec revision added the streamable-http transport (a.k.a. WebMCP) which is the production-relevant variant for distributed deployments.
This RFC defines how MCP fits into the Koder Stack across client and server roles, and opens the first 4 implementation tickets (KMCP-001..004) against the highest-impact components. It does NOT mandate every component become MCP-capable — only the ones where MCP unlocks a use case the user already wants but can't satisfy today.
Why MCP
The user's recurring pattern in 2026-Q2 has been "I want my chat in Kruze / my data in kdb / my repos in Flow to talk to LLMs without me copying and pasting context". Today each integration is bespoke: ai-gateway calls Koder Flow's Gitea API directly; kdb has a custom streaming-JSON channel for AI exports; Kruze's AI panel has no way to consume tools outside its own conversation buffer.
MCP collapses these into one wire format that any LLM host (Claude Desktop, Cursor, internal Koder AI panel, third-party agents) can speak. Concretely:
- The user adds an MCP server URL in Kruze's AI panel settings and
the chat gains
slack.send,linear.create_issue,notion.searchtools without ai-gateway shipping any new code. - An external Claude Desktop attaches to
https://kdb.koder.dev/mcp/with the user's Koder ID OAuth token and can
kdb.query+ readkdb://tables/usersas a resource — same wire format as accessing Slack or GitHub MCP servers. - Koder Flow exposes
repos.list,issues.create,prs.reviewasMCP tools at
https://flow.koder.dev/mcp/so coding agents (Aider, Cursor, our own future Koder Aider) drive Flow uniformly.
Roles
MCP client
A component that consumes toolsresourcesprompts from external MCP servers. The classic UX is "user pastes a server URL in settings; the LLM-driven chat in the app can now call that server's tools".
Koder components with a natural MCP-client surface today:
- Kruze AI panel (KMCP-001) — the chat sidebar in the desktop
browser. Highest value: zero ai-gateway changes needed; user- installed tools land in the chat immediately.
- Dek (deferred) — once
services/ai/chat-adapterstandardisesthe LLM-host shape used by Dek's editor.
MCP server
A component that exposes its own functions as tools / its own data as resources. The host LLM (anyone with a compatible client) calls them via JSON-RPC.
Koder components with a natural MCP-server surface today:
- Kruze browser surface (KMCP-002) —
open_tab,read_active_tab,screenshot,click,eval_js,print_pdf,start_debug_mode. Reuses the existing webview_cef + capture pipeline; new code is the JSON-RPC + Koder-ID-auth layer. - kdb-next (KMCP-003) —
list_tables,describe_table,query(sql),sample(table, n)as tools; large tables as MCP resources read-only. Casa com stack-RFC-001. - Koder Flow (KMCP-004) —
list_repos,list_issues,comment_pr,create_issue,git_log,get_file(repo, path). Forkargithub-mcp-server-style implementations and swap the REST backend for Gitea API.
Deferred but plausible v2 candidates: servicesaitrace (traces as MCP resources for LLM debugging incidents), Hub (search/install apps), servicesfoundationid (whoami, list_workspaces).
Architectural decisions
Transport: streamable-http (WebMCP)
stdio works for single-process LLM hosts (Claude Desktop launching a local Python server). For Koder we need cross-machine + cross-product: Kruze on the laptop talking to kdb in the LXC cluster. The 2025-06-18 spec's streamable-http transport uses HTTP POST for client→server and SSE (or chunked HTTP) for server→client streaming. Every Koder MCP server lives behind a Koder Jet vhost (<service>.koder.dev/mcp/).
stdio support stays available where it's cheap (e.g. a local CLI companion), but it's not the canonical transport.
Auth: Koder ID OAuth bearer tokens
Every MCP server MUST require a Authorization: Bearer <token> header and validate it against Koder ID. The bearer token IS the tenant discriminator — policies/multi-tenant-by-default.kmd applies: every tool's response set is scoped to the caller's koder_user_id. Cross- tenant attempts return 404 (per the multi-tenancy contract — never 403, to avoid existence-leak side channels).
For local stdio MCP servers, auth is the OS-process boundary (already trusted).
Carve-out — public read-only servers (amended 2026-05-24, ratified via design-RFC-010): an MCP server that exposes only public, read-only data with no tenant scope MAY serve anonymously (no Bearer required). The multi-tenancy contract does not apply because there is no tenant data to scope and nothing to leak — the same content is already served anonymously over HTTP. The KDS MCP server (design-RFC-010, kds.koder.dev/mcp/) is the first such server: it surfaces the public design-system docstokenscomponents. If such a server later adds an authenticated capability (e.g. per-user saved queries), that capability adopts the Bearer model above while anonymous read continues.
Tool naming: <server-slug>.<verb>_<noun>
Per the MCP spec, tools are namespaced by the server they live on. We add the convention that tool names within a server use <verb>_<noun> (snake_case), so the fully-qualified name in the client's tool list reads as kdb.list_tables, flow.create_issue, kruze.open_tab. This matches the OpenAPI verb/noun convention already in use across Koder backends.
SDK extraction (reuse-first.kmd trigger)
The MCP server boilerplate (JSON-RPC dispatcher + transport handshake + Koder ID auth middleware + multi-tenant scoping + standard error shapes) repeats across all 3+ planned server-side components (KMCP-002, -003, -004). Per policies/reuse-first.kmd §"create SDK ≥3 consumers", this triggers an SDK: engines/sdk/koder_mcp (Go) with a Dart binding to follow when Flutter components need MCP-client behaviour beyond what the chat adapter abstracts.
Open question for sub-tickets: should we adopt the existing mark3labs/mcp-go library wholesale (BSD-licensed, lightweight) versus rolling our own. Default direction: adopt + wrap with Koder-specific middleware (auth + tenant scoping); revisit if upstream proves limiting.
Sub-tickets
| ID | Title | Component | Effort |
|---|---|---|---|
| KMCP-001 | Kruze AI panel — MCP client wiring (settings + chat tool-call routing) | products/horizontal/kruze |
2-3 days |
| KMCP-002 | Kruze browser surface — MCP server (open_tabscreenshotpdf/debug_mode) |
products/horizontal/kruze |
3-5 days |
| KMCP-003 | kdb-next — MCP server (list_tablesquerysample) |
infra/data/kdb |
2-3 days |
| KMCP-004 | Koder Flow — MCP server (list_reposissuesprs/git_log) |
products/dev/flow |
1-2 days (fork github-mcp-server) |
Each sub-ticket has its own KMCP-NNN-*.md under the relevant component's backlog/pending/, opened in the same commit that lands this RFC.
Out of scope
- MCP server registry / discovery — users paste URLs manually
in v1. A central "koder/mcp-registry" listing the canonical Koder servers + community ones lands as a future ticket.
- Tool-call sandboxing — beyond per-tenant scoping, we don't
ratelimit or audit individual tool invocations in v1. Aud lands when KMCP-* components have real traffic.
- MCP prompts — the third primitive (after tools + resources)
is parameterised prompt templates. Useful but no clear v1 use case; defer until a component asks for it.
- MCP roots — file-system root negotiation. Useful for desktop
agents; v1 doesn't need it (server-side tools encode their own paths).
- Sampling — MCP servers can request the client's LLM to run
completions on their behalf. Powerful but a security cliff (server-controlled prompts). Off by default in every Koder MCP server; revisit when a real use case shows up.
Migration path
No existing functionality changes — MCP capability is additive on every component. Existing ai-gateway integrations with Flow/kdb stay as-is; the MCP server is a parallel endpoint that other LLM hosts can attach to.
Acceptance
A user with a fresh Koder ID account can:
- Install Kruze (released via the kruze-release.yml pipeline now
that KSHIP-001002003 are fixed).
- Add
https://flow.koder.dev/mcp/to the Kruze AI panel's MCPserver list, sign in via Koder ID OAuth.
- Type "list my open PRs in the koder-tools repo" in the chat.
- Watch the LLM call `flow.list_prs(repo: "koder-tools",
state: "open")` via MCP, receive the result, and produce a structured answer with PR titles + URLs.
That round-trip — without any custom ai-gateway integration code — demonstrates the full client + server + auth + multi-tenant chain.