Connecting MCP Servers
Applies to: ChengOS v0.1.0+ | Last verified: 2026-08-12 | Source:
crates/cheng-nodes/src/nodes/builtin/tools/mcp_hub.rs,crates/cheng-mcp/src/lib.rs,crates/cheng-core/src/domain/mcp_server.rs
ChengOS speaks MCP in both directions:
| Direction | What it means | Where |
|---|---|---|
| Client | Your agent calls tools from external MCP servers | tools/mcp_hub |
| Server | Your workflows are exposed as MCP tools to external AI clients | cheng-mcp |
This page is mostly about the client side, which is what "connecting an MCP server" usually means.
The MCP Hub node
tools/mcp_hub is one node that manages several MCP servers. Connect it to an agent's
tools port and the agent gains every tool those servers expose, subject to the visibility
rules below.
Its guiding principle is config-first: by default the node returns only server status. It performs a request only when you explicitly discover or call. Dropping the node on the canvas does not fire off connections.
Transports
| Transport | Configure |
|---|---|
stdio | command (e.g. uvx, npx) plus args and env — JSON-RPC over newline-delimited stdin/stdout to a child process |
streamable_http | url, optional authorization, optional headers — JSON-RPC over HTTP POST |
The two are mutually exclusive: a server config with a command is stdio, one with a url is
HTTP.
Each server entry also has enabled, a per-server timeout, and a long_running flag.
long_running keeps a persistent session — a live child process for stdio, a live HTTP session
otherwise — instead of reconnecting per call. Use it for servers with expensive startup; the
node caches the session and reuses it.
Credentials, not literals
Do not paste API keys into the server config. Connect a utils/credential node's env_vars
output to the hub's env_vars input, then reference values as ${env:KEY}.
Placeholders expand in every string field — url, authorization, headers, args, and
env. Values from the credential node take precedence over the system process environment when
both define a key, so a workflow's own credentials cannot be shadowed by whatever happens to be
in the server's environment.
Visibility control
Exposing every tool from every server to a model is usually wrong: it burns context and hands the model destructive operations by default. The hub separates visible (the model can see it) from callable (the model may invoke it).
The recommended structured form:
{
"servers": {
"my-server": { "visible": true, "callable": true },
"*": { "visible": true, "callable": true }
},
"tools": {
"my-server:search": { "visible": true, "callable": true },
"my-server:delete": { "visible": false, "callable": false },
"*": { "visible": true, "callable": true }
}
}
There is also a flat compatibility form keyed by server:tool. When both are present,
visibility_config wins over tool_visibility.
Matching runs most-specific first:
1. server:tool exact
2. server:* server wildcard
3. *:tool tool-name wildcard
4. * global default
The pattern worth adopting: set * to visible-and-callable, then explicitly deny the
destructive tools by name. A tool that is visible: false does not consume context either.
Calling tools
| Input | Meaning |
|---|---|
tool_name | server:tool — or just tool when there is only one server |
arguments | JSON object of parameters |
discover_only | List every server's tools without calling anything |
timeout_ms | Request timeout; 0 or empty means no limit |
tool_name, arguments, and discover_only are LLM-fillable. The server registry, visibility
config, and credentials are static canvas configuration the model never sees — which is what
keeps the model from editing its own permissions.
Output is a single result port carrying summary, per-server status, discovered tools, and any
tool-call output, so the node's shape does not change between configuration, discovery, and
call.
Multimodal results
MCP tools can return more than text, and the hub preserves all of it in content_parts:
{"type":"text","text":"..."}
{"type":"image","mimeType":"image/png","data":"base64..."}
{"type":"resource","resource":{"uri":"...","text":"...","mimeType":"..."}}
An MCP tool that renders a chart therefore reaches a vision-capable model as an image rather than as a dropped attachment.
Publishing workflows as MCP servers
The other direction. cheng-mcp provides two server modes:
| Mode | What it exposes | Endpoint |
|---|---|---|
ChengMcpServer | Application-level workflow operations as tools | /mcp, enabled by MCP_ENABLED=true |
WorkflowMcpServer | One published workflow's tools | Its own ephemeral port, e.g. http://127.0.0.1:PORT/mcp |
Both use Streamable HTTP. For stdio:
cargo run -p cheng-mcp -- --stdio
A published workflow gets an McpServer record binding it to a gateway port, an internal URL
reported by the node via heartbeat, and a public gateway URL used in the JSON manifest. The
publishing user owns the auth tokens.
Troubleshooting
| Symptom | Look at |
|---|---|
| Server shows disconnected | For stdio, is the command on the server's PATH? For HTTP, is url reachable from the backend, not from your browser? |
| Tools do not appear | Run with discover_only first; if they appear there but not to the agent, it is a visibility rule |
| Auth failures | ${env:KEY} unresolved — check the credential node is connected and the key name matches |
| Slow first call | The server has expensive startup; set long_running |
| Model calls a tool you did not want | Set callable: false for it — do not rely on prompt instructions |
Next steps
- Tool Discovery & Tool Hub — how the agent sees these tools alongside built-in ones.
- ReAct Agent Guide — the agent that calls them.
- Skills System Overview — the other way to package capability.