@mcp-layer/schema 1.0.3 → 2.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mcp-layer/schema
2
2
 
3
- `@mcp-layer/schema` extracts MCP server definitions (tools, resources, prompts, templates, and MCP Apps metadata) and normalizes them into a unified Zod-backed schema. The output is designed for downstream generators that need a single contract to build CLIs, REST endpoints, UI renderers, or additional MCP layers without re-parsing the MCP protocol surface.
3
+ `@mcp-layer/schema` extracts MCP server definitions (tools, resources, prompts, templates, and MCP Apps metadata) and normalizes them into a unified schema backed by Zod 4. The output is designed for downstream generators that need a single contract to build CLIs, REST endpoints, UI renderers, or additional MCP layers without re-parsing the MCP protocol surface.
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -11,6 +11,7 @@
11
11
  - [MCP Apps support](#mcp-apps-support)
12
12
  - [Generator guidance](#generator-guidance)
13
13
  - [Responsibilities and lifecycle](#responsibilities-and-lifecycle)
14
+ - [API Reference](#api-reference)
14
15
  - [Error handling](#error-handling)
15
16
  - [JSON Schema vs Zod](#json-schema-vs-zod)
16
17
  - [Runtime Error Reference](#runtime-error-reference)
@@ -30,7 +31,7 @@ yarn add @mcp-layer/schema
30
31
  ```js
31
32
  import { load } from '@mcp-layer/config';
32
33
  import { connect } from '@mcp-layer/connect';
33
- import { extract } from '@mcp-layer/schema';
34
+ import { composeCatalog, extract } from '@mcp-layer/schema';
34
35
 
35
36
  const config = await load(undefined, process.cwd());
36
37
  const session = await connect(config, 'demo');
@@ -39,15 +40,37 @@ const output = await extract(session);
39
40
  console.log(output.server.info);
40
41
  console.log(output.items[0]);
41
42
  await session.close();
43
+
44
+ const catalog = composeCatalog({
45
+ server: {
46
+ info: { name: 'demo', version: '1.0.0' },
47
+ capabilities: { tools: {} },
48
+ instructions: 'Use the local catalog.',
49
+ },
50
+ tools: [{
51
+ name: 'echo',
52
+ description: 'Echo text.',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ text: { type: 'string' },
57
+ },
58
+ required: ['text'],
59
+ },
60
+ }],
61
+ });
62
+
63
+ console.log(catalog.items[0]);
42
64
  ```
43
65
 
44
66
  ## What this package does
45
67
 
46
- 1) Reads the MCP server capabilities and metadata from the live client connection.
68
+ 1) Reads the MCP server capabilities and metadata from the live client connection with `extract(session)`.
47
69
  2) Calls MCP list endpoints (`tools/list`, `resources/list`, `resources/templates/list`, `prompts/list`) using pagination.
48
- 3) Normalizes everything into a unified, type-discriminated schema so generators can consume a single list.
49
- 4) Wraps tool input/output JSON Schemas into Zod validators backed by Ajv, while preserving the original JSON Schema.
50
- 5) Converts prompt arguments into a lightweight JSON Schema so prompts and tools share the same `detail.input` shape.
70
+ 3) Composes the same normalized catalog shape from raw definitions with `composeCatalog(...)` when a live session is not available yet.
71
+ 4) Normalizes everything into a unified, type-discriminated schema so generators can consume a single list.
72
+ 5) Wraps tool input/output JSON Schemas into Zod validators backed by Ajv, while preserving the original JSON Schema.
73
+ 6) Converts prompt arguments into a lightweight JSON Schema so prompts and tools share the same `detail.input` shape.
51
74
 
52
75
  ## Output shape (authoritative)
53
76
 
