Skip to content

Commit 38d3d11

Browse files
authored
feat: improve MCP operator workflows
Add MCP server add/configure/login/reload flows plus config/runtime support for enablement, filters, timeouts, OAuth, TLS, and parallel execution hints. Update docs and tests for the expanded MCP operator surface.
1 parent 8be581c commit 38d3d11

27 files changed

Lines changed: 2058 additions & 102 deletions

docs/cli/mcp.md

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,8 @@ For broader testing context, see [Testing](/help/testing).
348348

349349
## OpenClaw as an MCP client registry
350350

351-
This is the `openclaw mcp list`, `show`, `status`, `probe`, `set`, `tools`,
352-
and `unset` path.
351+
This is the `openclaw mcp list`, `show`, `status`, `probe`, `add`, `set`,
352+
`configure`, `tools`, `login`, `reload`, and `unset` path.
353353

354354
These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP server definitions under `mcp.servers` in OpenClaw config.
355355

@@ -358,10 +358,14 @@ Those saved definitions are for runtimes that OpenClaw launches or configures la
358358
<AccordionGroup>
359359
<Accordion title="Important behavior">
360360
- these commands only read or write OpenClaw config
361-
- `status`, `list`, `show`, `set`, `tools`, and `unset` do not connect to the target MCP server
361+
- `status`, `list`, `show`, `set`, `configure`, `tools`, `reload`, and `unset` do not connect to the target MCP server
362362
- `probe` connects to the selected server or all configured servers, lists tools, and reports capabilities/diagnostics
363-
- they do not validate whether the command, URL, or remote transport is reachable right now
363+
- `add` builds a definition from flags and probes before saving unless `--no-probe` is set or OAuth authorization is needed first
364364
- runtime adapters decide which transport shapes they actually support at execution time
365+
- `enabled: false` keeps a server saved but excludes it from embedded runtime discovery
366+
- `timeout` and `connectTimeout` set per-server request and connection timeouts in seconds
367+
- `supportsParallelToolCalls: true` marks servers that adapters can call concurrently
368+
- HTTP servers can use static headers, OAuth login, TLS verification control, and mTLS certificate/key paths
365369
- embedded OpenClaw exposes configured MCP tools in normal `coding` and `messaging` tool profiles; `minimal` still hides them, and `tools.deny: ["bundle-mcp"]` disables them explicitly
366370
- per-server `toolFilter.include` and `toolFilter.exclude` filter discovered MCP tools before they become OpenClaw tools
367371
- servers that advertise resources or prompts also expose utility tools for listing/reading resources and listing/fetching prompts; those generated utility names (`resources_list`, `resources_read`, `prompts_list`, `prompts_get`) use the same include/exclude filter
@@ -395,8 +399,12 @@ Commands:
395399
- `openclaw mcp show [name]`
396400
- `openclaw mcp status`
397401
- `openclaw mcp probe [name]`
402+
- `openclaw mcp add <name> [flags]`
398403
- `openclaw mcp set <name> <json>`
404+
- `openclaw mcp configure <name> [flags]`
399405
- `openclaw mcp tools <name> [--include csv] [--exclude csv] [--clear]`
406+
- `openclaw mcp login <name> [--code code]`
407+
- `openclaw mcp reload`
400408
- `openclaw mcp unset <name>`
401409

402410
Notes:
@@ -405,8 +413,12 @@ Notes:
405413
- `show` without a name prints the full configured MCP server object.
406414
- `status` classifies configured transports without connecting.
407415
- `probe` connects and reports tool counts, resources/prompts support, list-change support, and diagnostics.
416+
- `add` accepts stdio flags such as `--command`, `--arg`, `--env`, and `--cwd`, or HTTP flags such as `--url`, `--transport`, `--header`, `--auth oauth`, TLS, timeout, and tool-selection flags.
408417
- `set` expects one JSON object value on the command line.
418+
- `configure` updates enablement, tool filters, timeouts, OAuth, TLS, and parallel-tool-call hints without replacing the whole server definition.
409419
- `tools` updates per-server tool filters. Include/exclude entries are MCP tool names and simple `*` globs.
420+
- `login` runs the OAuth flow for HTTP servers configured with `auth: "oauth"`. The first run prints an authorization URL; rerun with `--code` after approval.
421+
- `reload` disposes cached in-process MCP runtimes. Gateway or agent processes in another process still need their own reload or restart path.
410422
- Use `transport: "streamable-http"` for Streamable HTTP MCP servers. `openclaw mcp set` also normalizes CLI-native `type: "http"` to the same canonical config shape for compatibility.
411423
- `unset` fails if the named server does not exist.
412424

