@mcp-layer/schema 0.0.0 → 1.0.1

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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/package.json +13 -9
  3. package/src/index.js +56 -32
package/README.md CHANGED
@@ -138,6 +138,7 @@ Suggested uses for the unified schema:
138
138
 
139
139
  - 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.
140
140
  - Servers that return empty lists produce an empty `items` array (not an error).
141
+ - Servers that support resources but not resource templates are treated as having zero templates.
141
142
 
142
143
  ## JSON Schema vs Zod
143
144
 
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@mcp-layer/schema",
3
- "version": "0.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Extract and normalize MCP schemas into a unified Zod-based format.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/3rd-Eden/mcp-layer"
8
+ },
5
9
  "type": "module",
6
10
  "keywords": [
7
11
  "mcp",
@@ -11,9 +15,6 @@
11
15
  "resources",
12
16
  "prompts"
13
17
  ],
14
- "scripts": {
15
- "test": "node --test --experimental-test-coverage ./test/**/*.js"
16
- },
17
18
  "main": "src/index.js",
18
19
  "exports": {
19
20
  ".": {
@@ -34,9 +35,12 @@
34
35
  "zod": "^3.25.76"
35
36
  },
36
37
  "devDependencies": {
37
- "@mcp-layer/attach": "workspace:*",
38
- "@mcp-layer/config": "workspace:*",
39
- "@mcp-layer/connect": "workspace:*",
40
- "@mcp-layer/test-server": "workspace:*"
38
+ "@mcp-layer/attach": "1.1.0",
39
+ "@mcp-layer/config": "1.0.0",
40
+ "@mcp-layer/connect": "1.0.0",
41
+ "@mcp-layer/test-server": "1.0.0"
42
+ },
43
+ "scripts": {
44
+ "test": "node --test --experimental-test-coverage ./test/**/*.js"
41
45
  }
42
- }
46
+ }
package/src/index.js CHANGED
@@ -5,24 +5,41 @@ const ajv = new Ajv({ allErrors: true, strict: false });
5
5
 
6
6
  /**
7
7
  * Test if a value is a plain object.
8
- * @param {unknown} value
8
+ * @param {unknown} value - Value to test for plain object shape.
9
9
  * @returns {value is Record<string, unknown>}
10
10
  */
11
11
  function isrecord(value) {
12
12
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
13
13
  }
14
14
 
15
+ /**
16
+ * Determine whether an MCP error indicates a missing method.
17
+ * @param {unknown} error - Error value to inspect.
18
+ * @returns {boolean}
19
+ */
20
+ function ismissing(error) {
21
+ if (!error || typeof error !== 'object') {
22
+ return false;
23
+ }
24
+ const code = 'code' in error ? error.code : undefined;
25
+ if (code === -32601) {
26
+ return true;
27
+ }
28
+ const message = 'message' in error ? String(error.message) : '';
29
+ return message.includes('Method') && message.includes('not found');
30
+ }
31
+
15
32
  /**
16
33
  * Build a Zod refinement function backed by Ajv.
17
- * @param {import('ajv').Ajv} validator
18
- * @param {import('ajv').ValidateFunction} check
34
+ * @param {import('ajv').Ajv} validator - Ajv instance used to format errors.
35
+ * @param {import('ajv').ValidateFunction} check - Compiled Ajv validation function.
19
36
  * @returns {(value: unknown, ctx: import('zod').RefinementCtx) => void}
20
37
  */
