/mcp/v1.
This is the inverse of MCP Integration: there, Comis connects
to third-party MCP servers; here, Comis is the MCP server other platforms
connect to.
Why use this
External agent platforms benefit from Comis’s accumulated state and skills:Claude Desktop
Hook into Comis’s per-tenant memory, search past sessions, or invoke
web tools from any Claude Desktop conversation.
Cursor or other IDEs
Use Comis’s web search, document extraction, or workspace tools from
inside your editor’s MCP integration.
Custom agent apps
Build internal agents that call Comis as a skill backend via a single
HTTPS endpoint with bearer-token auth.
Cross-platform memory
A Claude Desktop agent reads what your Discord agent remembered — same
memory store, same tenant.
Security model
The/mcp/v1 endpoint exposes only what the operator explicitly authorizes.
Five invariants make this safe.
1. Default-deny tool exposure
1. Default-deny tool exposure
Every platform tool is annotated with
mcpExportPolicy — one of
safe, permission-gated, or never-export. Missing annotation is
treated as never-export (default-deny). A CI architecture gate
rejects pull requests that add a tool without an explicit
classification.See Exposed tools for the full 51-tool table.2. mcp-client scope is disjoint from admin
2. mcp-client scope is disjoint from admin
The
mcp-client scope is rejected on any non-/mcp/v1 route. Tokens
that try to co-issue admin and mcp-client are rejected at
config-load with [scope_disjointness]. An MCP client cannot
escalate by calling tokens_manage against itself — the admin scope
is structurally unreachable from the MCP path.3. Rate limit: 30/min/tool/client by default
3. Rate limit: 30/min/tool/client by default
Every
tools/call increments a per-(client, tool) minute bucket.
Calls beyond the ceiling return [rate_limit_exceeded] with a
resetAt epoch-ms. The default is 30/min, overridable per token via
mcpClient.toolRateLimit[name].4. CONFIRMED-only resource visibility
4. CONFIRMED-only resource visibility
resources/read filters out outbound messages that have not yet been
delivered to the channel adapter (deliveryStatus !== "pending").
External MCP clients never see in-flight outbound, only what’s
already been delivered.5. Trust-flag isolation at dispatch
5. Trust-flag isolation at dispatch
The dispatcher strips any
_trustLevel field from MCP tool args
before invoking the underlying RPC, and the composition root’s
indirection (daemonRpcForMcpClient) never spreads
_trustLevel: "admin". Even if a never-export tool slipped past
the policy filter, the dispatch path makes admin-trust behavior
unreachable from the MCP wire.The endpoint inherits the gateway’s TLS configuration. Strongly recommended:
run
/mcp/v1 behind TLS in any deployment where the client is not on the
same host. See Hardening for gateway.tls setup.Bootstrap — issuing an mcp-client token
Token issuance is a two-step operator workflow.Mint the token via the admin LLM
An operator with an admin-scoped session asks the admin agent to call
The agent returns the new token’s
tokens_manage:id and secret. Capture the
secret once — it is hashed in storage and not recoverable.Claude Desktop config
Once your token is minted and themcpClient block is attached, configure
Claude Desktop’s mcpServers block.
- macOS
- Windows
- Linux
~/Library/Application Support/Claude/claude_desktop_config.jsonPer-client config reference
Thegateway.tokens[i].mcpClient block has three optional fields:
| Field | Type | Default | Purpose |
|---|---|---|---|
allowlist | string[] | [] | Permission-gated tools this token may invoke. Tools in the safe bucket are exposed even with an empty allowlist. Tools NOT in this list and NOT in safe are hidden from tools/list and rejected on tools/call. |
sessionAllowlist | string[] | [] | Session keys this token may read via resources/list and resources/read. Session-key format: {agent}:{channelType}:{channelId}:{userId} (e.g. default:discord:guild123:channel456:user789). Empty means no session resources visible. |
toolRateLimit | Record<string, number> | {} | Per-tool rate-limit override in calls/minute. Tools not listed use the 30/min default. Buckets reset on the UTC-minute boundary. |
The
mcpClient block is optional. A token with scopes: ["mcp-client"]
and no mcpClient block can only call safe tools, sees no session
resources, and hits the default 30/min/tool ceiling.Exposed tools
Tools are partitioned into three buckets permcpExportPolicy. This is the
starting classification — see the security-reviewer gate
before flipping /mcp/v1 live on a publicly reachable instance.
safe (3) — exposed to any mcp-client token
No allowlist required. These tools have no PII surface, perform no Comis
state read, and operate purely on caller-supplied input.
| Tool | Why safe |
|---|---|
web_search | Public web search; no Comis state read |
web_fetch | Public HTTP GET; no Comis state read |
browser | Public web browsing; no Comis state read |
permission-gated (21) — exposed only if token’s mcpClient.allowlist includes the tool
Caller-data-dependent. These read Comis state OR consume caller media that
may resolve to Comis-stored content.
| Tool | Why permission-gated |
|---|---|
read | Workspace file read |
ls | Workspace directory listing |
find | Workspace search by name |
grep | Workspace content search |
memory_search | Reads Comis memory (per-tenant) |
memory_get | Reads Comis memory by key |
session_search | Reads session history |
session_status | Reads session metadata |
sessions_list | Enumerates sessions |
sessions_history | Reads full session transcript |
obs_query | Observability read |
obs_explain | Incident report (digest-only) — runs the assembler directly under daemon authority, NOT the admin RPC; allowlist is the grant, no admin trust |
discover_tools | Reveals registered tool surface (meta) |
image_analyze | Vision over caller-supplied OR Comis-stored media — TODO: security-reviewer re-confirm |
describe_video | Same caveat as image_analyze |
extract_document | Same caveat as image_analyze |
transcribe_audio | Same caveat as image_analyze |
never-export (28) — NEVER exposed under any operator config
Admin, mutation, outbound channel sends, cross-account, and cost-bearing
synthesis. These cannot be added to an allowlist — the runtime filter
always drops them.
| Category | Tools |
|---|---|
| Filesystem mutation | write, edit, apply_patch |
| Process / command execution | exec, process |
| Memory write / delete | memory_store, memory_manage |
| Session mutation, send, spawn | sessions_manage, sessions_send, sessions_spawn, subagents |
| Scheduled tasks | pipeline, cron |
| Admin (9) | gateway, heartbeat_manage, channels_manage, tokens_manage, skills_manage, mcp_manage, agents_manage, providers_manage, models_manage |
| Outbound channel send | message, whatsapp_action, discord_action, telegram_action, slack_action |
| Cost-bearing synthesis | tts_synthesize |
Security-reviewer gate on the 51-tool classification
Before flipping/mcp/v1 live on any publicly reachable Comis instance, a
security reviewer must audit each classification against the threat model.
Use the rubric:
safe— no PII, no Comis state read, no mutation, no outbound. Pure caller-supplied input → pure caller-supplied output.permission-gated— reads Comis state OR consumes caller data; the operator gates per-token viamcpClient.allowlist.never-export— admin, mutation, outbound channel send, secrets, cross-account, or anything whose RPC contract is scoped["admin"].
image_analyze, describe_video, extract_document,
transcribe_audio) are conservatively classified as permission-gated.
The reviewer decides whether any can be upgraded to safe (if the
tool only ever reads caller-supplied media) or must stay
permission-gated (if any code path resolves a Comis-stored media ID).
Wire-format notes
The Comis MCP server runs@modelcontextprotocol/sdk@1.29.0. Compatible
client SDK versions: 1.20.0 and newer. Older clients may fail at
initialize if the protocol-version negotiation cannot find a common
version.
By default the transport uses Server-Sent Events (SSE). The SDK’s
enableJsonResponse option supports a JSON-only response mode, but Comis
ships SSE by default. Most Claude Desktop and Cursor versions handle SSE
natively. If you see a silent hang or an early-close from your client,
verify the client’s transport configuration matches.
Resource caching
resources/read returns the current CONFIRMED state of a session.
Re-reading the same URI may return additional messages as outbound
delivery completes (an in-flight outbound message that was hidden on the
first read may appear on a later read once the channel adapter confirms
delivery).
This matches the MCP spec’s “resources may change” semantics. Cache
invalidation is the client’s responsibility — if your client caches by
URI, set a short TTL or invalidate on every conversation turn.
Troubleshooting
| Error / symptom | Likely cause | Fix |
|---|---|---|
401 Unauthorized | Bearer token missing or invalid | Check the Authorization: Bearer ... header; re-mint the token via tokens_manage if you lost the secret. |
403 Insufficient scope | Token doesn’t include mcp-client | Issue a new token with scopes: ["mcp-client"]. |
403 disjoint-scope violation | Token co-issues admin and mcp-client | Config validation should have caught this at load time. Re-mint the token with only mcp-client; check daemon logs for the [scope_disjointness] line. |
tools/list returns only 3 tools | Empty mcpClient.allowlist — only safe tools surface | Add the permission-gated tool names you want exposed to mcpClient.allowlist and reload. |
[rate_limit_exceeded] on every call | Default 30/min/tool exhausted | Tune via mcpClient.toolRateLimit[name] for the specific tool; or wait until the next UTC-minute boundary (resetAt in the error). |
resources/list returns empty | mcpClient.sessionAllowlist is empty | Add the specific session keys you want exposed. Format: {agent}:{channelType}:{channelId}:{userId}. |
Silent connection close after initialize | SDK version mismatch | Verify client SDK >= 1.20.0. Check daemon logs for protocol-version-negotiation-failed. |
What’s NOT exposed
Out of scope for the current server, deferred:sampling/createMessage— server-initiated LLM calls from Comis back to the client. Not advertised.roots/notifications— workspace-root change notifications. Not advertised.prompts/*— Comis does not expose prompts via MCP. Thepromptscapability is not advertised; clients should fall back gracefully.- Native binary content in
tools/callresults — image and audio tool outputs are returned as text descriptions (JSON-stringified). Native{type: "image", data: base64}content is deferred.
See also
- MCP Integration — Comis as MCP client, the inverse direction.
- Token Security — token storage and rotation.
- Hardening — TLS, reverse proxy, and gateway hardening.
- Rate limiting — gateway-level rate limits that apply before the per-tool MCP rate limit.
