Skip to content

Latest commit

 

History

History
179 lines (129 loc) · 9.37 KB

MCP_AGENT_BOOTSTRAP.md

File metadata and controls

179 lines (129 loc) · 9.37 KB

MCP Agent Bootstrap (Hermes & OpenClaw)

How marketplace agent apps connect to the Hub MCP server at runtime, and what to verify before dev testing.

Hub MCP server (control plane edge)

The Hub exposes a platform MCP server (not the per-app stdio bridge):

Endpoint Method Auth
/api/mcp POST/GET (Streamable HTTP) Authorization: Bearer <API key>

The hashed key store is the sole auth authority (SEC-MCP-8). Nothing is seeded at boot: every key in the store was either created by an operator or provisioned to an app, so revoking one really retires it. The guard deliberately has no MCP_API_KEY env fallback — that value is derived from the appliance seed, and a live env compare would be a credential no revoke could retire.

Create a key one of two ways:

  • CLI — cihub api-key create --name "<label>". Prints the raw key once; store it immediately.
  • UI — Settings → Security. Rotate by creating the new key, rolling it out, then revoking the old one.

cihub mcp setup only toggles MCP_ENABLED; it does not mint or install a credential.

At app install, when hub_integration.mcp_client: true, the Hub injects:

Env var Purpose
HUB_URL Hub base URL (health, inference REST)
HUB_MCP_URL Streamable HTTP MCP endpoint — <HUB_URL>/api/mcp
HUB_MCP_API_KEY Per-app managed MCP key, minted by the Hub key store (revoked on uninstall)
HUB_WAKE_SECRET Validates inbound wake webhooks (OpenClaw)
HUB_MCP_ENABLED Set false to skip MCP wiring

Marketplace entries for ci-hermes and ci-openclaw set mcp_client: true.

The app's UI drives an agent that holds this key, so the Hub publishes an mcp_client app's host port on 127.0.0.1 only, not on the LAN or tailnet. To reach the app from another device, use Private VPN or Public web. Apps whose manifest sets hub_integration.edge_auth.default: true get the same bind, because they have no login of their own.