21
38
  function refiners(validator, check) {
22
39
  /**
23
40
  * Validate the value and emit Zod issues for Ajv failures.
24
- * @param {unknown} value
25
- * @param {import('zod').RefinementCtx} ctx
41
+ * @param {unknown} value - Value to validate.
42
+ * @param {import('zod').RefinementCtx} ctx - Zod refinement context for reporting issues.
26
43
  * @returns {void}
27
44
  */
28
45
  function apply(value, ctx) {
@@ -39,7 +56,7 @@ function refiners(validator, check) {
39
56
 
40
57
  /**
41
58
  * Wrap a JSON Schema into a Zod schema that delegates validation to Ajv.
42
- * @param {unknown} json
59
+ * @param {unknown} json - JSON Schema definition to wrap.
43
60
  * @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
44
61
  */
45
62
  function wrapschema(json) {
@@ -60,8 +77,8 @@ function wrapschema(json) {
60
77
  /**
61
78
  * Read all pages for a list request until pagination completes.
62
79
  * @template T
63
- * @param {(cursor?: string) => Promise<T & { nextCursor?: string }>} call
64
- * @param {(result: T) => Array<unknown>} pull
80
+ * @param {(cursor?: string) => Promise<T & { nextCursor?: string }>} call - Function that requests a page by cursor.
81
+ * @param {(result: T) => Array<unknown>} pull - Function that extracts items from each page result.
65
82
  * @returns {Promise<Array<unknown>>}
66
83
  */
67
84
  async function page(call, pull) {
@@ -85,13 +102,13 @@ async function page(call, pull) {
85
102
 
86
103
  /**
87
104
  * Extract tool entries from a client.
88
- * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
105
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
89
106
  * @returns {Promise<Array<Record<string, unknown>>>}
90
107
  */
91
108
  async function tools(client) {
92
109
  /**
93
110
  * Fetch a page of tools.
94
- * @param {string | undefined} cursor
111
+ * @param {string | undefined} cursor - Pagination cursor from the previous page.
95
112
  * @returns {Promise<{ tools: Array<Record<string, unknown>>, nextCursor?: string }>}
96
113
  */
97
114
  async function call(cursor) {
@@ -103,7 +120,7 @@ async function tools(client) {
103
120
 
104
121
  /**
105
122
  * Pluck tool definitions from a list response.
106
- * @param {{ tools?: Array<Record<string, unknown>> }} result
123
+ * @param {{ tools?: Array<Record<string, unknown>> }} result - listTools response payload.
107
124
  * @returns {Array<Record<string, unknown>>}
108
125
  */
109
126
  function pull(result) {
@@ -115,13 +132,13 @@ async function tools(client) {
115
132
 
116
133
  /**
117
134
  * Extract resource entries from a client.
118
- * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
135
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
119
136
  * @returns {Promise<Array<Record<string, unknown>>>}
120
137
  */
121
138
  async function resources(client) {
122
139
  /**
123
140
  * Fetch a page of resources.
124
- * @param {string | undefined} cursor
141
+ * @param {string | undefined} cursor - Pagination cursor from the previous page.
125
142
  * @returns {Promise<{ resources: Array<Record<string, unknown>>, nextCursor?: string }>}
126
143
  */
127
144
  async function call(cursor) {
@@ -133,7 +150,7 @@ async function resources(client) {
133
150
 
134
151
  /**
135
152
  * Pluck resource definitions from a list response.
136
- * @param {{ resources?: Array<Record<string, unknown>> }} result
153
+ * @param {{ resources?: Array<Record<string, unknown>> }} result - listResources response payload.
137
154
  * @returns {Array<Record<string, unknown>>}
138
155
  */
139
156
  function pull(result) {
@@ -145,13 +162,13 @@ async function resources(client) {
145
162
 
146
163
  /**
147
164
  * Extract resource template entries from a client.
148
- * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
165
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
149
166
  * @returns {Promise<Array<Record<string, unknown>>>}
150
167
  */
151
168
  async function templates(client) {
152
169
  /**
153
170
  * Fetch a page of resource templates.
154
- * @param {string | undefined} cursor
171
+ * @param {string | undefined} cursor - Pagination cursor from the previous page.
155
172
  * @returns {Promise<{ resourceTemplates: Array<Record<string, unknown>>, nextCursor?: string }>}
156
173
  */
157
174
  async function call(cursor) {
@@ -163,25 +180,32 @@ async function templates(client) {
163
180
 
164
181
  /**
165
182
  * Pluck resource template definitions from a list response.
166
- * @param {{ resourceTemplates?: Array<Record<string, unknown>> }} result
183
+ * @param {{ resourceTemplates?: Array<Record<string, unknown>> }} result - listResourceTemplates response payload.
167
184
  * @returns {Array<Record<string, unknown>>}
168
185
  */
169
186
  function pull(result) {
170
187
  return Array.isArray(result.resourceTemplates) ? result.resourceTemplates : [];
171
188
  }
172
189
 
173
- return page(call, pull);
190
+ try {
191
+ return await page(call, pull);
192
+ } catch (error) {
193
+ if (ismissing(error)) {
194
+ return [];
195
+ }
196
+ throw error;
197
+ }
174
198
  }
175
199
 
176
200
  /**
177
201
  * Extract prompt entries from a client.
178
- * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
202
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
179
203
  * @returns {Promise<Array<Record<string, unknown>>>}
180
204
  */
181
205
  async function prompts(client) {
182
206
  /**
183
207
  * Fetch a page of prompts.
184
- * @param {string | undefined} cursor
208
+ * @param {string | undefined} cursor - Pagination cursor from the previous page.
185
209
  * @returns {Promise<{ prompts: Array<Record<string, unknown>>, nextCursor?: string }>}
186
210
  */
187
211
  async function call(cursor) {
@@ -193,7 +217,7 @@ async function prompts(client) {
193
217
 
194
218
  /**
195
219
  * Pluck prompt definitions from a list response.
196
- * @param {{ prompts?: Array<Record<string, unknown>> }} result
220
+ * @param {{ prompts?: Array<Record<string, unknown>> }} result - listPrompts response payload.
197
221
  * @returns {Array<Record<string, unknown>>}
198
222
  */
199
223
  function pull(result) {
@@ -205,7 +229,7 @@ async function prompts(client) {
205
229
 
206
230
  /**
207
231
  * Build shared metadata from an MCP item.
208
- * @param {Record<string, unknown>} item
232
+ * @param {Record<string, unknown>} item - MCP definition object.
209
233
  * @returns {Record<string, unknown>}
210
234
  */
211
235
  function meta(item) {
@@ -221,7 +245,7 @@ function meta(item) {
221
245
 
222
246
  /**
223
247
  * Build metadata for tools, including annotations.
224
- * @param {Record<string, unknown>} tool
248
+ * @param {Record<string, unknown>} tool - Tool definition object.
225
249
  * @returns {Record<string, unknown>}
226
250
  */
227
251
  function toolmeta(tool) {
@@ -234,7 +258,7 @@ function toolmeta(tool) {
234
258
 
235
259
  /**
236
260
  * Derive a display title with tool annotation fallback.
237
- * @param {Record<string, unknown>} tool
261
+ * @param {Record<string, unknown>} tool - Tool definition object.
238
262
  * @returns {string | undefined}
239
263
  */
240
264
  function tooltitle(tool) {
@@ -249,7 +273,7 @@ function tooltitle(tool) {
249
273
 
250
274
  /**
251
275
  * Extract MCP Apps UI metadata from an item.
252
- * @param {Record<string, unknown>} item
276
+ * @param {Record<string, unknown>} item - MCP definition object.
253
277
  * @returns {Record<string, unknown> | undefined}
254
278
  */
255
279
  function uiitem(item) {
@@ -278,7 +302,7 @@ function uiitem(item) {
278
302
 
279
303
  /**
280
304
  * Normalize a tool definition into the unified schema.
281
- * @param {Record<string, unknown>} tool
305
+ * @param {Record<string, unknown>} tool - Raw tool definition from MCP.
282
306
  * @returns {Record<string, unknown>}
283
307
  */
284
308
  function toolitem(tool) {
@@ -306,7 +330,7 @@ function toolitem(tool) {
306
330
 
307
331
  /**
308
332
  * Normalize a resource definition into the unified schema.
309
- * @param {Record<string, unknown>} resource
333
+ * @param {Record<string, unknown>} resource - Raw resource definition from MCP.
310
334
  * @returns {Record<string, unknown>}
311
335
  */
312
336
  function resourceitem(resource) {
@@ -328,7 +352,7 @@ function resourceitem(resource) {
328
352
 
329
353
  /**
330
354
  * Normalize a resource template definition into the unified schema.
331
- * @param {Record<string, unknown>} template
355
+ * @param {Record<string, unknown>} template - Raw resource template definition from MCP.
332
356
  * @returns {Record<string, unknown>}
333
357
  */
334
358
  function templateitem(template) {
@@ -347,7 +371,7 @@ function templateitem(template) {
347
371
 
348
372
  /**
349
373
  * Build a JSON schema from prompt arguments so prompts share the input schema shape.
350
- * @param {Record<string, unknown>} prompt
374
+ * @param {Record<string, unknown>} prompt - Raw prompt definition from MCP.
351
375
  * @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
352
376
  */
353
377
  function promptinput(prompt) {
@@ -385,7 +409,7 @@ function promptinput(prompt) {
385
409
 
386
410
  /**
387
411
  * Normalize a prompt definition into the unified schema.
388
- * @param {Record<string, unknown>} prompt
412
+ * @param {Record<string, unknown>} prompt - Raw prompt definition from MCP.
389
413
  * @returns {Record<string, unknown>}
390
414
  */
391
415
  function promptitem(prompt) {
@@ -404,7 +428,7 @@ function promptitem(prompt) {
404
428
 
405
429
  /**
406
430
  * Convert a set of MCP definitions into a unified item list.
407
- * @param {{ tools?: Array<Record<string, unknown>>, resources?: Array<Record<string, unknown>>, templates?: Array<Record<string, unknown>>, prompts?: Array<Record<string, unknown>> }} data
431
+ * @param {{ tools?: Array<Record<string, unknown>>, resources?: Array<Record<string, unknown>>, templates?: Array<Record<string, unknown>>, prompts?: Array<Record<string, unknown>> }} data - Aggregated MCP list responses.
408
432
  * @returns {Array<Record<string, unknown>>}
409
433
  */
410
434
  function normalize(data) {
@@ -439,7 +463,7 @@ function normalize(data) {
439
463
 
440
464
  /**
441
465
  * Extract MCP tool/resource/prompt schemas into a unified Zod-backed format.
442
- * @param {import('@mcp-layer/session').Session} link
466
+ * @param {import('@mcp-layer/session').Session} link - Active session with a connected MCP server.
443
467
  * @returns {Promise<{ server: { info: Record<string, unknown> | undefined, capabilities: Record<string, unknown> | undefined, instructions: string | undefined }, items: Array<Record<string, unknown>> }>}
444
468
  */
445
469
  export async function extract(link) {