Unified MCP server — the single entry point for AI agents. One connection gives access to every KiwiStack service: mail, calendar, contacts, tasks, docs, search, and more. Speaks Streamable HTTP (MCP over SSE).
Meta-tools for discovery, plus the aggregated tool catalog from all registered services.
Single entry point for all AI agent interactions.
Kiwi MCP is the Skin layer — the outermost surface of the kiwi fruit. AI agents connect
to a single MCP endpoint and get access to every tool from every service. No need to know which service handles
what — Kiwi MCP routes tools/call requests to the right wrapper automatically.
Unlike other components, Kiwi MCP has no upstream binary. The rmcp SDK is a Rust library linked directly into the wrapper. This makes it a pure KiwiStack service — no two-process model, no upstream lifecycle management.
Flow: Agent → Kiwi Gate (TLS termination) → Kiwi MCP (JWT validation + tool routing) → fan-out to service wrappers (Kiwi Mail, Kiwi Chat, etc.) → aggregate responses → return to agent.
Agent ↓ Streamable HTTP (MCP/SSE) Kiwi Gate (10.10.20.1:443) ↓ TLS terminated, JWT forwarded Kiwi MCP (10.10.20.19:8443) ↓ Route by tool namespace Kiwi Mail (10.10.20.11:8443) ← mail.*, calendar.*, contacts.* Kiwi Chat (10.10.20.12:8443) ← chat.* Kiwi Work (10.10.20.14:8443) ← tasks.*, projects.*
Wrapper configuration — lives at /etc/kiwi/config.toml.
# kiwi-mcp configuration listen_addr = "10.10.20.19:8443" jwt_public_key = "/etc/kiwi/jwt-public.pem" log_level = "info" # Registered services (tool routing) [[services]] name = "kiwi-mail" addr = "http://10.10.20.11:8443" namespaces = ["mail", "calendar", "contacts"] [[services]] name = "kiwi-chat" addr = "http://10.10.20.12:8443" namespaces = ["chat"] [[services]] name = "kiwi-work" addr = "http://10.10.20.14:8443" namespaces = ["tasks", "projects"]
| Key | Type | Description |
|---|---|---|
| listen_addr | string | Bind address (private bridge) |
| jwt_public_key | path | Kiwi ID public key for JWT validation |
| services[].name | string | Service identifier for logging/metrics |
| services[].addr | string | Service wrapper HTTP address |
| services[].namespaces | string[] | Tool namespaces routed to this service |
| log_level | string | Tracing level: error, warn, info, debug, trace |
Simpler than other components — no upstream binary (rmcp is a library).
/ ├── opt/ │ └── kiwi/ # Kiwi MCP (Apache 2.0) │ ├── bin/kiwi-mcp # binary (rmcp SDK linked in) │ └── version # "0.1.0" ├── etc/ │ └── kiwi/ │ ├── config.toml # service config (see above) │ └── jwt-public.pem # Kiwi ID public key ├── var/ │ └── log/ │ └── kiwi/ # JSON structured logs └── run/
Fully Apache-2.0 — no copyleft concerns.
The rmcp SDK is licensed under Apache-2.0, the same license as KiwiStack itself. There are no copyleft obligations. Kiwi MCP is a pure Rust binary with no upstream process — the SDK is compiled directly into the binary.
This is the simplest licensing story in the KiwiStack catalog: everything is Apache-2.0, from the SDK to the wrapper to the configuration. No boundaries to manage, no file-level copyleft, no network isolation required.
An MCP tools/call over Streamable HTTP.
POST https://mcp.example.com/mcp Content-Type: application/json Authorization: Bearer <kiwi-id-jwt> { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "mail.search", "arguments": { "query": "quarterly report", "limit": 5 } } }
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "Found 3 emails matching 'quarterly report'..."
}],
"isError": false
}
}
Pinned versions — from compatibility.toml.
# kiwi-mcp/compatibility.toml [sdk] name = "rmcp" version = "0.1.5" [wrapper] version = "0.1.0" rust_edition = "2024" msrv = "1.85" [tested] date = "2026-02-28" kiwi_id = "0.1.0"