236 lines
4.7 KiB
Markdown
236 lines
4.7 KiB
Markdown
# 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.
|