You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,6 +30,10 @@ Framework-neutral foundation for building devframes.
30
30
</a>
31
31
</p>
32
32
33
+
## Credits
34
+
35
+
The `devframe connect` MCP connector (discovery + gateway tools + agent-steering errors) follows the architecture Vercel's [`next-devtools-mcp`](https://github.com/vercel/next-devtools-mcp) validated: the real MCP endpoint lives inside the framework, and a thin external connector discovers and proxies it.
Copy file name to clipboardExpand all lines: docs/adapters/mcp.md
+46Lines changed: 46 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -46,4 +46,50 @@ defineDevframe({
46
46
})
47
47
```
48
48
49
+
### Hosted bridges
50
+
51
+
Both hosted bridges forward the same option to their side-car dev server and advertise the endpoint (with its port) in the `__connection.json` they serve:
`createMcpFetchHandler(ctx, options)` returns the endpoint as a web-standard `Request → Response` handler plus a `dispose()` for session teardown — mount it on any fetch-shaped server (a Next.js App Router route, a custom Node server). The h3 `mountMcpHttp` used by the dev server is a thin wrapper over it.
// route every method on /__mcp to mcp.fetch(request)
74
+
```
75
+
76
+
## Discovery: `devframe connect`
77
+
78
+
The `devframe` bin ships an MCP **connector** — a thin discovery + proxy server in the shape [next-devtools-mcp](https://github.com/vercel/next-devtools-mcp) validated. Configure it once in an agent client and it finds every running devframe:
It exposes two gateway tools (the wire names of the `devframe:connect:*` ids — see [tool ids and wire names](/guide/agent-native#tool-ids-and-wire-names)):
89
+
90
+
-**`devframe_connect_list-instances`** — discover running devframe dev servers and list each one's MCP tools. Instances running without an MCP route are listed with a hint to restart with `--mcp`.
91
+
-**`devframe_connect_call-tool`** — invoke one tool on one instance (`{ port, tool, args }`) over its Streamable-HTTP endpoint.
92
+
93
+
Discovery reads the **instance registry**: every `createDevServer` (CLI `dev`, `viteDevBridge`, `@devframes/next`'s handler) writes a record to `~/.devframe/instances/<pid>-<port>.json` on boot and removes it on close; readers prune records whose liveness probe fails. In-process hosts register explicitly with `registerDevframeInstance` from `devframe/node` — see `createDevframeNextHost().mountMcp` for serving MCP on a Next app's own origin. `--port <n>` probes an explicit port besides the registry; `DEVFRAME_INSTANCES_DIR` relocates the registry and `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` opts a server out.
94
+
49
95
See the [Agent-Native](/guide/agent-native) page for the full API, safety model, and Claude Desktop integration example.
> Failed to update the devframe instance registry at "`{file}`": `{reason}`
10
+
11
+
## Cause
12
+
13
+
A dev server (or an in-process host calling `registerDevframeInstance`) could not write or remove its record under the instance registry directory — `~/.devframe/instances/` by default, or `$DEVFRAME_INSTANCES_DIR`. Typical causes are a read-only home directory, missing permissions, or a full disk. The server keeps running; only discovery is affected — `devframe connect` will not see this instance.
14
+
15
+
## Fix
16
+
17
+
- Check that the registry directory is writable and the disk has free space.
18
+
- Point `DEVFRAME_INSTANCES_DIR` at a writable directory.
19
+
- Set `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` to opt out of registration entirely.
20
+
21
+
## Source
22
+
23
+
-[`packages/devframe/src/node/instance-registry.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/instance-registry.ts) — `registerDevframeInstance()` reports this on a failed write and its `unregister()` on a failed removal.
> `devframe connect` requires the optional peer dependency @modelcontextprotocol/server: `{reason}`
10
+
11
+
## Cause
12
+
13
+
`devframe connect` was started but `@modelcontextprotocol/server` could not be imported. The SDK is an optional peer dependency of `devframe` — the MCP surface stays opt-in, so it only needs to be installed where MCP features are used.
14
+
15
+
## Fix
16
+
17
+
Install the SDK next to devframe and run the connector again:
18
+
19
+
```sh
20
+
npm install @modelcontextprotocol/server
21
+
devframe connect
22
+
```
23
+
24
+
## Source
25
+
26
+
-[`packages/devframe/src/cli/connect.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/connect.ts) — `startConnectServer()` throws this when the dynamic SDK import fails.
> Agent tool "`{id}`" is hidden from the MCP surface: its wire name "`{name}`" collides with the tool "`{existing}`".
10
+
11
+
## Cause
12
+
13
+
MCP clients constrain tool names to `^[a-zA-Z0-9_-]{1,128}$`, so the MCP adapter derives each tool's wire name from its id (runs of characters outside `[a-zA-Z0-9_-]` become a single `_`). Two registered ids sanitized to the same wire name — e.g. `demo:greet` and `demo_greet`. The first registration keeps the name; the later tool is hidden from `tools/list`.
ctx.agent.registerTool({ id: 'demo_greet', description: '…', handler }) // hidden: same wire name
20
+
```
21
+
22
+
## Fix
23
+
24
+
Rename one of the two ids so they sanitize to distinct wire names. Namespaced ids (`devframes:plugin:<slug>:<fn>`, `devframe:<area>:<fn>`) collide only when they differ solely in separator characters.
25
+
26
+
## Source
27
+
28
+
-[`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/adapters/mcp/build-server.ts) — the `tools/list` handler reports this once per hidden tool when deduplicating wire names.
The built-in `devframe_state_read` MCP tool was called with a `key` that is not among the shared-state keys the host publishes (or that the `exposeSharedState` filter allows). The error crosses the MCP boundary as structured JSON, so the calling agent can self-correct.
14
+
15
+
## Example
16
+
17
+
```ts
18
+
// The host publishes only `my-plugin:counter`; an agent calls:
Call the `devframe_state_read` tool without arguments to list the available keys, then retry with one of them.
25
+
26
+
## Source
27
+
28
+
-[`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/adapters/mcp/build-server.ts) — `readStateResult()` throws this when the requested key is absent from the filtered key list.
The `devframe connect` gateway tool `devframe_connect_call-tool` was invoked without a numeric `port` or a string `tool` name — the two fields that identify which instance to dial and which of its tools to call. The error crosses the MCP boundary as structured JSON, so the calling agent can self-correct.
Call `devframe_connect_list-instances` first — its result carries each instance's `port` and tool names — then retry with both fields.
24
+
25
+
## Source
26
+
27
+
-[`packages/devframe/src/cli/connect.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/connect.ts) — `call()` throws this when the gateway arguments fail validation.
The `devframe connect` gateway tool `devframe_connect_call-tool` targeted a port with no live devframe instance behind it — neither the instance registry nor a direct probe of the port found one serving `__connection.json`. The instance may have stopped, restarted on a different port, or never existed. The error crosses the MCP boundary as structured JSON, so the calling agent can self-correct.
Call `devframe_connect_list-instances` for the current instance list and retry with a live port.
25
+
26
+
## Source
27
+
28
+
-[`packages/devframe/src/cli/connect.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/connect.ts) — `call()` throws this when neither the registry nor the port probe finds an instance.
0 commit comments