@adia-ai/mcp 0.8.38 → 0.8.39

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/CHANGELOG.md CHANGED
@@ -9,6 +9,14 @@
9
9
  > entire changelog was still `[Unreleased]` at fold time (minted at ADR-0048 P4,
10
10
  > never released under its own name) and is merged into the section below.
11
11
 
12
+ ## [0.8.39] — 2026-08-15
13
+
14
+ ### Added
15
+ - **`get_catalog_ladder` on the `protocol` server (ADR-0050, gh#1291 phase 3) — 4 → 5 tools there (all-server total 43: gen-ui 31 · protocol 5 · factory 7).** ADR-0050's serving map gives the protocol server L0+L1; phase 1 held it at four tools because an L0-only enumerate tool duplicates `get_registry_map`. With L1 populated (44 curated widgets) the tool earns its place: an L1 entry carries its A2UI `template`, so a chat agent can enumerate a widget and emit it with no generation system and no corpus. No arguments returns the ladder summary — all five rungs with unit/composesTier/status/count plus a `servedHere` flag and the serving map in prose, so a client learns where L2–L4 live; `tier` (a two-value enum, `L0`|`L1`, so an unserved rung is refused at the schema rather than answered emptily) lists that rung's entry names; `tier` + `name` returns one entry's full record. **Isolation is unchanged by construction** — the tier index is data inside the zero-dependency `@adia-ai/a2ui` (`./catalog/tiers`), so the P4 module-graph walk still proves zero reach into `@adia-ai/gen-ui`/`@adia-ai/llm`/`@adia-ai/agent`. Zero tool-name overlap across the three servers (the factory smoke's ADR-0051 §4 assertion). **Purely additive** — no existing tool's input or output shape changed, so no versioning event; `protocol_status` deliberately did not grow catalog counters, which would have been an output-contract change for cosmetic value. gen-ui's `get_catalog_tiers` (all five rungs, for retrieval) is unchanged.
16
+
17
+ ### Maintenance
18
+ - **`bin/` touched in this release window** (1 file(s), e.g. `bin/adia-mcp`) — carried by the entries above.
19
+
12
20
  ## [0.8.38] — 2026-08-15
13
21
 
14
22
  ### Added
package/README.md CHANGED
@@ -152,13 +152,13 @@ Wraps [`@adia-ai/a2ui`](../a2ui) and nothing else. `@adia-ai/gen-ui` and
152
152
  `@adia-ai/llm` are deliberate **non**-dependencies of this surface — see
153
153
  [Why a separate server](#why-a-separate-server-not-just-a-shared-one).
154
154
 
155
- Four tools, no API key, no `.env`, **stdio only** (no HTTP mode — express and
155
+ Five tools, no API key, no `.env`, **stdio only** (no HTTP mode — express and
156
156
  session-sweep belong to `gen-ui`): `validate_document` · `get_registry_map` ·
157
- `get_wiring_registry` · `protocol_status`.
157
+ `get_wiring_registry` · `get_catalog_ladder` · `protocol_status`.
158
158
 
159
159
  ```bash
160
160
  node packages/gen-ui/mcp/protocol/server.js # stdio only
161
- npm run mcp:protocol:smoke # boot + exercise all 4 tools + isolation proof
161
+ npm run mcp:protocol:smoke # boot + exercise all 5 tools + isolation proof
162
162
  ```
163
163
 
164
164
  ### Tool name disambiguation
@@ -265,7 +265,7 @@ npm run build:protocol-mcp-server # protocol: server.ts + tools/*.ts -> .js (e
265
265
  npm run build:factory-mcp-server # factory: server.ts + tools/*.ts -> .js (esbuild)
266
266
  npm run build:factory-mcp-assets # factory: re-vendor scripts + re-derive doctrine resources from the plugin
267
267
  npm run mcp:smoke # gen-ui: boot over stdio, exercise all 31 tools
268
- npm run mcp:protocol:smoke # protocol: boot over stdio, exercise all 4 tools + isolation proof
268
+ npm run mcp:protocol:smoke # protocol: boot over stdio, exercise all 5 tools + isolation proof
269
269
  npm run mcp:factory:smoke # factory: asset freshness + boot + tools + zero-overlap/parity/isolation proofs
270
270
  npm run build:mcp-tools-md # regenerate TOOLS.md (all three servers, sectioned) from the live tool lists
271
271
  ```
package/TOOLS.md CHANGED
@@ -509,9 +509,9 @@ The A2UI **protocol** MCP server (`adia-mcp protocol`, `server.js`) exposes tool
509
509
 
510
510
  Looking for generation, retrieval, or the training corpus? That is a different server, `adia-mcp gen-ui` — see its section above in this same file.
511
511
 
512
- These four tool names are DISTINCT from the gen-ui server's own tool surface (gh#1248 renamed them off their original same-named forms before this server ever published) but each still serves a narrower, protocol-only counterpart — `validate_document` (schema + runtime registry, no catalog; gen-ui's `validate_schema`) and `get_registry_map` (type → tag, no descriptions or prop schemas; gen-ui's `get_component_map`). `get_wiring_registry` likewise reports the live wiring registry rather than the authoring knowledge base (gen-ui's `get_wiring_catalog`). For installation + configuration see [`README.md`](./README.md).
512
+ Every tool name here is DISTINCT from the gen-ui and factory servers' surfaces (gh#1248 renamed the original four off their same-named forms before this server ever published) but each still serves a narrower, protocol-only counterpart — `validate_document` (schema + runtime registry, no catalog; gen-ui's `validate_schema`), `get_registry_map` (type → tag, no descriptions or prop schemas; gen-ui's `get_component_map`), and `get_catalog_ladder` (the L0+L1 rungs ADR-0050's serving map puts on this server; gen-ui's `get_catalog_tiers` serves the whole ladder for retrieval). `get_wiring_registry` likewise reports the live wiring registry rather than the authoring knowledge base (gen-ui's `get_wiring_catalog`). For installation + configuration see [`README.md`](./README.md).
513
513
 
514
- This server exposes **4 tools**.
514
+ This server exposes **5 tools**.
515
515
 
516
516
  ### Tool index
517
517
 
@@ -519,6 +519,7 @@ This server exposes **4 tools**.
519
519
  |---|---|
520
520
  | **Validation** | [`validate_document`](#protocol-validate_document) |
521
521
  | **Registry introspection** | [`get_registry_map`](#protocol-get_registry_map), [`get_wiring_registry`](#protocol-get_wiring_registry) |
522
+ | **Catalog contract** | [`get_catalog_ladder`](#protocol-get_catalog_ladder) |
522
523
  | **Status** | [`protocol_status`](#protocol-protocol_status) |
523
524
 
524
525
  #### Validation
@@ -557,6 +558,23 @@ Read live from the runtime's wiringRegistry, so it cannot drift from what the re
557
558
 
558
559
  _No arguments._
559
560
 
561
+ #### Catalog contract
562
+
563
+ <a id="protocol-get_catalog_ladder"></a>
564
+
565
+ ##### `get_catalog_ladder`
566
+
567
+ Enumerate the AdiaUI catalog contract's served rungs: L0 primitives (component vocabulary) and L1 widgets (named, versioned, chat-scale functional units — a Row of stat cards, a sign-in card, a tool-call accordion). An L1 entry carries its A2UI `template`, so this is the tool to call before generating a document by hand: find the widget, take its template, fill in the copy.
568
+
569
+ Called with no arguments it returns the ladder summary — every rung with its entry unit, the rung it composes, populated/reserved status, and entry count. Pass `tier` ("L0" or "L1") for that rung's entry names; pass `tier` + `name` for one entry's full record (`composes`, `template`, `keywords`, `domain`, provenance).
570
+
571
+ Scope, per ADR-0050's serving map: this server serves **L0 + L1** — the rungs a chat agent enumerates to emit A2UI. L2 layouts, L3 shells, and L4 flows are developer-facing and belong to the factory surface; `adia-mcp gen-ui`'s `get_catalog_tiers` serves the whole ladder for retrieval. The ladder's one law is that every tier-N entry is a declared composition of tier-(N-1) entries, so `composes` is the edge you follow down a rung — an L1 entry's `composes` names L0 types you can validate with `validate_document`.
572
+
573
+ | Param | Type | Required | Default | Description |
574
+ |---|---|---|---|---|
575
+ | `tier` | `L0` \| `L1` | no | — | List one served rung's entry names instead of the ladder summary |
576
+ | `name` | string | no | — | With `tier` (required alongside it), return this single entry's full record |
577
+
560
578
  #### Status
561
579
 
562
580
  <a id="protocol-protocol_status"></a>
@@ -567,7 +585,7 @@ Returns operational status of this A2UI protocol MCP server: transport and proto
567
585
 
568
586
  _No arguments._
569
587
 
570
- Tools on this server (4): `get_registry_map`, `get_wiring_registry`, `protocol_status`, `validate_document`.
588
+ Tools on this server (5): `get_catalog_ladder`, `get_registry_map`, `get_wiring_registry`, `protocol_status`, `validate_document`.
571
589
 
572
590
  ---
573
591
 
package/bin/adia-mcp CHANGED
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * adia-mcp gen-ui — the generation server (31 tools: compose, corpus,
12
12
  * retrieval, feedback/eval loop)
13
- * adia-mcp protocol — the A2UI protocol server (4 tools: validate + registry
13
+ * adia-mcp protocol — the A2UI protocol server (5 tools: validate + registry
14
14
  * introspection; no generation system, no model client)
15
15
  * adia-mcp factory — the adia-factory server (7 tools: orient, scaffold,
16
16
  * audit, surface QA — the factory plugin's gated
@@ -54,7 +54,7 @@ const SERVERS = {
54
54
  },
55
55
  protocol: {
56
56
  path: join(__dirname, '..', 'protocol', 'server.js'),
57
- summary: '4-tool protocol server (validate + registry introspection; no gen-ui, no llm)',
57
+ summary: '5-tool protocol server (validate + registry introspection + L0/L1 catalog rungs; no gen-ui, no llm)',
58
58
  },
59
59
  factory: {
60
60
  path: join(__dirname, '..', 'factory', 'server.js'),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adia-ai/mcp",
3
- "version": "0.8.38",
4
- "description": "AdiaUI's three MCP servers, one npm package (gh#1240, ADR-0051). `adia-mcp gen-ui` — the 30-tool generation server (compose engine, corpus, retrieval, feedback/eval loop). `adia-mcp protocol` — the 4-tool A2UI protocol server (validate + registry introspection, no generation system, no model client). `adia-mcp factory` — the 7-tool adia-factory server (orient, scaffold, audit, surface QA for building adia-ui apps from any MCP harness; no generation system, no model client). ADR-0048 §3's distinct-server decision is unchanged; only the distribution unified.",
3
+ "version": "0.8.39",
4
+ "description": "AdiaUI's three MCP servers, one npm package (gh#1240, ADR-0051). `adia-mcp gen-ui` — the 31-tool generation server (compose engine, corpus, retrieval, feedback/eval loop). `adia-mcp protocol` — the 5-tool A2UI protocol server (validate + registry introspection + the L0/L1 catalog-contract rungs, no generation system, no model client). `adia-mcp factory` — the 7-tool adia-factory server (orient, scaffold, audit, surface QA for building adia-ui apps from any MCP harness; no generation system, no model client). ADR-0048 §3's distinct-server decision is unchanged; only the distribution unified.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "adia-mcp": "./bin/adia-mcp"
@@ -1,8 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
 
3
3
  /**
4
- * Builds the A2UI protocol MCP server (4 tools: validate_document,
5
- * get_registry_map, get_wiring_registry, protocol_status) with no transport
4
+ * Builds the A2UI protocol MCP server (5 tools: validate_document,
5
+ * get_registry_map, get_wiring_registry, get_catalog_ladder, protocol_status)
6
+ * with no transport
6
7
  * attached. Exported for tests and for `scripts/build/generate-mcp-tools-md.mjs`;
7
8
  * the module's own top-level `isEntryPoint()` guard is what actually starts a
8
9
  * stdio transport when this file is run directly (`node server.js`), not when
@@ -16,7 +16,7 @@ async function main() {
16
16
  const server = createServer();
17
17
  const transport = new StdioServerTransport();
18
18
  await server.connect(transport);
19
- console.error("[a2ui-protocol-mcp] stdio transport ready (4 protocol tools)");
19
+ console.error("[a2ui-protocol-mcp] stdio transport ready (5 protocol tools)");
20
20
  }
21
21
  function isEntryPoint() {
22
22
  if (typeof process === "undefined" || !process.argv[1]) return false;
@@ -10,6 +10,11 @@ This is the registry view \u2014 the authoritative answer to "what types exist a
10
10
  const GET_WIRING_REGISTRY_DESCRIPTION = `Get the A2UI wiring registry: controller types, action-handler names, and data-URI resolver schemes the runtime can resolve.
11
11
 
12
12
  Read live from the runtime's wiringRegistry, so it cannot drift from what the renderer will actually accept. The richer authoring knowledge base (UI event payloads, refresh strategies, value sources, association types) is generation-side \u2014 see gen-ui-mcp's \`get_wiring_catalog\` tool instead.`;
13
+ const GET_CATALOG_LADDER_DESCRIPTION = `Enumerate the AdiaUI catalog contract's served rungs: L0 primitives (component vocabulary) and L1 widgets (named, versioned, chat-scale functional units \u2014 a Row of stat cards, a sign-in card, a tool-call accordion). An L1 entry carries its A2UI \`template\`, so this is the tool to call before generating a document by hand: find the widget, take its template, fill in the copy.
14
+
15
+ Called with no arguments it returns the ladder summary \u2014 every rung with its entry unit, the rung it composes, populated/reserved status, and entry count. Pass \`tier\` ("L0" or "L1") for that rung's entry names; pass \`tier\` + \`name\` for one entry's full record (\`composes\`, \`template\`, \`keywords\`, \`domain\`, provenance).
16
+
17
+ Scope, per ADR-0050's serving map: this server serves **L0 + L1** \u2014 the rungs a chat agent enumerates to emit A2UI. L2 layouts, L3 shells, and L4 flows are developer-facing and belong to the factory surface; \`adia-mcp gen-ui\`'s \`get_catalog_tiers\` serves the whole ladder for retrieval. The ladder's one law is that every tier-N entry is a declared composition of tier-(N-1) entries, so \`composes\` is the edge you follow down a rung \u2014 an L1 entry's \`composes\` names L0 types you can validate with \`validate_document\`.`;
13
18
  const PROTOCOL_STATUS_DESCRIPTION = `Returns operational status of this A2UI protocol MCP server: transport and protocol-registry stats. Reports on the protocol server only \u2014 gen-ui-mcp's \`server_status\` tool reports its own corpus-side status separately.`;
14
19
  function buildRegistryView() {
15
20
  const byTag = /* @__PURE__ */ new Map();
@@ -25,6 +30,14 @@ function buildRegistryView() {
25
30
  })).sort((a, b) => a.type.localeCompare(b.type));
26
31
  return { totalTypes: registry.size, totalTags: byTag.size, entries };
27
32
  }
33
+ const SERVED_TIERS = ["L0", "L1"];
34
+ let _tierIndex = null;
35
+ async function loadTierIndex() {
36
+ if (_tierIndex) return _tierIndex;
37
+ const mod = await import("@adia-ai/a2ui/catalog/tiers", { with: { type: "json" } });
38
+ _tierIndex = mod.default ?? mod;
39
+ return _tierIndex;
40
+ }
28
41
  function buildWiringView() {
29
42
  return {
30
43
  controllers: [...wiringRegistry.controllers.keys()].sort(),
@@ -68,6 +81,116 @@ function registerProtocolTools(server) {
68
81
  return { content: [{ type: "text", text: JSON.stringify(buildWiringView(), null, 2) }] };
69
82
  }
70
83
  );
84
+ server.tool(
85
+ "get_catalog_ladder",
86
+ GET_CATALOG_LADDER_DESCRIPTION,
87
+ {
88
+ tier: z.enum(["L0", "L1"]).optional().describe("List one served rung's entry names instead of the ladder summary"),
89
+ name: z.string().optional().describe("With `tier` (required alongside it), return this single entry's full record")
90
+ },
91
+ async ({ tier, name }) => {
92
+ if (name && !tier) {
93
+ return {
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `\`name\` needs a \`tier\`: entry names are unique only within a rung. Retry with { tier: "L1", name: "${name}" } (or "L0"), or omit \`name\` for the ladder summary.`
98
+ }
99
+ ],
100
+ isError: true
101
+ };
102
+ }
103
+ let index;
104
+ try {
105
+ index = await loadTierIndex();
106
+ } catch (err) {
107
+ const e = err instanceof Error ? err : new Error(String(err));
108
+ return {
109
+ content: [{ type: "text", text: `Catalog tier index unavailable: ${e.message}` }],
110
+ isError: true
111
+ };
112
+ }
113
+ if (!tier) {
114
+ const ladder = Object.fromEntries(
115
+ Object.entries(index.tiers).map(([id, t]) => [
116
+ id,
117
+ {
118
+ unit: t.unit,
119
+ composesTier: t.composesTier,
120
+ status: t.status,
121
+ count: t.count,
122
+ servedHere: SERVED_TIERS.includes(id)
123
+ }
124
+ ])
125
+ );
126
+ return {
127
+ content: [
128
+ {
129
+ type: "text",
130
+ text: JSON.stringify(
131
+ {
132
+ catalogId: index.catalogId,
133
+ servedTiers: [...SERVED_TIERS],
134
+ servingMap: "protocol server: L0+L1 (this tool) \xB7 gen-ui server: all tiers, for retrieval (get_catalog_tiers) \xB7 factory server: L2-L4, developer-facing",
135
+ ladder
136
+ },
137
+ null,
138
+ 2
139
+ )
140
+ }
141
+ ]
142
+ };
143
+ }
144
+ const rung = index.tiers[tier];
145
+ if (!rung) {
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: `Tier "${tier}" is absent from the catalog tier index (${Object.keys(index.tiers).join(", ")} present) \u2014 the index may predate it.`
151
+ }
152
+ ],
153
+ isError: true
154
+ };
155
+ }
156
+ if (name) {
157
+ const entry = rung.entries[name];
158
+ if (!entry) {
159
+ return {
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: `No ${tier} entry named "${name}". ${rung.count} entries in this tier \u2014 call this tool with just \`tier\` for the names.`
164
+ }
165
+ ],
166
+ isError: true
167
+ };
168
+ }
169
+ return {
170
+ content: [{ type: "text", text: JSON.stringify({ tier, name, entry }, null, 2) }]
171
+ };
172
+ }
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: JSON.stringify(
178
+ {
179
+ tier,
180
+ unit: rung.unit,
181
+ composesTier: rung.composesTier,
182
+ status: rung.status,
183
+ count: rung.count,
184
+ entries: Object.keys(rung.entries).sort()
185
+ },
186
+ null,
187
+ 2
188
+ )
189
+ }
190
+ ]
191
+ };
192
+ }
193
+ );
71
194
  server.tool(
72
195
  "protocol_status",
73
196
  PROTOCOL_STATUS_DESCRIPTION,