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:

DirectionWhat it meansWhere
ClientYour agent calls tools from external MCP serverstools/mcp_hub
ServerYour workflows are exposed as MCP tools to external AI clientscheng-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

TransportConfigure
stdiocommand (e.g. uvx, npx) plus args and env — JSON-RPC over newline-delimited stdin/stdout to a child process
streamable_httpurl, 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

InputMeaning
tool_nameserver:tool — or just tool when there is only one server
argumentsJSON object of parameters
discover_onlyList every server's tools without calling anything
timeout_msRequest 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:

ModeWhat it exposesEndpoint
ChengMcpServerApplication-level workflow operations as tools/mcp, enabled by MCP_ENABLED=true
WorkflowMcpServerOne published workflow's toolsIts 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

SymptomLook at
Server shows disconnectedFor stdio, is the command on the server's PATH? For HTTP, is url reachable from the backend, not from your browser?
Tools do not appearRun 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 callThe server has expensive startup; set long_running
Model calls a tool you did not wantSet callable: false for it — do not rely on prompt instructions

Next steps