Skip to main content

MCP

Kheish can load Model Context Protocol servers and expose their tools directly inside the runtime.

Loading MCP

At startup, the daemon can read MCP configuration from Codex-compatible locations or explicit paths. It can also expand built-in MCP catalog profiles with --mcp-profile. Durable MCP tokens should live in the daemon secret store, not in config files. The daemon owns that MCP inventory globally. Personas and sessions do not start or stop MCP servers themselves. Examples:
./target/debug/kheish-daemon serve \
  --mcp-profile docs \
  --state-root .kheish-daemon-test \
  --workspace-root .kheish-workspace-test
KHEISH_MCP_PROFILES=docs,repo ./target/debug/kheish-daemon serve
Catalog profiles are opt-in. Kheish ships the catalog in the binary so users can discover and enable common servers without writing TOML, but it does not start every catalog entry by default. Use these local CLI commands to inspect the shipped catalog:
./target/debug/kheish-daemon mcp catalog list
./target/debug/kheish-daemon mcp catalog list --profile docs --supported-only
./target/debug/kheish-daemon mcp catalog get github
./target/debug/kheish-daemon mcp auth slots linear
./target/debug/kheish-daemon mcp profiles list
./target/debug/kheish-daemon mcp profiles get docs
catalog_only entries are documented for operators but are not started by built-in profiles yet, usually because the upstream server requires a vendor-specific OAuth/client setup that has not passed Kheish’s true-binary promotion gate, or because the startup artifact is a mutable local executable that the operator should pin explicitly. GitHub is in this category: it works through explicit Docker stdio config, but --mcp-profile repo does not start it. For the operator-facing connection matrix, including GitHub, GitLab, Linear, Notion, Slack, Playwright, Sentry, Neon, Vercel, Cloudflare, Stripe, and Figma, see MCP catalog. That page distinguishes:
  • entries Kheish can start directly with --mcp-profile
  • entries that need mcp auth set before startup
  • entries that are catalog-only and need explicit --mcp-config
  • entries that depend on upstream OAuth setup outside Kheish

What the runtime gets

When MCP is enabled, Kheish surfaces:
  • the active MCP tool names
  • server-level instructions
  • runtime snapshot information visible through the control plane
  • selected built-in profile names when --mcp-profile is used
  • per-server source metadata such as built_in_catalog or codex_config
This makes MCP a first-class runtime surface rather than an afterthought hidden behind a single generic tool.

Session and persona filtering

The daemon-global MCP inventory is not automatically exposed in full to every session. There are now two MCP boundaries:
  • CapabilityScope decides which MCP servers, MCP tools, and MCP helper tools remain visible
  • CredentialScope decides whether auth-backed MCP surfaces remain usable for one session or child agent
Runtime views can still expose a smaller active subset after that, for example when the current agent configuration explicitly denies some MCP tools. By default, a session with no persona capability baseline and no session capability scope starts from the daemon-global MCP inventory. Once you bind a persona or set a session capability scope, the runtime can hide:
  • whole MCP servers
  • individual MCP tools
  • helper tools such as list_mcp_resources, list_mcp_resource_templates, and read_mcp_resource
This lets operators keep one global MCP inventory on the daemon while exposing only the right subset to each persona or session. If you also set a session credential scope, the daemon can deny credentialed MCP servers even when they remain visible in the tool surface. This is the correct way to express rules such as “this child may keep route access but must not inherit GitHub or Datadog credentials”.

Sidechains

Sidechains inherit the parent session’s effective capability boundary at spawn time. That means child agents do not suddenly regain MCP visibility that the parent session had already restricted. They also inherit the parent session’s effective credential boundary. When a delegated child does not request an explicit child credential_scope, Kheish keeps route access but denies connector credentials and credentialed MCP access by default.

Current auth boundary

The daemon-global MCP inventory is still loaded and supervised daemon-wide.
  • CredentialScope governs whether daemon-held credentials may be used for one execution
  • it does not imply one dedicated stdio server process per session
  • public MCP helpers or non-credentialed MCP tools may remain available even when delegated credential access is denied
