# pyMCPBroker Specification ## Purpose pyMCPBroker exposes a very small stable API to a language model while wrapping one or more MCP servers running over `stdio`. The model never sees raw MCP tools directly. It only sees three stable meta-tools: - `meta_tree` - `meta_desc` - `meta_call` Normal workflow: 1. call `meta_tree` to navigate 2. call `meta_desc` on a leaf path 3. call `meta_call` with arguments matching the schema returned by `meta_desc` ## Terminology - **entry**: logical element exposed to the model - `type=node`: navigation node - `type=tool`: callable leaf - **path**: stable absolute URL-like identifier - **source**: real MCP backend mounted on a node - `backend=stdio`: MCP process launched locally over stdin/stdout ## Public API ### `POST /meta_tree` Input: ```json { "path": "/" } ``` Returns the direct children of a node path. Errors if the path does not exist or if it points to a tool leaf. ### `POST /meta_desc` Input: ```json { "path": "/repo/read/get_file" } ``` For a node path, returns node metadata and optionally summarized children. For a tool path, returns: - stable path - summary - description - exact `args_schema` - optional `example_args` ### `POST /meta_call` Input: ```json { "path": "/repo/read/get_file", "args": { "owner": "myorg", "repo": "demo-repo", "ref": "main", "filePath": "README.md" } } ``` The broker validates `args` against the dynamic schema previously returned by `meta_desc`, then calls the real MCP tool. ## Config format The config is static JSON loaded at startup. Top-level shape: ```json { "tree": [ ... ] } ``` or: ```json { "tree": { "path": "/", "type": "node", "children": [ ... ] } } ``` The explicit root node is optional. If omitted, `/` is created implicitly. ### Node fields - `path` - `type="node"` - `summary` optional - `description` optional - `children` optional - `source` optional ### Source fields - `backend`: currently only `"stdio"` - `command`: shell command to launch the MCP server - `tool_filter`: optional unordered list of allow/deny glob patterns - `tool_overrides`: optional per-tool overrides - `path_aliases`: optional mapping from real MCP tool name to exposed leaf name ### Environment variables `${ENV_VAR}` substitution is supported in strings, especially in `command`. Missing variables fail at startup. ## Source mounting model A `source` mounted on a node causes the broker to: 1. start the MCP process 2. initialize the MCP session 3. fetch `tools/list` 4. apply `tool_filter` 5. apply `tool_overrides` 6. expose the remaining tools as child leaves under the node path The exposed leaf path is: - `parent_path + / + alias`, if `path_aliases` defines one - otherwise `parent_path + / + tool_name` Example: - node path: `/repo/read` - real tool: `get_file_contents` - alias: `get_file` - exposed path: `/repo/read/get_file` ## Filter semantics `tool_filter` is optional. Positive patterns allow tools. Patterns prefixed with `!` deny tools. Rules: - if there is no positive pattern, all tools are allowed first, then deny rules are applied - if at least one positive pattern exists, only tools matching a positive pattern are allowed, then deny rules are applied - pattern order does not matter Examples: - `[]` → expose all tools - `["!delete_*"]` → expose everything except delete tools - `["get_*", "list_*"]` → expose only get/list tools - `["get_*", "!get_secret_*"]` → expose get tools except secret ones ## Overrides `tool_overrides` is optional. Supported fields: - `summary` - `description` - `max_output_chars` - `timeout` - `example_args` - `render_mode` They only affect the broker-facing presentation and execution limits. They do not rename the real MCP tool. ## Output normalization The broker can truncate large outputs using `max_output_chars`. Current behavior: - preserve JSON structure when possible - truncate long strings first - compact long lists if needed - return an explicit wrapper when truncation happened ## Internal MCP support Current transport support: - MCP `stdio` only Required MCP methods: - `initialize` - `tools/list` - `tools/call` The broker also sends `notifications/initialized` after initialization. ## CLI ```bash python -m pyMCPBroker 0.0.0.0:8100 /config.json ``` Optional shared secret: ```bash python -m pyMCPBroker 0.0.0.0:8100 /config.json mysecret ``` Accepted options: - `--reload` - `--ignore-broken-tool` - `--log-level` - `--dump-tree` ## Repository editor config The repository may include a `.vscode/` directory with recommended Python extensions plus launch/task settings for pytest and for starting the broker against `smoke_config.json`. This editor config is optional and does not affect runtime behavior.