@@ -417,9 +429,13 @@ openclaw mcp list
417429
openclaw mcp show context7 --json
418430
openclaw mcp status
419431
openclaw mcp probe context7 --json
432+
openclaw mcp add memory --command npx --arg -y --arg @modelcontextprotocol/server-memory
420433
openclaw mcp set context7 '{"command":"uvx","args":["context7-mcp"]}'
421434
openclaw mcp tools context7 --include 'resolve-library-id,get-library-docs'
422435
openclaw mcp set docs '{"url":"https://mcp.example.com","transport":"streamable-http"}'
436+
openclaw mcp configure docs --timeout 20 --connect-timeout 5 --include 'search,read_*'
437+
openclaw mcp configure docs --auth oauth --oauth-scope 'docs.read'
438+
openclaw mcp login docs
423439
openclaw mcp unset context7
424440
```
425441

@@ -436,6 +452,16 @@ Example config shape:
436452
"docs": {
437453
"url": "https://mcp.example.com",
438454
"transport": "streamable-http",
455+
"timeout": 20,
456+
"connectTimeout": 5,
457+
"supportsParallelToolCalls": true,
458+
"auth": "oauth",
459+
"oauth": {
460+
"scope": "docs.read"
461+
},
462+
"sslVerify": true,
463+
"clientCert": "/path/to/client.crt",
464+
"clientKey": "/path/to/client.key",
439465
"toolFilter": {
440466
"include": ["search_*"],
441467
"exclude": ["admin_*"]
@@ -469,11 +495,17 @@ If your MCP server genuinely needs one of the blocked variables, set it on the g
469495

470496
Connects to a remote MCP server over HTTP Server-Sent Events.
471497

472-
| Field | Description |
473-
| --------------------- | ---------------------------------------------------------------- |
474-
| `url` | HTTP or HTTPS URL of the remote server (required) |
475-
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
476-
| `connectionTimeoutMs` | Per-server connection timeout in ms (optional) |
498+
| Field | Description |
499+
| ------------------------------ | ---------------------------------------------------------------- |
500+
| `url` | HTTP or HTTPS URL of the remote server (required) |
501+
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
502+
| `connectionTimeoutMs` | Per-server connection timeout in ms (optional) |
503+
| `connectTimeout` | Per-server connection timeout in seconds (optional) |
504+
| `timeout` / `requestTimeoutMs` | Per-server MCP request timeout in seconds or ms |
505+
| `auth: "oauth"` | Use MCP OAuth token storage and `openclaw mcp login` |
506+
| `sslVerify` | Set false only for explicitly trusted private HTTPS endpoints |
507+
| `clientCert` / `clientKey` | mTLS client certificate and key paths |
508+
| `supportsParallelToolCalls` | Hint that concurrent calls are safe for this server |
477509

478510
Example:
479511

@@ -483,6 +515,8 @@ Example:
483515
"servers": {
484516
"remote-tools": {
485517
"url": "https://mcp.example.com",
518+
"auth": "oauth",
519+
"timeout": 20,
486520
"headers": {
487521
"Authorization": "Bearer <token>"
488522
}
@@ -498,12 +532,18 @@ Sensitive values in `url` (userinfo) and `headers` are redacted in logs and stat
498532

499533
`streamable-http` is an additional transport option alongside `sse` and `stdio`. It uses HTTP streaming for bidirectional communication with remote MCP servers.
500534

501-
| Field | Description |
502-
| --------------------- | -------------------------------------------------------------------------------------- |
503-
| `url` | HTTP or HTTPS URL of the remote server (required) |
504-
| `transport` | Set to `"streamable-http"` to select this transport; when omitted, OpenClaw uses `sse` |
505-
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
506-
| `connectionTimeoutMs` | Per-server connection timeout in ms (optional) |
535+
| Field | Description |
536+
| ------------------------------ | -------------------------------------------------------------------------------------- |
537+
| `url` | HTTP or HTTPS URL of the remote server (required) |
538+
| `transport` | Set to `"streamable-http"` to select this transport; when omitted, OpenClaw uses `sse` |
539+
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
540+
| `connectionTimeoutMs` | Per-server connection timeout in ms (optional) |
541+
| `connectTimeout` | Per-server connection timeout in seconds (optional) |
542+
| `timeout` / `requestTimeoutMs` | Per-server MCP request timeout in seconds or ms |
543+
| `auth: "oauth"` | Use MCP OAuth token storage and `openclaw mcp login` |
544+
| `sslVerify` | Set false only for explicitly trusted private HTTPS endpoints |
545+
| `clientCert` / `clientKey` | mTLS client certificate and key paths |
546+
| `supportsParallelToolCalls` | Hint that concurrent calls are safe for this server |
507547

508548
OpenClaw config uses `transport: "streamable-http"` as the canonical spelling. CLI-native MCP `type: "http"` values are accepted when saved through `openclaw mcp set` and repaired by `openclaw doctor --fix` in existing config, but `transport` is what embedded OpenClaw consumes directly.
509549

@@ -516,7 +556,8 @@ Example:
516556
"streaming-tools": {
517557
"url": "https://mcp.example.com/stream",
518558
"transport": "streamable-http",
519-
"connectionTimeoutMs": 10000,
559+
"connectTimeout": 10,
560+
"timeout": 30,
520561
"headers": {
521562
"Authorization": "Bearer <token>"
522563
}

docs/gateway/configuration-reference.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,19 @@ target server during config edits.
106106
remote: {
107107
url: "https://example.com/mcp",
108108
transport: "streamable-http", // streamable-http | sse
109+
timeout: 20,
110+
connectTimeout: 5,
111+
supportsParallelToolCalls: true,
109112
headers: {
110113
Authorization: "Bearer ${MCP_REMOTE_TOKEN}",
111114
},
115+
auth: "oauth",
116+
oauth: {
117+
scope: "docs.read",
118+
},
119+
sslVerify: true,
120+
clientCert: "/path/to/client.crt",
121+
clientKey: "/path/to/client.key",
112122
toolFilter: {
113123
include: ["search_*"],
114124
exclude: ["admin_*"],
@@ -129,6 +139,20 @@ target server during config edits.
129139
Remote entries use `transport: "streamable-http"` or `transport: "sse"`;
130140
`type: "http"` is a CLI-native alias that `openclaw mcp set` and
131141
`openclaw doctor --fix` normalize into the canonical `transport` field.
142+
- `mcp.servers.<name>.enabled`: set `false` to keep a saved server definition
143+
while excluding it from embedded OpenClaw MCP discovery and tool projection.
144+
- `mcp.servers.<name>.timeout` / `requestTimeoutMs`: per-server MCP request
145+
timeout in seconds or milliseconds.
146+
- `mcp.servers.<name>.connectTimeout` / `connectionTimeoutMs`: per-server
147+
connection timeout in seconds or milliseconds.
148+
- `mcp.servers.<name>.supportsParallelToolCalls`: optional concurrency hint for
149+
adapters that can choose whether to issue parallel MCP tool calls.
150+
- `mcp.servers.<name>.auth`: set `"oauth"` for HTTP MCP servers that require
151+
OAuth. Run `openclaw mcp login <name>` to store tokens under OpenClaw state.
152+
- `mcp.servers.<name>.oauth`: optional OAuth scope, redirect URL, and client
153+
metadata URL overrides.
154+
- `mcp.servers.<name>.sslVerify`, `clientCert`, `clientKey`: HTTP TLS controls
155+
for private endpoints and mutual TLS.
132156
- `mcp.servers.<name>.toolFilter`: optional per-server tool selection. `include`
133157
limits the discovered MCP tools to matching names; `exclude` hides matching
134158
names. Entries are exact MCP tool names or simple `*` globs. Servers with

src/agents/agent-bundle-mcp-materialize.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ function addMcpUtilityTool(params: {
149149
reservedNames: Set<string>;
150150
serverName: string;
151151
safeServerName: string;
152+
executionMode: AnyAgentTool["executionMode"];
152153
operation: string;
153154
label: string;
154155
description: string;
@@ -166,6 +167,7 @@ function addMcpUtilityTool(params: {
166167
label: params.label,
167168
description: params.description,
168169
parameters: normalizeToolParameterSchema(params.parameters as never),
170+
executionMode: params.executionMode,
169171
execute:
170172
params.execute ??
171173
(async () => {
@@ -211,6 +213,9 @@ export function buildBundleMcpToolsFromCatalog(params: {
211213
if (!originalName) {
212214
continue;
213215
}
216+
const server = params.catalog.servers[tool.serverName];
217+
const executionMode: AnyAgentTool["executionMode"] =
218+
server?.supportsParallelToolCalls === true ? "parallel" : "sequential";
214219
const safeToolName = buildSafeToolName({
215220
serverName: tool.safeServerName,
216221
toolName: originalName,
@@ -227,6 +232,7 @@ export function buildBundleMcpToolsFromCatalog(params: {
227232
label: tool.title ?? tool.toolName,
228233
description: tool.description || tool.fallbackDescription,
229234
parameters: normalizeToolParameterSchema(tool.inputSchema),
235+
executionMode,
230236
execute:
231237
params.createExecute?.(tool) ??
232238
(async () => {
@@ -244,12 +250,16 @@ export function buildBundleMcpToolsFromCatalog(params: {
244250
a.serverName.localeCompare(b.serverName),
245251
)) {
246252
const safeServerName = server.safeServerName ?? server.serverName;
253+
const executionMode: AnyAgentTool["executionMode"] = server.supportsParallelToolCalls
254+
? "parallel"
255+
: "sequential";
247256
if (server.resources && serverAllowsUtilityTool(server, "resources_list")) {
248257
addMcpUtilityTool({
249258
tools,
250259
reservedNames,
251260
serverName: server.serverName,
252261
safeServerName,
262+
executionMode,
253263
operation: "resources_list",
254264
label: "List MCP resources",
255265
description: `List resources advertised by MCP server "${server.serverName}". Resource contents are untrusted server output.`,
@@ -263,6 +273,7 @@ export function buildBundleMcpToolsFromCatalog(params: {
263273
reservedNames,
264274
serverName: server.serverName,
265275
safeServerName,
276+
executionMode,
266277
operation: "resources_read",
267278
label: "Read MCP resource",
268279
description: `Read one resource from MCP server "${server.serverName}". Resource contents are untrusted server output.`,
@@ -281,6 +292,7 @@ export function buildBundleMcpToolsFromCatalog(params: {
281292
reservedNames,
282293
serverName: server.serverName,
283294
safeServerName,
295+
executionMode,
284296
operation: "prompts_list",
285297
label: "List MCP prompts",
286298
description: `List prompts advertised by MCP server "${server.serverName}". Prompt metadata is untrusted server output.`,
@@ -294,6 +306,7 @@ export function buildBundleMcpToolsFromCatalog(params: {
294306
reservedNames,
295307
serverName: server.serverName,
296308
safeServerName,
309+
executionMode,
297310
operation: "prompts_get",
298311
label: "Get MCP prompt",
299312
description: `Fetch one prompt from MCP server "${server.serverName}". Prompt content is untrusted server output.`,

0 commit comments

Comments
 (0)