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
Fix route-mode defects found while re-porting movie-library (#380, #381, #383)
- #380: a `mcp.servers.<id>` block for a route-generated server augments it
(env, args, targets, apps, transport: 'stdio') instead of failing
AB4304/AB4322; redeclaring entry/command/url under an explicit generated
mode is the new AB4340.
- #381: bundle the TypeScript parser (devDependency, pinned 5.9.3) so an
npm install of agent-bundle never links a `tsc` bin over the consumer's
own TypeScript; packed-tarball proof added. The emitted chunk gets an
import.meta.url-derived __filename/__dirname shim for the parser's
eager getNodeSystem().
- #383: `Agent.Result metadata` projects to `CallToolResult._meta`
(object only; a non-object fails closed with
McpProjectionError('invalid-result-metadata')).
- Generated tools advertise `outputSchema` only when `resultSchema`
describes an object, so text-only routes need no `structuredContent`.
- Document the `[<code>] <message>` error text form on the MCP wire.
Copy file name to clipboardExpand all lines: docs/diagnostics.md
+15-1Lines changed: 15 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ gate a build, a validation, or a dev rebuild.
14
14
|`AB40xx`| Plugin metadata and Skill source validation (`AB4000`/`AB4001`: name/version; `AB4002`–`AB4007`: Skill fields; `AB4008`–`AB4011` and `AB4013`: release identity, see below; `AB4012`: declared `plugin.logo` is missing, not a file, or outside the project). |
15
15
|`AB41xx`| Normalized model invariants (unknown targets, duplicate IDs and outputs). |
16
16
|`AB42xx`| Hook configuration and native hook sources. |
17
-
|`AB43xx`| MCP server and MCP App configuration. |
17
+
|`AB43xx`| MCP server and MCP App configuration (`AB4340`: a declaration for a route-generated server redeclares `entry`/`command`/`url`; see below). |
The local-entry field rules apply to it unchanged (`AB4305`, `AB4308`–`AB4312`,
299
+
`AB432x`), and it never triggers `AB4304` or `AB4322`: the route modules are
300
+
its entry.
301
+
302
+
| Code | Severity | Trigger |
303
+
| --- | --- | --- |
304
+
|`AB4340`| error | A declaration for a route-generated server sets `entry`, `command`, or `url` while `routes.servers.<id>` is `generated`. The routes already compile this server, so a second entry claim has no reading the compiler could honor. Remove the field to keep the generated server (the other fields still apply), or set the mode to `custom`, `command`, or `remote` to serve the declared entry and omit the routes. Without an explicit mode the same collision is `AB4800`. |
305
+
292
306
## Conventional host components: rules and commands (`AB4900`–`AB4906`, `AB4920`–`AB4926`)
293
307
294
308
Conventional `src/rules/*.mdc` documents compile to the Rule IR (closed
Copy file name to clipboardExpand all lines: docs/entry-conventions.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -87,6 +87,26 @@ entries carry `provenance.kind: 'conventional'` in the normalized model.
87
87
Route and package entry conventions match `.ts` and `.tsx` files exactly;
88
88
the state convention is specifically `src/state.ts`.
89
89
90
+
### Config beside a route-generated MCP server
91
+
92
+
A `mcp.servers.<server>` block whose `<server>` the route graph compiles in
93
+
`generated` mode does not redeclare the server — its entry is the route
94
+
modules — it **augments** it. This is the precedence table for one generated
95
+
server (config wins, conventions fill):
96
+
97
+
| Field | Source of truth | Config declaration |
98
+
| --- | --- | --- |
99
+
| Entry, transport (`stdio`), `cwd` (plugin root) |`src/mcp/<server>/{tools,resources,prompts}/*` and the generated stdio shell |`entry`, `command`, or `url` is `AB4340` under `routes.servers.<server>: 'generated'` and `AB4800` without an explicit mode; `transport: 'stdio'` is accepted, any other transport is `AB4308`; `cwd` is `AB4309`; `headers` is `AB4310`. |
|`args`| The content-hashed entry path | Appended after the entry path (`AB4311` shape rules). |
102
+
|`targets`| The project's selected targets | Replaces the default selection (`AB4305` shape rules). |
103
+
|`apps`|`src/mcp/<server>/apps/*` routes | Config-side Apps are compiled and registered on the generated server beside the route-declared ones (`AB432x` rules; `AB4334` checks App targets against the declared server targets). |
104
+
105
+
Provenance stays `conventional` (the first route module) because the routes
106
+
supply the entry; `inspect` shows the merged `env`, `args`, and `targets`.
107
+
Setting `routes.servers.<server>` to `custom`, `command`, or `remote` turns the
108
+
same block back into an ordinary server declaration and omits the routes.
109
+
90
110
### Generated state mounting
91
111
92
112
The compiler parses `src/state.ts` without executing it and requires one
Copy file name to clipboardExpand all lines: docs/framework-mode.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -106,6 +106,19 @@ stay `unknown`. Route-unit and CLI-dispatch tests inject fixture values through
106
106
`renderRoute(id, { context: { providers: { library } } })`; the harness never
107
107
executes provider modules on a test's behalf.
108
108
109
+
### What reaches the MCP wire
110
+
111
+
The final Agent Document of a tool route lowers to one `CallToolResult`:
112
+
113
+
| Route surface | Wire effect |
114
+
| --- | --- |
115
+
|`Agent.Text`, `Agent.Markdown`, `Agent.Context`, `Agent.Json` children | Ordered `content` text blocks (`Agent.Json` as its JSON text). |
116
+
|`Agent.Image`, `Agent.Audio`, `Agent.Resource`| Native `image`, `audio`, and `resource_link` blocks; a host without that capability fails the projection closed unless a text fallback is selected. |
117
+
|`Agent.Result value`|`structuredContent` when the value is a JSON object; a non-object value emits none and is never wrapped. |
118
+
|`Agent.Result metadata`|`CallToolResult._meta`. It must be a JSON object (snapshotted through the same wire boundary as `structuredContent`); anything else fails the projection closed with `McpProjectionError('invalid-result-metadata')`. Listing-level `_meta` still comes from static `config._meta`, so the MCP Apps convention stamps `_meta.ui.resourceUri` on both halves. |
119
+
|`Agent.Error code message`|`isError: true` plus one text block `[<code>] <message>`. The wire has no error-code field, so the code is deliberately kept in the text (the routed CLI prints the same `**[code]** message` form); choose codes that read well to the model. |
120
+
|`resultSchema`|`outputSchema` in `tools/list`**only when the schema describes an object** (`z.object`, `z.record`, a discriminated union of objects). The MCP specification requires every result of a tool that declares `outputSchema` to carry `structuredContent`, so a text-only route declares `resultSchema = z.undefined()` (or any non-object schema), advertises no `outputSchema`, and returns no `structuredContent`. An object schema keeps the SDK's fail-closed output validation on every call. |
121
+
109
122
Everything else is power-tier reference: custom/remote server modes and
110
123
collision recovery are in [Entry conventions](entry-conventions.md); accepted
111
124
static metadata, generated `.agent-bundle/routes.d.ts`, and diagnostics are in
message: `MCP server ${JSON.stringify(name)} is compiled from src/mcp/${name}/ route modules, so its declaration cannot set ${claims.join(', ')}; a config declaration for a generated server only augments it.`,
783
+
recovery: `Remove ${claims.join(', ')} to keep the generated server (env, args, targets, and apps still apply), or set routes.servers.${name} to custom, command, or remote to serve the declared entry instead of the route modules.`,
0 commit comments