On its own app a managed key passes every per-app grant check but one: changing which custom domain the app serves takes an organization owner or admin, so that change is refused with CUSTOM_DOMAIN_ROLE_REQUIRED at every level. On every other app, the level the key is given in Settings → Security decides how far it reaches (CI-Hub#1397). The level is read on every call, so a change applies from the agent's next call:

Level On an app that is not its own
read Read the app: status, logs, config, backups, skill, tools, OpenAPI spec, and operation status.
write (what a managed key is provisioned with) Also start, stop, or restart it, and call its MCP tools (hub_call_app_tool) and its API with any method (hub_call_app_api).
full Everything it may do on its own app. Only an organization owner or admin can give a key full.

Below full, the rest is refused with APP_ACTION_GRANT_DENIED: installing the app; changing its configuration (user config, ignored versions, custom-app compose or metadata, availability repair, and app config); uninstalling, resetting, or updating it; cancelling its operation in progress; and taking, restoring, or deleting a backup. Cancelling and taking a backup sit with full because both can lose data: cancelling an install deletes what that install wrote to an app with no earlier data. Each backup's retention cleanup deletes the app's oldest backups.

These are grant rules, and the key's capability still gates each tool on top of them, on its own app too. At write the destructive tools stay refused: hub_call_app_tool; a hub_call_app_api call that does more than read; uninstall, reset and update; restoring or deleting a backup; rewriting user config or a custom app's compose; an install or config form that names a custom domain; and the bulk update, stop and restart tools.

The bulk tools apply the same rule to each app. At full, start, stop, restart, and update all act on every app; below full, the bulk start tool starts every app, and the bulk update tool updates only the key's own app. If an agent must change other apps, give it an operator key created in Settings → Security, which acts with the grants and role of the person who created it. A key from cihub api-key create records no creator, so no one's grants bound it: it may act on every app, up to its capability, though it cannot change which custom domain an app serves.

The tools that change the whole Hub, or read its log, take a key whose creator is an organization owner or admin, or the operator who claimed the Hub, asked on every call. They are the model pull, load, unload, pin and unpin tools, hub_set_cloud_provider, hub_set_auto_updates, hub_perform_update, the app store add, update, delete and pull tools and hub_get_hub_logs. A managed key and a key from cihub api-key create have no such person, so Hermes and OpenClaw don't see these tools. On a 2025-era session their call gets HUB_MANAGE_KEY_REISSUE_REQUIRED. A key a member created gets HUB_MANAGE_ROLE_REQUIRED there. A 2026-07-28 client never has these tools registered for such a key, so its call fails as an unknown tool (Tool <name> not found).

CI-Hermes path

Boot order (entrypoint.sh / gateway-entrypoint.sh):

  1. bootstrap-from-hub.sh — pulls inference credentials, writes /opt/data/config.yaml and .env.hub-bootstrap.
  2. bootstrap-ci-context.sh — Companion Intelligence plugin, memory MCP, then Hub MCP.
  3. configure-hub-mcp.py — idempotently adds mcp_servers.hub to config.yaml:
mcp_servers:
  hub:
    url: "http://ci-hub:5002/api/mcp"
    headers:
      Authorization: "Bearer <HUB_MCP_API_KEY>"
    timeout: 180
    connect_timeout: 60

There is deliberately no transport: line — omitting it leaves the Hermes agent on its default Streamable HTTP client, which is what /api/mcp speaks. Hermes reaches Hub tools through that native client; no hand-rolled protocol code is on the hot path.

Tests: ci-hermes/tests/configure-hub-mcp.test.sh, ci-hermes/tests/hub-mcp-smoke.sh

CI-OpenClaw path

Boot order (entrypoint.sh):

  1. bootstrap-from-hub.sh — inference + Ollama env.
  2. bootstrap-ci-memory.sh — CI-Server memory plugin.
  3. bootstrap-ci-hub-mcp.sh — registers bundled plugin path in openclaw.json when creds present.
  4. server.cjs — on setup/onboarding, ensureOpenClawPlugin('ci-hub', …) enables the entry.

The bundled plugin lives at openclaw-context/plugins/ci-hub/:

  • index.js — OpenClaw entry; reads env only (no secrets in openclaw.json).
  • hub-plugin.mjs — bundled from ci-hub/packages/openclaw-plugin (pnpm bundle with OPENCLAW_PLUGIN_OUT_DIR).

Plugin behavior:

  • Exposes POST /hooks/hub-wake for Hub → agent wake notifications.
  • Optional SSE listener (off by default).

It does not register Hub tools or an inference provider. Tools load through OpenClaw's own MCP client via the mcp.servers.ci-hub entry in openclaw.json; provider registration was removed deliberately (CI-Hub#895) and must not be reintroduced.

Re-bundle after plugin source changes:

cd ci-hub/packages/openclaw-plugin
OPENCLAW_PLUGIN_OUT_DIR=/path/to/ci-openclaw/openclaw-context/plugins/ci-hub pnpm bundle

Inference vs MCP

These are separate channels:

Channel Hermes OpenClaw
Chat inference OPENAI_API_* from Hub bootstrap ci-hub Ollama provider in openclaw.json
Memory MCP CI-Server via configure-ci-server.py ci-memory plugin slot
Hub platform MCP mcp_servers.hub in config.yaml ci-hub OpenClaw plugin

Intent catalog plugins (companionintelligence) from upstream master are orthogonal — they add skills/intents, not Hub MCP wiring.

Dev test checklist

Hub

  • MCP enabled (cihub mcp setup) and an mcp-scoped key created (cihub api-key create)
  • GET /api/health OK
  • scripts/qa-mcp-bridge.ts or e2e pnpm e2e:mcp passes

Install / restart agents

  • Install or restart ci-hermes and ci-openclaw from marketplace
  • Container logs show Hub MCP registration (Hermes: [Hub MCP] Server registered; OpenClaw: CI-Hub plugin initializing)

Hermes

  • config.yaml contains mcp_servers.hub with the /api/mcp URL, Bearer header, and no transport: line
  • Agent can list/call a Hub tool (e.g. hub_get_inference_status)

OpenClaw

  • openclaw.json has plugins.entries.ci-hub.enabled: true and an mcp.servers.ci-hub entry
  • Gateway logs show Hub MCP tools served via native OpenClaw mcp.servers.ci-hub config
  • POST /hooks/hub-wake accepts signed payloads when wake is configured

Frontend (ci-hub)

  • Registration, onboarding, AI settings, app store install flows
  • Hermes/OpenClaw install injects env (inspect app container env in Hub UI or docker inspect)

See also: MCP_TESTING_STRATEGY.md