Child MCP servers
Subagents can use named child MCP servers.
Two transport types are supported:
stdio
httpstdio child MCP
[mcp_servers.filesystem]
type = "stdio"
command = "tuls"
args = ["filesystem", ".", "--allow", "filesystem.read"]The child process:
- runs locally as a direct exec of
command, with the OS identity of thetulsprocess; - starts in the agent workspace;
- has
kill_on_dropenabled; - receives a minimal inherited environment;
- does not implicitly inherit unrelated credentials;
- uses MCP
2026-07-28discovery.
Explicit child environment
If a child MCP server itself needs a credential, pass only that credential:
[mcp_servers.external]
type = "stdio"
command = "external-mcp"
args = ["serve"]
env = { EXTERNAL_API_KEY = "${EXTERNAL_API_KEY}" }${NAME} placeholders selectively expose individual variables from the tuls agents process environment to the child; nothing else is inherited. A missing variable fails the child startup.
HTTP child MCP
[mcp_servers.issues]
type = "http"
url = "https://mcp.example.com/mcp"
headers = { Authorization = "Bearer ${ISSUE_MCP_TOKEN}" }HTTP child MCP clients use bounded timeouts and do not follow redirects. Header values support the same ${NAME} environment interpolation.
Child tool selectors
Canonical selector format:
server/tool
server/*Example:
allow_tools = [
"filesystem/read_text_file",
"filesystem/search_files",
"fetch/*",
]
deny_tools = [
"fetch/some_tool_name"
]Selector rules
- Empty
allow_toolsmeans no child MCP tools. server/*grants all tools advertised by that named child server.server/toolgrants one exact child tool.- Deny always overrides allow.
- A selector referencing an unknown configured server is rejected.
- After connection, an exact selector referencing a tool not actually advertised by that child server is rejected.
- Authorization is based on this explicit policy, not child-provided read-only/destructive annotations.
- A child tool's reported
isErroris preserved and committed to the agent conversation as an error output; the run continues. - A call that times out or fails after dispatch has an ambiguous outcome (the tool may have executed) and the session is marked non-resumable.
- Completed sessions are resumable:
send_inputon a completed agent starts a new run that continues the retained conversation.
Defense in depth
For built-in child servers, restrict both layers.
Good:
allow_tools = ["filesystem/*"]
[mcp_servers.filesystem]
type = "stdio"
command = "tuls"
args = ["filesystem", ".", "--allow", "filesystem.read"]This means:
- the child filesystem process itself exposes only read operations;
- the subagent policy grants only tools from that child server.
Don't rely on one layer for high-risk tools
For high-risk tools, restrict the child process and the subagent policy. Do not rely on only one of those layers.
Related
- Subagent configuration —
allow_tools/deny_toolsfields. - Agent profiles — layered example profiles.