@@ -329,8 +329,12 @@ type CodeModeFailedResult = {
329329};
330330```
331331
332- ` exec ` returns ` waiting ` when the QuickJS VM suspends with resumable state. The
333- result includes a ` runId ` for ` wait ` .
332+ ` exec ` returns ` waiting ` when the QuickJS VM suspends with resumable state that
333+ still needs a model-visible continuation. The result includes a ` runId ` for
334+ ` wait ` . Namespace bridge calls, including MCP namespace calls, are auto-drained
335+ inside the same ` exec ` /` wait ` call while they are ready, so a compact code block
336+ can inspect ` $api() ` and call an MCP tool without forcing one model tool call per
337+ namespace await.
334338
335339` exec ` returns ` completed ` only when the guest VM has no pending work and the
336340final value is JSON-compatible after OpenClaw's output adapter runs.
@@ -436,18 +440,55 @@ const hits = await tools.web_search({ query: "OpenClaw code mode" });
436440```
437441
438442MCP catalog entries are not callable through ` tools.call(...) ` or convenience
439- functions in code mode. Use the generated ` MCP ` namespace instead:
443+ functions in code mode. They are exposed only through the generated ` MCP `
444+ namespace, which includes TypeScript-style API headers for discovery:
440445
441446``` typescript
447+ const servers = await MCP .$api ();
448+ const githubApi = await MCP .github .$api ();
449+ const createIssueApi = await MCP .github .$api (" createIssue" , { schema: true });
450+
442451const issue = await MCP .github .createIssue ({
443452 owner: " openclaw" ,
444453 repo: " openclaw" ,
445454 title: " Investigate gateway logs" ,
446455});
447456
448457const snapshot = await MCP .chromeDevtools .takeSnapshot ({ output: " markdown" });
449- const resource = await MCP .docs .resources .read (" memo://one" );
450- const prompt = await MCP .docs .prompts .get (" brief" , { topic: " release" });
458+ const resource = await MCP .docs .resources .read ({ uri: " memo://one" });
459+ const prompt = await MCP .docs .prompts .get ({
460+ name: " brief" ,
461+ arguments: { topic: " release" },
462+ });
463+ ```
464+
465+ ` MCP.<server>.$api() ` returns a compact header inferred from MCP tool metadata:
466+
467+ ``` typescript
468+ type McpToolResult = {
469+ content? : unknown [];
470+ structuredContent? : unknown ;
471+ isError? : boolean ;
472+ [key : string ]: unknown ;
473+ };
474+
475+ declare namespace MCP .github {
476+ /** Return this TypeScript-style API header. */
477+ function $api(toolName ? : string , options ? : { schema? : boolean }): Promise <McpApiHeader >;
478+
479+ /**
480+ * Create a GitHub issue.
481+ * @param owner Repository owner
482+ * @param repo Repository name
483+ * @param title Issue title
484+ */
485+ function createIssue(input : {
486+ owner: string ;
487+ repo: string ;
488+ title: string ;
489+ body? : string ;
490+ }): Promise <McpToolResult >;
491+ }
451492```
452493
453494The guest runtime must not expose host objects directly. Inputs and outputs cross
@@ -493,8 +534,9 @@ run follows this path:
4935346 . Guest calls suspend through the worker bridge, resolve the namespace path on
494535 the host, map the call to a declared plugin-owned catalog tool, and execute
495536 that tool through ` ToolSearchRuntime.call ` .
496- 7 . ` wait ` resumes the same namespace runtime when a code-mode run suspended on
497- nested tool work.
537+ 7 . OpenClaw auto-drains ready namespace bridge calls inside the active
538+ ` exec ` /` wait ` tool call. If namespace work is still pending at the timeout or
539+ the guest yields explicitly, ` wait ` resumes the same namespace runtime later.
4985408 . Plugin rollback or uninstall calls ` clearCodeModeNamespacesForPlugin(pluginId) `
499541 so stale globals do not survive a failed plugin load.
500542
@@ -701,9 +743,10 @@ This prevents recursion and keeps the model-facing contract narrow.
701743
702744MCP entries stay in the run-scoped catalog so policy, approvals, hooks,
703745telemetry, transcript projection, and exact tool ids remain shared with normal
704- tool execution. The guest-facing ` tools.call(...) ` bridge rejects MCP entries;
705- the generated ` MCP.<server>.<tool>(...) ` namespace resolves back to the exact
706- catalog id and then dispatches through the same executor path.
746+ tool execution. The guest-facing ` ALL_TOOLS ` , ` tools.search(...) ` ,
747+ ` tools.describe(...) ` , and ` tools.call(...) ` views omit MCP entries. The
748+ generated ` MCP.<server>.<tool>({ ...input }) ` namespace resolves back to the
749+ exact catalog id and then dispatches through the same executor path.
707750
708751## Tool Search interaction
709752
@@ -716,8 +759,9 @@ When `tools.codeMode.enabled` is true and code mode activates:
716759 or ` tool_call ` as model-visible tools.
717760- The same cataloging idea moves inside the guest runtime.
718761- The guest runtime receives compact ` ALL_TOOLS ` metadata and search, describe,
719- and call helpers.
720- - MCP calls use the generated ` MCP ` namespace instead of ` tools.call(...) ` .
762+ and call helpers for non-MCP tools.
763+ - MCP calls use the generated ` MCP ` namespace and its ` $api() ` headers instead
764+ of ` tools.call(...) ` .
721765- Nested calls dispatch through the same OpenClaw executor path that Tool Search
722766 uses.
723767
@@ -934,11 +978,13 @@ Code mode coverage should prove:
934978 active for the run
935979- raw no-tool runs, ` disableTools ` , and empty allowlists do not trigger code-mode
936980 payload enforcement
937- - all effective tools appear in ` ALL_TOOLS `
981+ - all effective non-MCP tools appear in ` ALL_TOOLS `
938982- denied tools do not appear in ` ALL_TOOLS `
939983- ` tools.search ` , ` tools.describe ` , and ` tools.call ` work for OpenClaw tools
940- - MCP namespace calls work for visible MCP tools and direct MCP ` tools.call `
941- attempts fail closed
984+ - MCP namespace ` $api() ` returns TypeScript-style headers inferred from MCP
985+ schemas
986+ - MCP namespace calls work for visible MCP tools with one object input, while
987+ direct MCP catalog entries are absent from ` tools.* `
942988- Tool Search control tools are hidden from both the model surface and the hidden
943989 catalog
944990- nested calls preserve approval and hook behavior
@@ -968,14 +1014,16 @@ Run these as integration or end-to-end tests when changing the runtime:
96810147 . In ` exec ` , read ` ALL_TOOLS ` and assert the effective test tools are present.
96910158 . In ` exec ` , call OpenClaw/plugin/client tools through ` tools.search ` ,
9701016 ` tools.describe ` , and ` tools.call ` .
971- 9 . In ` exec ` , call MCP tools through ` MCP.<server>.<tool>(...) ` and assert direct
972- MCP ` tools.call(...) ` attempts fail.
973- 10 . Assert denied tools are absent and cannot be called by guessed id.
974- 11 . Start a nested tool call that resolves after ` exec ` returns ` waiting ` .
975- 12 . Call ` wait ` and assert the restored VM receives the tool result.
976- 13 . Assert the final answer contains output produced after restore.
977- 14 . Assert timeout, abort, and snapshot expiry clean up runtime state.
978- 15 . Export trajectory and assert nested calls are visible under the parent
1017+ 9 . In ` exec ` , call ` MCP.$api() ` and ` MCP.<server>.$api() ` and assert the headers
1018+ describe visible MCP tools.
1019+ 10 . In ` exec ` , call MCP tools through ` MCP.<server>.<tool>({ ...input }) ` and
1020+ assert direct MCP catalog entries are absent from ` ALL_TOOLS ` and ` tools.* ` .
1021+ 11 . Assert denied tools are absent and cannot be called by guessed id.
1022+ 12 . Start a nested tool call that resolves after ` exec ` returns ` waiting ` .
1023+ 13 . Call ` wait ` and assert the restored VM receives the tool result.
1024+ 14 . Assert the final answer contains output produced after restore.
1025+ 15 . Assert timeout, abort, and snapshot expiry clean up runtime state.
1026+ 16 . Export trajectory and assert nested calls are visible under the parent
9791027 code-mode call.
9801028
9811029Docs-only changes to this page should still run ` pnpm check:docs ` .
0 commit comments