MCP integration across the Koder Stack
stackRFC002 — MCP integration across the Koder Stack
Abstract
Model Context Protocol (MCP) is Anthropic's JSONRPC2.0-based standard for LLMs to talk to *ools*(callable functions), *esources*(readable contexts), and *rompts*(parametric templates). Originally stdioonly; the 20250618 spec revision added the `streamablehttp` 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 *lient*and *erver*roles, and opens the first 4 implementation tickets (KMCP001..004) against the highestimpact 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 *ne 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 *onsumes*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:
- *ruze AI panel*(KMCP-001) — the chat sidebar in the desktop
browser. Highest value: zero ai
gateway changes needed; userinstalled tools land in the chat immediately. - *ek*(deferred) — once
services/ai/chat-adapterstandardisesthe LLM-host shape used by Dek's editor.
MCP server
A component that *xposes*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:
- *ruze 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 JSONRPC + KoderID-auth layer. - *db
next*(KMCP003) —list_tables,describe_table,query(sql),sample(table, n)as tools; large tables as MCP resources readonly. Casa com stackRFC-001. - *oder 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 crossmachine + crossproduct: Kruze on the laptop talking to kdb in the LXC cluster. The 20250618 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).
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 serverside components (KMCP002, 003, -004). Per `policies/reusefirst.kmd §"create SDK ≥3 consumers",
this triggers an SDK: **enginessdkkoder_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 |
Each subticket has its own `KMCPNNN-*.md under the relevant
component's backlogpending`, opened in the same commit that lands this RFC.
Out of scope
- *CP 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.
- *ool
call sandboxing*— beyond pertenant scoping, we don'tratelimit or audit individual tool invocations in v1. Aud lands when KMCP-* components have real traffic.
- *CP 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.
- *CP roots*— file-system root negotiation. Useful for desktop
agents; v1 doesn't need it (server-side tools encode their own paths).
- *ampling*— 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 roundtrip — without any custom aigateway integration code — demonstrates the full client + server + auth + multi-tenant chain.