dynmcp 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/index.cjs +547 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +547 -141
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema/mcp-config.json +12 -0
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ Large MCPs routinely expose tens to hundreds of tools. When several are active a
|
|
|
15
15
|
|
|
16
16
|
The agent workflow: scan the catalog in `discover_tool`'s description to find relevant tools, call `discover_tool` to load the full schema of the one it needs, then call `use_tool` to execute it. Full schemas of tools the agent never needs never enter the context window.
|
|
17
17
|
|
|
18
|
+
For larger configurations, an optional third tool — **`load_mcp`** — lets the agent defer **whole MCP servers** until needed. Servers declared with a `description` field aren't connected at startup; they appear in a `<mcp_servers>` block in `discover_tool`'s description with their description, and the agent calls `load_mcp` with the server's name to bring it online. See [Dynamic Discovery](#dynamic-discovery).
|
|
19
|
+
|
|
18
20
|
## Usage
|
|
19
21
|
|
|
20
22
|
Requires Node.js >= 20.
|
|
@@ -102,6 +104,74 @@ When no `--` command is provided, `dynmcp` looks for a config file in this order
|
|
|
102
104
|
|
|
103
105
|
MCP names (the keys in the config) must match `^[a-z0-9][a-z0-9-]*$`.
|
|
104
106
|
|
|
107
|
+
## Dynamic Discovery
|
|
108
|
+
|
|
109
|
+
When at least one entry in the config declares a `description` field, **dynamic discovery** is enabled. The named MCP becomes *lazy* — its connection is deferred until the agent explicitly calls `load_mcp` for it. The same trick that `discover_tool`/`use_tool` apply to tool schemas is now applied to whole servers: agents only pay context cost for servers they decide they need.
|
|
110
|
+
|
|
111
|
+
When dynamic discovery is on:
|
|
112
|
+
|
|
113
|
+
- A third meta-tool **`load_mcp`** is exposed to the host.
|
|
114
|
+
- `discover_tool`'s description gains a `<mcp_servers>` block listing every lazy MCP with the description from your config.
|
|
115
|
+
- `<tools>` shows only the catalog of eager (non-lazy) MCPs at startup. As the agent calls `load_mcp`, loaded servers are promoted into `<tools>`.
|
|
116
|
+
|
|
117
|
+
### Example config
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"$schema": "https://unpkg.com/dynmcp/schema/mcp-config.json",
|
|
122
|
+
"mcp": {
|
|
123
|
+
"filesystem": {
|
|
124
|
+
"transport": "stdio",
|
|
125
|
+
"command": "npx",
|
|
126
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
|
|
127
|
+
},
|
|
128
|
+
"chrome-devtools": {
|
|
129
|
+
"description": "Chrome browser automation and DevTools control. Navigate pages, take screenshots, inspect the DOM, run JavaScript, record performance traces, analyze network requests, read console messages. Use for any task that needs to interact with or debug a live web page.",
|
|
130
|
+
"transport": "stdio",
|
|
131
|
+
"command": "npx",
|
|
132
|
+
"args": ["-y", "chrome-devtools-mcp@latest"]
|
|
133
|
+
},
|
|
134
|
+
"aws-knowledge": {
|
|
135
|
+
"description": "AWS documentation, code samples, and best-practice guidance. Search and read AWS docs, API references, blog posts, CDK/CloudFormation templates, and regional availability info. Use when the task involves AWS services or infrastructure-as-code.",
|
|
136
|
+
"transport": "streamable-http",
|
|
137
|
+
"url": "https://knowledge-mcp.global.api.aws"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
In this example, `filesystem` connects at startup. `chrome-devtools` and `aws-knowledge` stay lazy — neither child process is spawned and no HTTP connection is opened until the agent calls `load_mcp` with the corresponding name.
|
|
144
|
+
|
|
145
|
+
### Writing good descriptions
|
|
146
|
+
|
|
147
|
+
The description is what an agent reads to decide whether to load a server. Write it as if you were briefing a teammate who has never seen the MCP:
|
|
148
|
+
|
|
149
|
+
- **Lead with the verbs** the MCP enables ("navigate, click, screenshot...").
|
|
150
|
+
- **Mention the domain** ("Jira tickets", "AWS docs", "Chrome browser").
|
|
151
|
+
- **Include a "use when..." clause** describing the kind of task it's appropriate for.
|
|
152
|
+
- Keep it to a few sentences. The agent reads every lazy server's description on every `discover_tool` call.
|
|
153
|
+
|
|
154
|
+
### `load_mcp`
|
|
155
|
+
|
|
156
|
+
The `load_mcp` tool takes a single `mcp_name` argument matching a key under `mcp` in your config. On success it returns a structured listing of the now-available tools, resources, resource templates, and prompts, and the host receives `notifications/tools/list_changed` (plus `resources/list_changed` and `prompts/list_changed` when applicable) so `discover_tool`'s description refreshes.
|
|
157
|
+
|
|
158
|
+
Notable semantics:
|
|
159
|
+
|
|
160
|
+
- **Idempotent** — calling `load_mcp` for a server that is already loaded (or for an eager server) is a successful no-op returning the current listing.
|
|
161
|
+
- **Permanent** — loaded servers stay loaded for the lifetime of the `dynmcp` process. There is no `unload_mcp`.
|
|
162
|
+
- **Atomic on failure** — if the upstream fails to connect, initialize, or return its catalog, the partial state is torn down; the lazy entry remains in `<mcp_servers>` and the agent can retry.
|
|
163
|
+
- **Retry budget** — after **three** consecutive failed `load_mcp` attempts, the entry is evicted from `<mcp_servers>` entirely. Further calls return "unknown server". This prevents an agent from burning context retrying a permanently broken upstream.
|
|
164
|
+
- **Concurrency** — concurrent `load_mcp` calls for the same name coalesce onto one connection attempt; calls for different names run in parallel.
|
|
165
|
+
|
|
166
|
+
### Capability caveat
|
|
167
|
+
|
|
168
|
+
The MCP protocol negotiates capabilities (resources, prompts, completion, logging) **once** during the host `initialize` call. Lazy upstreams aren't connected at that point, so they contribute nothing to the negotiated capability set. Practical implications:
|
|
169
|
+
|
|
170
|
+
- A lazy MCP's **tools** always work via `load_mcp` → `use_tool` (the `tools` capability is always advertised).
|
|
171
|
+
- A lazy MCP's **resources or prompts** work reliably only if at least one eager MCP in your config also advertises that capability, so the host negotiated to listen for them. The proxy still emits the relevant `*/list_changed` notification on load — but hosts that strictly gate on negotiated capabilities may ignore it.
|
|
172
|
+
|
|
173
|
+
If you want a lazy MCP's resources and prompts to be reachable, keep at least one eager MCP that advertises the same capability, or simply leave the heavier MCP eager.
|
|
174
|
+
|
|
105
175
|
## Environment Variable Interpolation
|
|
106
176
|
|
|
107
177
|
Config files can reference environment variables in any string-typed leaf value using shell-style syntax. This is useful for keeping secrets (bearer tokens, API keys) and host-specific values (paths, ports) out of the config file itself.
|