Treat CapabilityScope plus CredentialScope as the supported session and sidechain boundary. Do not interpret the current global stdio transport model as per-session MCP process isolation. Built-in catalog entries that use local stdio commands are currently catalog_only unless Kheish can start them without mutable executable artifacts. The stdio runner path supports a restricted environment for built-in entries instead of inheriting the whole daemon environment. Codex-compatible custom stdio config keeps compatibility behavior and inherits the daemon environment when no secret refs are configured. When env_secret_refs is present, Kheish forces a restricted child environment and rejects inherit_env = true; put every required value in env or env_secret_refs. For supported catalog entries with token auth, use the catalog auth helper before startup:
export KHEISH_AUTH_STORE_MASTER_KEY="$(./target/debug/kheish-daemon secrets generate)"
./target/debug/kheish-daemon mcp auth slots linear
./target/debug/kheish-daemon mcp auth set linear \
  --from-env LINEAR_API_KEY \
  --offline \
  --state-root .kheish-daemon-mcp-test
The helper stores a generic opaque secret under the catalog slot, such as mcp.linear.LINEAR_API_KEY. Built-in catalog startup resolves those mcp.* slots from the daemon secret store and does not require the token to remain in the process environment. When you write the secret offline before daemon startup, start the daemon with the same --state-root and the same KHEISH_AUTH_STORE_MASTER_KEY. For Linear, the built-in planning profile uses Streamable HTTP with bearer/API-key material from the daemon secret store; without mcp.linear.LINEAR_API_KEY, Linear reports an auth-required connection error. Explicit MCP config can also reference daemon secrets:
[mcp_servers.internal]
url = "https://mcp.internal.example/mcp"
bearer_token_secret_ref = "mcp.custom.internal.BEARER_TOKEN"
http_header_secret_refs = { "x-api-key" = "mcp.custom.internal.X_API_KEY" }

[mcp_servers.local]
command = "internal-mcp"
env_secret_refs = { "INTERNAL_TOKEN" = "mcp.custom.local.INTERNAL_TOKEN" }
inherit_env = false
Explicit HTTP MCP config can reference a daemon-managed OAuth account:
[mcp_servers.acme]
url = "https://mcp.acme.example/mcp"
oauth_slot_ref = "mcp.oauth.acme"
oauth_resource = "https://mcp.acme.example/mcp"
oauth_scopes = ["read", "search"]
Create that account with kheish-daemon mcp oauth login ...; see OAuth. Current OAuth-backed MCP config is account plumbing plus fail-closed startup validation; Kheish does not yet expose tools from these servers. If a configured server would need OAuth material during bootstrap, runtime get reports error: "oauth_requires_scoped_runtime_initialization" instead of sending a daemon-global token. Catalog-only entries still show their expected credential_env and credential_secret_refs for operator planning, but built-in profiles do not start them directly. MCP inventory is loaded at daemon startup. After changing --mcp-config or rotating a secret that an MCP server reads, restart the daemon so the server reconnects with the new configuration.

Collision behavior

MCP tool names are qualified as mcp__<server>__<tool>. If two configured servers or tools would collapse to the same registered name after qualification and sanitization, daemon startup fails instead of silently replacing a tool.

Operational guidance

Validate MCP on a real daemon with the providers you intend to use. The effective experience depends on server connectivity, credential resolution, tool exposure, and how the model uses the surfaced tools and instructions.

Playbooks and flows

Playbooks can describe expected tool policy and scope guidance, but MCP exposure is still governed by the daemon runtime, persona/session capability scopes, and credential scopes. A Flow does not create a separate MCP inventory.

Evidence note

  • Code verified: crates/kheish-mcp/src/catalog.rs, crates/kheish-mcp/src/config.rs, crates/kheish-mcp/src/manager.rs, crates/kheish-daemon/src/cli/commands/mcp.rs, crates/kheish-daemon/src/cli/secrets.rs, crates/kheish-daemon/src/cli/serve.rs, crates/kheish-auth/src/oauth.rs.
  • CLI verified: mcp catalog, mcp profiles, mcp auth slots, mcp auth set, mcp oauth status/login/refresh/logout, and serve --mcp-profile are implemented in the daemon CLI.
  • Daemon live tested: yes, through the true-binary MCP catalog, secret-store, and MCP OAuth account-protocol E2E scenarios when this page was updated.
  • Vendor manual live checks: GitHub explicit Docker stdio config was validated outside the local harness with evidence under /tmp/kheish-github-mcp-run.7lFCOV: mcp.github.GITHUB_PERSONAL_ACCESS_TOKEN from the Kheish secret store, OpenAI gpt-5.4, runtime source: "codex_config", and a real mcp__github__get_me tool call returning GITHUB_MCP_OK:graniet. Linear without LINEAR_API_KEY was checked as a fail-closed auth-required path only.
  • Provider-specific tested: GitHub was validated with OpenAI gpt-5.4; other MCP servers and provider routes still need their own live validation before rollout.