@@ -82,7 +105,7 @@ Type discriminator:
82
105
  ```js
83
106
  detail: {
84
107
  input: {
85
- schema, // Zod schema (Ajv-backed)
108
+ schema, // Zod 4 schema (Ajv-backed)
86
109
  json, // original JSON Schema
87
110
  error // optional Ajv compile error message
88
111
  },
@@ -143,10 +166,30 @@ Suggested uses for the unified schema:
143
166
 
144
167
  ## Responsibilities and lifecycle
145
168
 
146
- - This package does not open or close connections. It expects a live `Session` from `@mcp-layer/connect` or `@mcp-layer/attach`.
169
+ - `extract(session)` does not open or close connections. It expects a live `Session` from `@mcp-layer/connect` or `@mcp-layer/attach`.
147
170
  - You are responsible for calling `session.close()` after extraction.
171
+ - `composeCatalog(...)` is pure and does not require a connection.
148
172
  - If the server doesn't advertise a capability (tools/resources/prompts), extraction skips that surface.
149
173
 
174
+ ## API Reference
175
+
176
+ ### `composeCatalog(input = {})`
177
+
178
+ Build a catalog from MCP data that is already available. This function does not connect to a server.
179
+
180
+ - **Input:** an optional `server` object with `info`, `capabilities`, and `instructions`, plus optional `tools`, `resources`, `templates`, and `prompts` arrays.
181
+ - **Result:** `{ server, items }`. Missing values become `undefined` or add no items. Tool input and output schemas use Zod 4 and Ajv.
182
+ - **Errors:** Ajv compile errors appear in the item's `error` field. This function does not make MCP calls.
183
+
184
+ ### `extract(session)`
185
+
186
+ Read a catalog from an active `Session`.
187
+
188
+ - **Input:** a connected `Session` from `@mcp-layer/connect` or `@mcp-layer/attach`.
189
+ - **Result:** `Promise<{ server, items }>` with the same shape as `composeCatalog`.
190
+ - **Errors:** if `session.client` is missing, this function throws `LayerError` with reference `error-49ae61`. It passes MCP call errors to the caller.
191
+ - **Lifecycle:** this function does not close the session.
192
+
150
193
  ## Error handling
151
194
 
152
195
  - Schema compilation uses Ajv under the hood. If a tool exposes an invalid JSON Schema, extraction still succeeds but `detail.input.error` or `detail.output.error` will be populated for that item.
@@ -155,7 +198,7 @@ Suggested uses for the unified schema:
155
198
 
156
199
  ## JSON Schema vs Zod
157
200
 
158
- Zod validators are produced to allow direct runtime validation, but the original JSON Schema is preserved in `detail.input.json` and `detail.output.json` so generators can emit OpenAPI, JSON Schema, or other schema formats without losing fidelity.
201
+ The `schema` field contains a Zod 4 validator backed by Ajv. Call `.parse()` or `.safeParse()` to validate data. The original JSON Schema stays in `detail.input.json` and `detail.output.json` for generators that need OpenAPI, JSON Schema, or another schema format.
159
202
 
160
203
  ## Runtime Error Reference
161
204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-layer/schema",
3
- "version": "1.0.3",
3
+ "version": "2.0.0",
4
4
  "description": "Extract and normalize MCP schemas into a unified Zod-based format.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,12 +34,12 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "ajv": "^8.17.1",
38
- "zod": "^3.25.76",
37
+ "ajv": "^8.20.0",
38
+ "zod": "^4.6.7",
39
39
  "@mcp-layer/error": "0.2.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@mcp-layer/attach": "1.1.2",
42
+ "@mcp-layer/attach": "1.2.0",
43
43
  "@mcp-layer/config": "1.0.2",
44
44
  "@mcp-layer/connect": "1.2.1",
45
45
  "@mcp-layer/test-server": "1.0.4"
package/src/index.js CHANGED
@@ -416,6 +416,45 @@ function normalize(data) {
416
416
  return list;
417
417
  }
418
418
 
419
+ /**
420
+ * Derive advertised capabilities from the provided raw MCP definition sets.
421
+ * @param {{ tools?: Array<Record<string, unknown>>, resources?: Array<Record<string, unknown>>, templates?: Array<Record<string, unknown>>, prompts?: Array<Record<string, unknown>> }} data - Raw MCP definition sets.
422
+ * @returns {Record<string, unknown> | undefined}
423
+ */
424
+ function capabilities(data) {
425
+ const caps = {};
426
+
427
+ if (Array.isArray(data.tools)) caps.tools = {};
428
+ if (Array.isArray(data.resources) || Array.isArray(data.templates)) caps.resources = {};
429
+ if (Array.isArray(data.prompts)) caps.prompts = {};
430
+
431
+ return Object.keys(caps).length > 0 ? caps : undefined;
432
+ }
433
+
434
+ /**
435
+ * Compose a unified catalog from raw MCP definitions without a live session.
436
+ * This lets adapters bootstrap routes from already-known tool/resource shapes
437
+ * while keeping the output identical to `extract(session)`.
438
+ *
439
+ * @param {{ server?: { info?: Record<string, unknown>, capabilities?: Record<string, unknown>, instructions?: string }, tools?: Array<Record<string, unknown>>, resources?: Array<Record<string, unknown>>, templates?: Array<Record<string, unknown>>, prompts?: Array<Record<string, unknown>> }} input - Raw server metadata and MCP definitions.
440
+ * @returns {{ server: { info: Record<string, unknown> | undefined, capabilities: Record<string, unknown> | undefined, instructions: string | undefined }, items: Array<Record<string, unknown>> }}
441
+ */
442
+ export function composeCatalog(input = {}) {
443
+ const server = isrecord(input.server) ? input.server : {};
444
+ const info = isrecord(server.info) ? server.info : undefined;
445
+ const caps = isrecord(server.capabilities) ? server.capabilities : capabilities(input);
446
+ const instructions = typeof server.instructions === 'string' ? server.instructions : undefined;
447
+
448
+ return {
449
+ server: {
450
+ info,
451
+ capabilities: caps,
452
+ instructions,
453
+ },
454
+ items: normalize(input),
455
+ };
456
+ }
457
+
419
458
  /**
420
459
  * Extract MCP tool/resource/prompt schemas into a unified Zod-backed format.
421
460
  * @param {import('@mcp-layer/session').Session} link - Active session with a connected MCP server.
@@ -451,14 +490,12 @@ export async function extract(link) {
451
490
 
452
491
  if (caps && caps.prompts) data.prompts = await prompts(client);
453
492
 
454
- const items = normalize(data);
455
-
456
- return {
493
+ return composeCatalog({
457
494
  server: {
458
495
  info,
459
496
  capabilities: caps,
460
- instructions
497
+ instructions,
461
498
  },
462
- items
463
- };
499
+ ...data,
500
+ });
464
501
  }
package/types/index.d.ts CHANGED
@@ -1,3 +1,29 @@
1
+ /**
2
+ * Compose a unified catalog from raw MCP definitions without a live session.
3
+ * This lets adapters bootstrap routes from already-known tool/resource shapes
4
+ * while keeping the output identical to `extract(session)`.
5
+ *
6
+ * @param {{ server?: { info?: Record<string, unknown>, capabilities?: Record<string, unknown>, instructions?: string }, tools?: Array<Record<string, unknown>>, resources?: Array<Record<string, unknown>>, templates?: Array<Record<string, unknown>>, prompts?: Array<Record<string, unknown>> }} input - Raw server metadata and MCP definitions.
7
+ * @returns {{ server: { info: Record<string, unknown> | undefined, capabilities: Record<string, unknown> | undefined, instructions: string | undefined }, items: Array<Record<string, unknown>> }}
8
+ */
9
+ export function composeCatalog(input?: {
10
+ server?: {
11
+ info?: Record<string, unknown>;
12
+ capabilities?: Record<string, unknown>;
13
+ instructions?: string;
14
+ };
15
+ tools?: Array<Record<string, unknown>>;
16
+ resources?: Array<Record<string, unknown>>;
17
+ templates?: Array<Record<string, unknown>>;
18
+ prompts?: Array<Record<string, unknown>>;
19
+ }): {
20
+ server: {
21
+ info: Record<string, unknown> | undefined;
22
+ capabilities: Record<string, unknown> | undefined;
23
+ instructions: string | undefined;
24
+ };
25
+ items: Array<Record<string, unknown>>;
26
+ };
1
27
  /**
2
28
  * Extract MCP tool/resource/prompt schemas into a unified Zod-backed format.
3
29
  * @param {import('@mcp-layer/session').Session} link - Active session with a connected MCP server.