@mcp-layer/schema 1.0.0 → 1.0.2
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 +47 -0
- package/package.json +7 -6
- package/src/index.js +78 -99
package/README.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
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.
|
|
4
4
|
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [What this package does](#what-this-package-does)
|
|
10
|
+
- [Output shape (authoritative)](#output-shape-authoritative)
|
|
11
|
+
- [MCP Apps support](#mcp-apps-support)
|
|
12
|
+
- [Generator guidance](#generator-guidance)
|
|
13
|
+
- [Responsibilities and lifecycle](#responsibilities-and-lifecycle)
|
|
14
|
+
- [Error handling](#error-handling)
|
|
15
|
+
- [JSON Schema vs Zod](#json-schema-vs-zod)
|
|
16
|
+
- [Runtime Error Reference](#runtime-error-reference)
|
|
17
|
+
|
|
5
18
|
## Installation
|
|
6
19
|
|
|
7
20
|
```sh
|
|
@@ -138,7 +151,41 @@ Suggested uses for the unified schema:
|
|
|
138
151
|
|
|
139
152
|
- 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
153
|
- Servers that return empty lists produce an empty `items` array (not an error).
|
|
154
|
+
- Servers that support resources but not resource templates are treated as having zero templates.
|
|
141
155
|
|
|
142
156
|
## JSON Schema vs Zod
|
|
143
157
|
|
|
144
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.
|
|
159
|
+
|
|
160
|
+
## Runtime Error Reference
|
|
161
|
+
|
|
162
|
+
This section is written for high-pressure debugging moments. Each entry maps directly to schema extraction preconditions.
|
|
163
|
+
|
|
164
|
+
<a id="error-49ae61"></a>
|
|
165
|
+
### Expected a Session instance.
|
|
166
|
+
|
|
167
|
+
Thrown from: `extract`
|
|
168
|
+
|
|
169
|
+
This happens when `extract(link)` is called without a connected `Session` object (or with an object that does not expose `.client`). The extractor relies on MCP client capabilities and list calls.
|
|
170
|
+
|
|
171
|
+
Step-by-step resolution:
|
|
172
|
+
1. Verify you are passing the `Session` returned by `connect(...)` or `attach(...)`.
|
|
173
|
+
2. Confirm session setup completed and `session.client` is available.
|
|
174
|
+
3. Avoid passing raw MCP client objects directly; wrap flows around `Session`.
|
|
175
|
+
4. Add an integration test that extracts from a real connected session.
|
|
176
|
+
|
|
177
|
+
<details>
|
|
178
|
+
<summary>Fix Example: extract schemas from a connected Session</summary>
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
const session = await connect(config, 'local-dev');
|
|
182
|
+
const catalog = await extract(session);
|
|
183
|
+
console.log(catalog.items.length);
|
|
184
|
+
await session.close();
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
</details>
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-layer/schema",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Extract and normalize MCP schemas into a unified Zod-based format.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,13 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"ajv": "^8.17.1",
|
|
35
|
-
"zod": "^3.25.76"
|
|
35
|
+
"zod": "^3.25.76",
|
|
36
|
+
"@mcp-layer/error": "0.2.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@mcp-layer/attach": "1.
|
|
39
|
-
"@mcp-layer/config": "1.0.
|
|
40
|
-
"@mcp-layer/connect": "1.0.
|
|
41
|
-
"@mcp-layer/test-server": "1.0.
|
|
39
|
+
"@mcp-layer/attach": "1.1.1",
|
|
40
|
+
"@mcp-layer/config": "1.0.1",
|
|
41
|
+
"@mcp-layer/connect": "1.0.1",
|
|
42
|
+
"@mcp-layer/test-server": "1.0.1"
|
|
42
43
|
},
|
|
43
44
|
"scripts": {
|
|
44
45
|
"test": "node --test --experimental-test-coverage ./test/**/*.js"
|
package/src/index.js
CHANGED
|
@@ -1,35 +1,47 @@
|
|
|
1
1
|
import Ajv from 'ajv';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { LayerError } from '@mcp-layer/error';
|
|
3
4
|
|
|
4
5
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Test if a value is a plain object.
|
|
8
|
-
* @param {unknown} value
|
|
9
|
+
* @param {unknown} value - Value to test for plain object shape.
|
|
9
10
|
* @returns {value is Record<string, unknown>}
|
|
10
11
|
*/
|
|
11
12
|
function isrecord(value) {
|
|
12
13
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Determine whether an MCP error indicates a missing method.
|
|
18
|
+
* @param {unknown} error - Error value to inspect.
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
function ismissing(error) {
|
|
22
|
+
if (!error || typeof error !== 'object') return false;
|
|
23
|
+
const code = 'code' in error ? error.code : undefined;
|
|
24
|
+
if (code === -32601) return true;
|
|
25
|
+
const message = 'message' in error ? String(error.message) : '';
|
|
26
|
+
return message.includes('Method') && message.includes('not found');
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
/**
|
|
16
30
|
* Build a Zod refinement function backed by Ajv.
|
|
17
|
-
* @param {import('ajv').Ajv} validator
|
|
18
|
-
* @param {import('ajv').ValidateFunction} check
|
|
31
|
+
* @param {import('ajv').Ajv} validator - Ajv instance used to format errors.
|
|
32
|
+
* @param {import('ajv').ValidateFunction} check - Compiled Ajv validation function.
|
|
19
33
|
* @returns {(value: unknown, ctx: import('zod').RefinementCtx) => void}
|
|
20
34
|
*/
|
|
21
35
|
function refiners(validator, check) {
|
|
22
36
|
/**
|
|
23
37
|
* Validate the value and emit Zod issues for Ajv failures.
|
|
24
|
-
* @param {unknown} value
|
|
25
|
-
* @param {import('zod').RefinementCtx} ctx
|
|
38
|
+
* @param {unknown} value - Value to validate.
|
|
39
|
+
* @param {import('zod').RefinementCtx} ctx - Zod refinement context for reporting issues.
|
|
26
40
|
* @returns {void}
|
|
27
41
|
*/
|
|
28
42
|
function apply(value, ctx) {
|
|
29
43
|
const ok = check(value);
|
|
30
|
-
if (ok)
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
44
|
+
if (ok) return;
|
|
33
45
|
const text = validator.errorsText(check.errors, { dataVar: 'value' });
|
|
34
46
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: text });
|
|
35
47
|
}
|
|
@@ -39,7 +51,7 @@ function refiners(validator, check) {
|
|
|
39
51
|
|
|
40
52
|
/**
|
|
41
53
|
* Wrap a JSON Schema into a Zod schema that delegates validation to Ajv.
|
|
42
|
-
* @param {unknown} json
|
|
54
|
+
* @param {unknown} json - JSON Schema definition to wrap.
|
|
43
55
|
* @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
|
|
44
56
|
*/
|
|
45
57
|
function wrapschema(json) {
|
|
@@ -60,8 +72,8 @@ function wrapschema(json) {
|
|
|
60
72
|
/**
|
|
61
73
|
* Read all pages for a list request until pagination completes.
|
|
62
74
|
* @template T
|
|
63
|
-
* @param {(cursor?: string) => Promise<T & { nextCursor?: string }>} call
|
|
64
|
-
* @param {(result: T) => Array<unknown>} pull
|
|
75
|
+
* @param {(cursor?: string) => Promise<T & { nextCursor?: string }>} call - Function that requests a page by cursor.
|
|
76
|
+
* @param {(result: T) => Array<unknown>} pull - Function that extracts items from each page result.
|
|
65
77
|
* @returns {Promise<Array<unknown>>}
|
|
66
78
|
*/
|
|
67
79
|
async function page(call, pull) {
|
|
@@ -71,13 +83,9 @@ async function page(call, pull) {
|
|
|
71
83
|
while (true) {
|
|
72
84
|
const result = await call(cursor);
|
|
73
85
|
const batch = pull(result);
|
|
74
|
-
if (Array.isArray(batch))
|
|
75
|
-
list.push(...batch);
|
|
76
|
-
}
|
|
86
|
+
if (Array.isArray(batch)) list.push(...batch);
|
|
77
87
|
cursor = result.nextCursor;
|
|
78
|
-
if (!cursor)
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
88
|
+
if (!cursor) break;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
return list;
|
|
@@ -85,13 +93,13 @@ async function page(call, pull) {
|
|
|
85
93
|
|
|
86
94
|
/**
|
|
87
95
|
* Extract tool entries from a client.
|
|
88
|
-
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
|
|
96
|
+
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
|
|
89
97
|
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
90
98
|
*/
|
|
91
99
|
async function tools(client) {
|
|
92
100
|
/**
|
|
93
101
|
* Fetch a page of tools.
|
|
94
|
-
* @param {string | undefined} cursor
|
|
102
|
+
* @param {string | undefined} cursor - Pagination cursor from the previous page.
|
|
95
103
|
* @returns {Promise<{ tools: Array<Record<string, unknown>>, nextCursor?: string }>}
|
|
96
104
|
*/
|
|
97
105
|
async function call(cursor) {
|
|
@@ -103,7 +111,7 @@ async function tools(client) {
|
|
|
103
111
|
|
|
104
112
|
/**
|
|
105
113
|
* Pluck tool definitions from a list response.
|
|
106
|
-
* @param {{ tools?: Array<Record<string, unknown>> }} result
|
|
114
|
+
* @param {{ tools?: Array<Record<string, unknown>> }} result - listTools response payload.
|
|
107
115
|
* @returns {Array<Record<string, unknown>>}
|
|
108
116
|
*/
|
|
109
117
|
function pull(result) {
|
|
@@ -115,13 +123,13 @@ async function tools(client) {
|
|
|
115
123
|
|
|
116
124
|
/**
|
|
117
125
|
* Extract resource entries from a client.
|
|
118
|
-
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
|
|
126
|
+
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
|
|
119
127
|
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
120
128
|
*/
|
|
121
129
|
async function resources(client) {
|
|
122
130
|
/**
|
|
123
131
|
* Fetch a page of resources.
|
|
124
|
-
* @param {string | undefined} cursor
|
|
132
|
+
* @param {string | undefined} cursor - Pagination cursor from the previous page.
|
|
125
133
|
* @returns {Promise<{ resources: Array<Record<string, unknown>>, nextCursor?: string }>}
|
|
126
134
|
*/
|
|
127
135
|
async function call(cursor) {
|
|
@@ -133,7 +141,7 @@ async function resources(client) {
|
|
|
133
141
|
|
|
134
142
|
/**
|
|
135
143
|
* Pluck resource definitions from a list response.
|
|
136
|
-
* @param {{ resources?: Array<Record<string, unknown>> }} result
|
|
144
|
+
* @param {{ resources?: Array<Record<string, unknown>> }} result - listResources response payload.
|
|
137
145
|
* @returns {Array<Record<string, unknown>>}
|
|
138
146
|
*/
|
|
139
147
|
function pull(result) {
|
|
@@ -145,13 +153,13 @@ async function resources(client) {
|
|
|
145
153
|
|
|
146
154
|
/**
|
|
147
155
|
* Extract resource template entries from a client.
|
|
148
|
-
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
|
|
156
|
+
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
|
|
149
157
|
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
150
158
|
*/
|
|
151
159
|
async function templates(client) {
|
|
152
160
|
/**
|
|
153
161
|
* Fetch a page of resource templates.
|
|
154
|
-
* @param {string | undefined} cursor
|
|
162
|
+
* @param {string | undefined} cursor - Pagination cursor from the previous page.
|
|
155
163
|
* @returns {Promise<{ resourceTemplates: Array<Record<string, unknown>>, nextCursor?: string }>}
|
|
156
164
|
*/
|
|
157
165
|
async function call(cursor) {
|
|
@@ -163,25 +171,30 @@ async function templates(client) {
|
|
|
163
171
|
|
|
164
172
|
/**
|
|
165
173
|
* Pluck resource template definitions from a list response.
|
|
166
|
-
* @param {{ resourceTemplates?: Array<Record<string, unknown>> }} result
|
|
174
|
+
* @param {{ resourceTemplates?: Array<Record<string, unknown>> }} result - listResourceTemplates response payload.
|
|
167
175
|
* @returns {Array<Record<string, unknown>>}
|
|
168
176
|
*/
|
|
169
177
|
function pull(result) {
|
|
170
178
|
return Array.isArray(result.resourceTemplates) ? result.resourceTemplates : [];
|
|
171
179
|
}
|
|
172
180
|
|
|
173
|
-
|
|
181
|
+
try {
|
|
182
|
+
return await page(call, pull);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (ismissing(error)) return [];
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
174
187
|
}
|
|
175
188
|
|
|
176
189
|
/**
|
|
177
190
|
* Extract prompt entries from a client.
|
|
178
|
-
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
|
|
191
|
+
* @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client - MCP client instance.
|
|
179
192
|
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
180
193
|
*/
|
|
181
194
|
async function prompts(client) {
|
|
182
195
|
/**
|
|
183
196
|
* Fetch a page of prompts.
|
|
184
|
-
* @param {string | undefined} cursor
|
|
197
|
+
* @param {string | undefined} cursor - Pagination cursor from the previous page.
|
|
185
198
|
* @returns {Promise<{ prompts: Array<Record<string, unknown>>, nextCursor?: string }>}
|
|
186
199
|
*/
|
|
187
200
|
async function call(cursor) {
|
|
@@ -193,7 +206,7 @@ async function prompts(client) {
|
|
|
193
206
|
|
|
194
207
|
/**
|
|
195
208
|
* Pluck prompt definitions from a list response.
|
|
196
|
-
* @param {{ prompts?: Array<Record<string, unknown>> }} result
|
|
209
|
+
* @param {{ prompts?: Array<Record<string, unknown>> }} result - listPrompts response payload.
|
|
197
210
|
* @returns {Array<Record<string, unknown>>}
|
|
198
211
|
*/
|
|
199
212
|
function pull(result) {
|
|
@@ -205,80 +218,58 @@ async function prompts(client) {
|
|
|
205
218
|
|
|
206
219
|
/**
|
|
207
220
|
* Build shared metadata from an MCP item.
|
|
208
|
-
* @param {Record<string, unknown>} item
|
|
221
|
+
* @param {Record<string, unknown>} item - MCP definition object.
|
|
209
222
|
* @returns {Record<string, unknown>}
|
|
210
223
|
*/
|
|
211
224
|
function meta(item) {
|
|
212
225
|
const out = {};
|
|
213
|
-
if (Array.isArray(item.icons))
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
if (isrecord(item._meta)) {
|
|
217
|
-
out._meta = item._meta;
|
|
218
|
-
}
|
|
226
|
+
if (Array.isArray(item.icons)) out.icons = item.icons;
|
|
227
|
+
if (isrecord(item._meta)) out._meta = item._meta;
|
|
219
228
|
return out;
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
/**
|
|
223
232
|
* Build metadata for tools, including annotations.
|
|
224
|
-
* @param {Record<string, unknown>} tool
|
|
233
|
+
* @param {Record<string, unknown>} tool - Tool definition object.
|
|
225
234
|
* @returns {Record<string, unknown>}
|
|
226
235
|
*/
|
|
227
236
|
function toolmeta(tool) {
|
|
228
237
|
const out = meta(tool);
|
|
229
|
-
if (isrecord(tool.annotations))
|
|
230
|
-
out.annotations = tool.annotations;
|
|
231
|
-
}
|
|
238
|
+
if (isrecord(tool.annotations)) out.annotations = tool.annotations;
|
|
232
239
|
return out;
|
|
233
240
|
}
|
|
234
241
|
|
|
235
242
|
/**
|
|
236
243
|
* Derive a display title with tool annotation fallback.
|
|
237
|
-
* @param {Record<string, unknown>} tool
|
|
244
|
+
* @param {Record<string, unknown>} tool - Tool definition object.
|
|
238
245
|
* @returns {string | undefined}
|
|
239
246
|
*/
|
|
240
247
|
function tooltitle(tool) {
|
|
241
|
-
if (typeof tool.title === 'string')
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
if (isrecord(tool.annotations) && typeof tool.annotations.title === 'string') {
|
|
245
|
-
return tool.annotations.title;
|
|
246
|
-
}
|
|
248
|
+
if (typeof tool.title === 'string') return tool.title;
|
|
249
|
+
if (isrecord(tool.annotations) && typeof tool.annotations.title === 'string') return tool.annotations.title;
|
|
247
250
|
return undefined;
|
|
248
251
|
}
|
|
249
252
|
|
|
250
253
|
/**
|
|
251
254
|
* Extract MCP Apps UI metadata from an item.
|
|
252
|
-
* @param {Record<string, unknown>} item
|
|
255
|
+
* @param {Record<string, unknown>} item - MCP definition object.
|
|
253
256
|
* @returns {Record<string, unknown> | undefined}
|
|
254
257
|
*/
|
|
255
258
|
function uiitem(item) {
|
|
256
|
-
if (!isrecord(item._meta))
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
if (!isrecord(item._meta.ui)) {
|
|
260
|
-
return undefined;
|
|
261
|
-
}
|
|
259
|
+
if (!isrecord(item._meta)) return undefined;
|
|
260
|
+
if (!isrecord(item._meta.ui)) return undefined;
|
|
262
261
|
const ui = /** @type {Record<string, unknown>} */ (item._meta.ui);
|
|
263
262
|
const out = {};
|
|
264
|
-
if (typeof ui.resourceUri === 'string')
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
out.csp = ui.csp;
|
|
269
|
-
}
|
|
270
|
-
if (Array.isArray(ui.permissions)) {
|
|
271
|
-
out.permissions = ui.permissions;
|
|
272
|
-
}
|
|
273
|
-
if (Object.keys(out).length === 0) {
|
|
274
|
-
return undefined;
|
|
275
|
-
}
|
|
263
|
+
if (typeof ui.resourceUri === 'string') out.resourceUri = ui.resourceUri;
|
|
264
|
+
if (typeof ui.csp === 'string') out.csp = ui.csp;
|
|
265
|
+
if (Array.isArray(ui.permissions)) out.permissions = ui.permissions;
|
|
266
|
+
if (Object.keys(out).length === 0) return undefined;
|
|
276
267
|
return out;
|
|
277
268
|
}
|
|
278
269
|
|
|
279
270
|
/**
|
|
280
271
|
* Normalize a tool definition into the unified schema.
|
|
281
|
-
* @param {Record<string, unknown>} tool
|
|
272
|
+
* @param {Record<string, unknown>} tool - Raw tool definition from MCP.
|
|
282
273
|
* @returns {Record<string, unknown>}
|
|
283
274
|
*/
|
|
284
275
|
function toolitem(tool) {
|
|
@@ -287,12 +278,8 @@ function toolitem(tool) {
|
|
|
287
278
|
const detail = { input };
|
|
288
279
|
const ui = uiitem(tool);
|
|
289
280
|
|
|
290
|
-
if (isrecord(tool.outputSchema))
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
if (ui) {
|
|
294
|
-
detail.ui = ui;
|
|
295
|
-
}
|
|
281
|
+
if (isrecord(tool.outputSchema)) detail.output = output;
|
|
282
|
+
if (ui) detail.ui = ui;
|
|
296
283
|
|
|
297
284
|
return {
|
|
298
285
|
type: 'tool',
|
|
@@ -306,7 +293,7 @@ function toolitem(tool) {
|
|
|
306
293
|
|
|
307
294
|
/**
|
|
308
295
|
* Normalize a resource definition into the unified schema.
|
|
309
|
-
* @param {Record<string, unknown>} resource
|
|
296
|
+
* @param {Record<string, unknown>} resource - Raw resource definition from MCP.
|
|
310
297
|
* @returns {Record<string, unknown>}
|
|
311
298
|
*/
|
|
312
299
|
function resourceitem(resource) {
|
|
@@ -328,7 +315,7 @@ function resourceitem(resource) {
|
|
|
328
315
|
|
|
329
316
|
/**
|
|
330
317
|
* Normalize a resource template definition into the unified schema.
|
|
331
|
-
* @param {Record<string, unknown>} template
|
|
318
|
+
* @param {Record<string, unknown>} template - Raw resource template definition from MCP.
|
|
332
319
|
* @returns {Record<string, unknown>}
|
|
333
320
|
*/
|
|
334
321
|
function templateitem(template) {
|
|
@@ -347,30 +334,22 @@ function templateitem(template) {
|
|
|
347
334
|
|
|
348
335
|
/**
|
|
349
336
|
* Build a JSON schema from prompt arguments so prompts share the input schema shape.
|
|
350
|
-
* @param {Record<string, unknown>} prompt
|
|
337
|
+
* @param {Record<string, unknown>} prompt - Raw prompt definition from MCP.
|
|
351
338
|
* @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
|
|
352
339
|
*/
|
|
353
340
|
function promptinput(prompt) {
|
|
354
|
-
if (!Array.isArray(prompt.arguments))
|
|
355
|
-
return wrapschema(undefined);
|
|
356
|
-
}
|
|
341
|
+
if (!Array.isArray(prompt.arguments)) return wrapschema(undefined);
|
|
357
342
|
|
|
358
343
|
const properties = {};
|
|
359
344
|
const required = [];
|
|
360
345
|
|
|
361
346
|
for (const item of prompt.arguments) {
|
|
362
|
-
if (!isrecord(item))
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
if (typeof item.name !== 'string' || item.name.length === 0) {
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
347
|
+
if (!isrecord(item)) continue;
|
|
348
|
+
if (typeof item.name !== 'string' || item.name.length === 0) continue;
|
|
368
349
|
properties[item.name] = {
|
|
369
350
|
description: typeof item.description === 'string' ? item.description : undefined
|
|
370
351
|
};
|
|
371
|
-
if (item.required === true)
|
|
372
|
-
required.push(item.name);
|
|
373
|
-
}
|
|
352
|
+
if (item.required === true) required.push(item.name);
|
|
374
353
|
}
|
|
375
354
|
|
|
376
355
|
const json = {
|
|
@@ -385,7 +364,7 @@ function promptinput(prompt) {
|
|
|
385
364
|
|
|
386
365
|
/**
|
|
387
366
|
* Normalize a prompt definition into the unified schema.
|
|
388
|
-
* @param {Record<string, unknown>} prompt
|
|
367
|
+
* @param {Record<string, unknown>} prompt - Raw prompt definition from MCP.
|
|
389
368
|
* @returns {Record<string, unknown>}
|
|
390
369
|
*/
|
|
391
370
|
function promptitem(prompt) {
|
|
@@ -404,7 +383,7 @@ function promptitem(prompt) {
|
|
|
404
383
|
|
|
405
384
|
/**
|
|
406
385
|
* 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
|
|
386
|
+
* @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
387
|
* @returns {Array<Record<string, unknown>>}
|
|
409
388
|
*/
|
|
410
389
|
function normalize(data) {
|
|
@@ -439,12 +418,16 @@ function normalize(data) {
|
|
|
439
418
|
|
|
440
419
|
/**
|
|
441
420
|
* Extract MCP tool/resource/prompt schemas into a unified Zod-backed format.
|
|
442
|
-
* @param {import('@mcp-layer/session').Session} link
|
|
421
|
+
* @param {import('@mcp-layer/session').Session} link - Active session with a connected MCP server.
|
|
443
422
|
* @returns {Promise<{ server: { info: Record<string, unknown> | undefined, capabilities: Record<string, unknown> | undefined, instructions: string | undefined }, items: Array<Record<string, unknown>> }>}
|
|
444
423
|
*/
|
|
445
424
|
export async function extract(link) {
|
|
446
425
|
if (!link || !link.client) {
|
|
447
|
-
throw new
|
|
426
|
+
throw new LayerError({
|
|
427
|
+
name: 'schema',
|
|
428
|
+
method: 'extract',
|
|
429
|
+
message: 'Expected a Session instance.',
|
|
430
|
+
});
|
|
448
431
|
}
|
|
449
432
|
|
|
450
433
|
const client = link.client;
|
|
@@ -459,18 +442,14 @@ export async function extract(link) {
|
|
|
459
442
|
prompts: undefined
|
|
460
443
|
};
|
|
461
444
|
|
|
462
|
-
if (caps && caps.tools)
|
|
463
|
-
data.tools = await tools(client);
|
|
464
|
-
}
|
|
445
|
+
if (caps && caps.tools) data.tools = await tools(client);
|
|
465
446
|
|
|
466
447
|
if (caps && caps.resources) {
|
|
467
448
|
data.resources = await resources(client);
|
|
468
449
|
data.templates = await templates(client);
|
|
469
450
|
}
|
|
470
451
|
|
|
471
|
-
if (caps && caps.prompts)
|
|
472
|
-
data.prompts = await prompts(client);
|
|
473
|
-
}
|
|
452
|
+
if (caps && caps.prompts) data.prompts = await prompts(client);
|
|
474
453
|
|
|
475
454
|
const items = normalize(data);
|
|
476
455
|
|