openai: shared MCP layer + openclaw-tools bridge [Adolf P5]
- adolf-llm/server.js now loads SHARED_MCP_SERVERS from the mounted
/shared-mcp.json instead of a hardcoded stub, so adding a shared MCP
server is a one-file change. Verified end-to-end: a real chat-completions
turn writes a session .mcp.json containing both cognee and openclaw-tools
entries (kimi itself still needs `kimi login` in adolf-llm-home, unrelated
to this change).
- Documented the Gate-1 transport reconciliation: decompiled the installed
@moonshot-ai/kimi-code package to confirm its .mcp.json schema keys remote
servers on `transport` ("stdio"/"http"/"sse", inferred as "http" from a
bare `url`, never "sse"), while OpenClaw's own canonical mcp.servers schema
uses different literals ("streamable-http"/"sse") for the same field name
and treats `type` as a CLI-native alias it normalizes itself. `type: "http"`
is the one shape both consumers tolerate, so shared-mcp.json keeps it.
- New openai/openclaw-tools/ service: a stateless MCP-over-Streamable-HTTP
bridge (Node, @modelcontextprotocol/sdk) exposing message_send, cron_create,
cron_list, nodes_invoke, and browser_invoke, each proxying to the OpenClaw
gateway's POST /tools/invoke. Verified initialize + tools/list handshake and
a tools/call against the not-yet-running `adolf` gateway returns a clean
isError content instead of breaking the MCP connection. Documented that
cron/nodes are hard-denied on that HTTP surface by default until P6 adds
them to gateway.tools.allow; message/browser are not similarly restricted.
- Wired openclaw-tools into docker-compose.yml (openai network, :8020) and
added its shared-mcp.json entry alongside cognee.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
@@ -19,21 +19,45 @@ fs.mkdirSync(STATE_DIR, { recursive: true });
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared MCP layer (Gate 1). Kimi Code CLI has NO `--mcp-config-file` flag and
|
||||
// no `kimi mcp` subcommand; it auto-discovers a project-root `.mcp.json`
|
||||
// (Claude-Code-compatible schema) by walking up from its cwd. So we drop a
|
||||
// `.mcp.json` into each session's working directory before spawning kimi.
|
||||
// no `kimi mcp` subcommand; it auto-discovers a project-root `.mcp.json` by
|
||||
// walking up from its cwd to the nearest `.git` (falling back to cwd itself
|
||||
// when none is found). So we drop a `.mcp.json` into each session's working
|
||||
// directory before spawning kimi.
|
||||
//
|
||||
// STUB: the server list is empty for now. cognee-mcp (memory_search/add/cognify,
|
||||
// P4) and the openclaw-tools bridge (P5) do not exist yet. Wiring them later is
|
||||
// a one-line change here — add entries to SHARED_MCP_SERVERS and every future
|
||||
// session dir picks them up automatically. Schema per server, e.g.:
|
||||
// cognee: { command, args, env } (stdio)
|
||||
// openclaw-tools:{ type: "sse"|"http", url, headers } (remote)
|
||||
// TODO(P4/P5): populate SHARED_MCP_SERVERS from cognee-mcp + openclaw-tools.
|
||||
const SHARED_MCP_SERVERS = {
|
||||
// TODO(P4): "cognee": { command: "...", args: [...], env: {...} },
|
||||
// TODO(P5): "openclaw-tools": { type: "sse", url: "http://openclaw:.../mcp" },
|
||||
};
|
||||
// Single source of truth: `/shared-mcp.json` (mounted read-only from the repo
|
||||
// root's `shared-mcp.json`, the same file P6 wires into OpenClaw's own
|
||||
// `mcp.servers` registry). Adding a server is then a one-file change — no
|
||||
// server list is hardcoded here anymore.
|
||||
//
|
||||
// Gate-1 transport finding (P5, verified by decompiling the installed
|
||||
// @moonshot-ai/kimi-code package, packages/agent-core/src/config/schema.ts's
|
||||
// McpServerConfigSchema): Kimi's own field name for remote MCP servers is
|
||||
// `transport` (literal "stdio" | "http" | "sse"), not `type`. When `transport`
|
||||
// is omitted, Kimi's config preprocessor infers it from shape: `command` ->
|
||||
// "stdio", `url` -> "http" (never "sse" — sse requires an explicit
|
||||
// `transport: "sse"`). It does NOT recognize a `type` key at all; unknown keys
|
||||
// are silently stripped by the (non-strict) zod schema.
|
||||
// OpenClaw's own canonical `mcp.servers` schema (docs/gateway/
|
||||
// configuration-reference.md) uses different literals for the same
|
||||
// transport: `transport: "streamable-http"` or `"sse"`, with `type: "http"`
|
||||
// documented as a *CLI-native alias* that `openclaw mcp set` / `openclaw
|
||||
// doctor --fix` normalize into canonical `transport: "streamable-http"`.
|
||||
// So the two consumers disagree on the literal value for HTTP streaming
|
||||
// ("http" vs "streamable-http") under the same field name `transport` --
|
||||
// writing `transport` explicitly in shared-mcp.json would satisfy at most one
|
||||
// side. `type: "http"` is the one shape both sides tolerate today: Kimi
|
||||
// ignores the unrecognized `type` key and correctly infers transport "http"
|
||||
// from the `url` field alone; OpenClaw recognizes `type` as its documented
|
||||
// alias and normalizes it on its own terms (P6 concern, not touched here).
|
||||
// Hence shared-mcp.json intentionally keeps `"type": "http"` for both cognee
|
||||
// and openclaw-tools rather than switching to `transport`.
|
||||
let SHARED_MCP_SERVERS = {};
|
||||
try {
|
||||
const raw = fs.readFileSync('/shared-mcp.json', 'utf8');
|
||||
SHARED_MCP_SERVERS = JSON.parse(raw).mcpServers || {};
|
||||
} catch (err) {
|
||||
console.error(`shared-mcp.json not loaded (${err.message}); sessions will get no shared MCP servers`);
|
||||
}
|
||||
|
||||
function writeMcpConfig(dir) {
|
||||
const cfg = { mcpServers: SHARED_MCP_SERVERS };
|
||||
|
||||
Reference in New Issue
Block a user