@mcp-layer/schema 0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/package.json +42 -0
  4. package/src/index.js +485 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arnout Kazemier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # @mcp-layer/schema
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.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ pnpm add @mcp-layer/schema
9
+ # or
10
+ npm install @mcp-layer/schema
11
+ # or
12
+ yarn add @mcp-layer/schema
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```js
18
+ import { load } from '@mcp-layer/config';
19
+ import { connect } from '@mcp-layer/connect';
20
+ import { extract } from '@mcp-layer/schema';
21
+
22
+ const config = await load(undefined, process.cwd());
23
+ const session = await connect(config, 'demo');
24
+ const output = await extract(session);
25
+
26
+ console.log(output.server.info);
27
+ console.log(output.items[0]);
28
+ await session.close();
29
+ ```
30
+
31
+ ## What this package does
32
+
33
+ 1) Reads the MCP server capabilities and metadata from the live client connection.
34
+ 2) Calls MCP list endpoints (`tools/list`, `resources/list`, `resources/templates/list`, `prompts/list`) using pagination.
35
+ 3) Normalizes everything into a unified, type-discriminated schema so generators can consume a single list.
36
+ 4) Wraps tool input/output JSON Schemas into Zod validators backed by Ajv, while preserving the original JSON Schema.
37
+ 5) Converts prompt arguments into a lightweight JSON Schema so prompts and tools share the same `detail.input` shape.
38
+
39
+ ## Output shape (authoritative)
40
+
41
+ The package returns:
42
+
43
+ ```js
44
+ {
45
+ server: {
46
+ info, // initialize.serverInfo
47
+ capabilities, // initialize.capabilities
48
+ instructions // initialize.instructions (optional)
49
+ },
50
+ items: [
51
+ {
52
+ type, // "tool" | "resource" | "resource-template" | "prompt"
53
+ name,
54
+ title,
55
+ description,
56
+ meta, // icons, annotations, _meta
57
+ detail // type-specific data
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ Type discriminator:
64
+ - `tool`, `resource`, `resource-template`, `prompt`
65
+ - MCP Apps do not introduce a new `type`; tools remain `tool` and UI resources remain `resource`.
66
+
67
+ ### detail: tools
68
+
69
+ ```js
70
+ detail: {
71
+ input: {
72
+ schema, // Zod schema (Ajv-backed)
73
+ json, // original JSON Schema
74
+ error // optional Ajv compile error message
75
+ },
76
+ output: { schema, json, error }, // only when the tool advertises outputSchema
77
+ ui: { resourceUri, csp, permissions } // when _meta.ui is present (MCP Apps)
78
+ }
79
+ ```
80
+
81
+ ### detail: resources
82
+
83
+ ```js
84
+ detail: {
85
+ uri,
86
+ mimeType,
87
+ size,
88
+ ui: { resourceUri, csp, permissions } // when _meta.ui is present (MCP Apps)
89
+ }
90
+ ```
91
+
92
+ ### detail: resource templates
93
+
94
+ ```js
95
+ detail: {
96
+ uriTemplate,
97
+ mimeType
98
+ }
99
+ ```
100
+
101
+ ### detail: prompts
102
+
103
+ ```js
104
+ detail: {
105
+ input: {
106
+ schema, // Zod schema derived from prompt arguments
107
+ json, // JSON Schema derived from prompt arguments
108
+ error // optional Ajv compile error message
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## MCP Apps support
114
+
115
+ If a server exposes MCP Apps metadata:
116
+ - Tools may include `_meta.ui.resourceUri` pointing to a `ui://` resource.
117
+ - UI resources may include `_meta.ui` with CSP and permissions.
118
+
119
+ This package normalizes those fields into `detail.ui` so generators can:
120
+ - Detect which tools expect UI rendering.
121
+ - Resolve and render the `ui://` resource.
122
+ - Apply CSP and permissions when hosting the UI in a webview or browser sandbox.
123
+
124
+ ## Generator guidance
125
+
126
+ Suggested uses for the unified schema:
127
+ - CLI: Use `name`, `description`, and `detail.input` to build flags and help text.
128
+ - REST: Use `name` for endpoints, `detail.input.json` for request validation, and `detail.output` for response schema.
129
+ - UI: Use `detail.ui.resourceUri` to locate the UI and read `detail.ui.csp` / `permissions`.
130
+
131
+ ## Responsibilities and lifecycle
132
+
133
+ - This package does not open or close connections. It expects a live `Session` from `@mcp-layer/connect` or `@mcp-layer/attach`.
134
+ - You are responsible for calling `session.close()` after extraction.
135
+ - If the server doesn't advertise a capability (tools/resources/prompts), extraction skips that surface.
136
+
137
+ ## Error handling
138
+
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
+ - Servers that return empty lists produce an empty `items` array (not an error).
141
+
142
+ ## JSON Schema vs Zod
143
+
144
+ 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.
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mcp-layer/schema",
3
+ "version": "0.0.0",
4
+ "description": "Extract and normalize MCP schemas into a unified Zod-based format.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "mcp",
8
+ "schema",
9
+ "zod",
10
+ "tools",
11
+ "resources",
12
+ "prompts"
13
+ ],
14
+ "scripts": {
15
+ "test": "node --test --experimental-test-coverage ./test/**/*.js"
16
+ },
17
+ "main": "src/index.js",
18
+ "exports": {
19
+ ".": {
20
+ "import": "./src/index.js",
21
+ "default": "./src/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "files": [
26
+ "src",
27
+ "LICENSE"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "ajv": "^8.17.1",
34
+ "zod": "^3.25.76"
35
+ },
36
+ "devDependencies": {
37
+ "@mcp-layer/attach": "workspace:*",
38
+ "@mcp-layer/config": "workspace:*",
39
+ "@mcp-layer/connect": "workspace:*",
40
+ "@mcp-layer/test-server": "workspace:*"
41
+ }
42
+ }
package/src/index.js ADDED
@@ -0,0 +1,485 @@
1
+ import Ajv from 'ajv';
2
+ import { z } from 'zod';
3
+
4
+ const ajv = new Ajv({ allErrors: true, strict: false });
5
+
6
+ /**
7
+ * Test if a value is a plain object.
8
+ * @param {unknown} value
9
+ * @returns {value is Record<string, unknown>}
10
+ */
11
+ function isrecord(value) {
12
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
13
+ }
14
+
15
+ /**
16
+ * Build a Zod refinement function backed by Ajv.
17
+ * @param {import('ajv').Ajv} validator
18
+ * @param {import('ajv').ValidateFunction} check
19
+ * @returns {(value: unknown, ctx: import('zod').RefinementCtx) => void}
20
+ */
21
+ function refiners(validator, check) {
22
+ /**
23
+ * Validate the value and emit Zod issues for Ajv failures.
24
+ * @param {unknown} value
25
+ * @param {import('zod').RefinementCtx} ctx
26
+ * @returns {void}
27
+ */
28
+ function apply(value, ctx) {
29
+ const ok = check(value);
30
+ if (ok) {
31
+ return;
32
+ }
33
+ const text = validator.errorsText(check.errors, { dataVar: 'value' });
34
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: text });
35
+ }
36
+
37
+ return apply;
38
+ }
39
+
40
+ /**
41
+ * Wrap a JSON Schema into a Zod schema that delegates validation to Ajv.
42
+ * @param {unknown} json
43
+ * @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
44
+ */
45
+ function wrapschema(json) {
46
+ if (!isrecord(json)) {
47
+ return { schema: z.unknown(), json: undefined };
48
+ }
49
+
50
+ try {
51
+ const check = ajv.compile(json);
52
+ const refine = refiners(ajv, check);
53
+ return { schema: z.any().superRefine(refine), json };
54
+ } catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ return { schema: z.unknown(), json, error: message };
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Read all pages for a list request until pagination completes.
62
+ * @template T
63
+ * @param {(cursor?: string) => Promise<T & { nextCursor?: string }>} call
64
+ * @param {(result: T) => Array<unknown>} pull
65
+ * @returns {Promise<Array<unknown>>}
66
+ */
67
+ async function page(call, pull) {
68
+ let cursor;
69
+ const list = [];
70
+
71
+ while (true) {
72
+ const result = await call(cursor);
73
+ const batch = pull(result);
74
+ if (Array.isArray(batch)) {
75
+ list.push(...batch);
76
+ }
77
+ cursor = result.nextCursor;
78
+ if (!cursor) {
79
+ break;
80
+ }
81
+ }
82
+
83
+ return list;
84
+ }
85
+
86
+ /**
87
+ * Extract tool entries from a client.
88
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
89
+ * @returns {Promise<Array<Record<string, unknown>>>}
90
+ */
91
+ async function tools(client) {
92
+ /**
93
+ * Fetch a page of tools.
94
+ * @param {string | undefined} cursor
95
+ * @returns {Promise<{ tools: Array<Record<string, unknown>>, nextCursor?: string }>}
96
+ */
97
+ async function call(cursor) {
98
+ if (cursor) {
99
+ return client.listTools({ cursor });
100
+ }
101
+ return client.listTools();
102
+ }
103
+
104
+ /**
105
+ * Pluck tool definitions from a list response.
106
+ * @param {{ tools?: Array<Record<string, unknown>> }} result
107
+ * @returns {Array<Record<string, unknown>>}
108
+ */
109
+ function pull(result) {
110
+ return Array.isArray(result.tools) ? result.tools : [];
111
+ }
112
+
113
+ return page(call, pull);
114
+ }
115
+
116
+ /**
117
+ * Extract resource entries from a client.
118
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
119
+ * @returns {Promise<Array<Record<string, unknown>>>}
120
+ */
121
+ async function resources(client) {
122
+ /**
123
+ * Fetch a page of resources.
124
+ * @param {string | undefined} cursor
125
+ * @returns {Promise<{ resources: Array<Record<string, unknown>>, nextCursor?: string }>}
126
+ */
127
+ async function call(cursor) {
128
+ if (cursor) {
129
+ return client.listResources({ cursor });
130
+ }
131
+ return client.listResources();
132
+ }
133
+
134
+ /**
135
+ * Pluck resource definitions from a list response.
136
+ * @param {{ resources?: Array<Record<string, unknown>> }} result
137
+ * @returns {Array<Record<string, unknown>>}
138
+ */
139
+ function pull(result) {
140
+ return Array.isArray(result.resources) ? result.resources : [];
141
+ }
142
+
143
+ return page(call, pull);
144
+ }
145
+
146
+ /**
147
+ * Extract resource template entries from a client.
148
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
149
+ * @returns {Promise<Array<Record<string, unknown>>>}
150
+ */
151
+ async function templates(client) {
152
+ /**
153
+ * Fetch a page of resource templates.
154
+ * @param {string | undefined} cursor
155
+ * @returns {Promise<{ resourceTemplates: Array<Record<string, unknown>>, nextCursor?: string }>}
156
+ */
157
+ async function call(cursor) {
158
+ if (cursor) {
159
+ return client.listResourceTemplates({ cursor });
160
+ }
161
+ return client.listResourceTemplates();
162
+ }
163
+
164
+ /**
165
+ * Pluck resource template definitions from a list response.
166
+ * @param {{ resourceTemplates?: Array<Record<string, unknown>> }} result
167
+ * @returns {Array<Record<string, unknown>>}
168
+ */
169
+ function pull(result) {
170
+ return Array.isArray(result.resourceTemplates) ? result.resourceTemplates : [];
171
+ }
172
+
173
+ return page(call, pull);
174
+ }
175
+
176
+ /**
177
+ * Extract prompt entries from a client.
178
+ * @param {import('@modelcontextprotocol/sdk/client/index.js').Client} client
179
+ * @returns {Promise<Array<Record<string, unknown>>>}
180
+ */
181
+ async function prompts(client) {
182
+ /**
183
+ * Fetch a page of prompts.
184
+ * @param {string | undefined} cursor
185
+ * @returns {Promise<{ prompts: Array<Record<string, unknown>>, nextCursor?: string }>}
186
+ */
187
+ async function call(cursor) {
188
+ if (cursor) {
189
+ return client.listPrompts({ cursor });
190
+ }
191
+ return client.listPrompts();
192
+ }
193
+
194
+ /**
195
+ * Pluck prompt definitions from a list response.
196
+ * @param {{ prompts?: Array<Record<string, unknown>> }} result
197
+ * @returns {Array<Record<string, unknown>>}
198
+ */
199
+ function pull(result) {
200
+ return Array.isArray(result.prompts) ? result.prompts : [];
201
+ }
202
+
203
+ return page(call, pull);
204
+ }
205
+
206
+ /**
207
+ * Build shared metadata from an MCP item.
208
+ * @param {Record<string, unknown>} item
209
+ * @returns {Record<string, unknown>}
210
+ */
211
+ function meta(item) {
212
+ const out = {};
213
+ if (Array.isArray(item.icons)) {
214
+ out.icons = item.icons;
215
+ }
216
+ if (isrecord(item._meta)) {
217
+ out._meta = item._meta;
218
+ }
219
+ return out;
220
+ }
221
+
222
+ /**
223
+ * Build metadata for tools, including annotations.
224
+ * @param {Record<string, unknown>} tool
225
+ * @returns {Record<string, unknown>}
226
+ */
227
+ function toolmeta(tool) {
228
+ const out = meta(tool);
229
+ if (isrecord(tool.annotations)) {
230
+ out.annotations = tool.annotations;
231
+ }
232
+ return out;
233
+ }
234
+
235
+ /**
236
+ * Derive a display title with tool annotation fallback.
237
+ * @param {Record<string, unknown>} tool
238
+ * @returns {string | undefined}
239
+ */
240
+ function tooltitle(tool) {
241
+ if (typeof tool.title === 'string') {
242
+ return tool.title;
243
+ }
244
+ if (isrecord(tool.annotations) && typeof tool.annotations.title === 'string') {
245
+ return tool.annotations.title;
246
+ }
247
+ return undefined;
248
+ }
249
+
250
+ /**
251
+ * Extract MCP Apps UI metadata from an item.
252
+ * @param {Record<string, unknown>} item
253
+ * @returns {Record<string, unknown> | undefined}
254
+ */
255
+ function uiitem(item) {
256
+ if (!isrecord(item._meta)) {
257
+ return undefined;
258
+ }
259
+ if (!isrecord(item._meta.ui)) {
260
+ return undefined;
261
+ }
262
+ const ui = /** @type {Record<string, unknown>} */ (item._meta.ui);
263
+ const out = {};
264
+ if (typeof ui.resourceUri === 'string') {
265
+ out.resourceUri = ui.resourceUri;
266
+ }
267
+ if (typeof ui.csp === 'string') {
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
+ }
276
+ return out;
277
+ }
278
+
279
+ /**
280
+ * Normalize a tool definition into the unified schema.
281
+ * @param {Record<string, unknown>} tool
282
+ * @returns {Record<string, unknown>}
283
+ */
284
+ function toolitem(tool) {
285
+ const input = wrapschema(tool.inputSchema);
286
+ const output = wrapschema(tool.outputSchema);
287
+ const detail = { input };
288
+ const ui = uiitem(tool);
289
+
290
+ if (isrecord(tool.outputSchema)) {
291
+ detail.output = output;
292
+ }
293
+ if (ui) {
294
+ detail.ui = ui;
295
+ }
296
+
297
+ return {
298
+ type: 'tool',
299
+ name: typeof tool.name === 'string' ? tool.name : 'tool',
300
+ title: tooltitle(tool),
301
+ description: typeof tool.description === 'string' ? tool.description : undefined,
302
+ meta: toolmeta(tool),
303
+ detail
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Normalize a resource definition into the unified schema.
309
+ * @param {Record<string, unknown>} resource
310
+ * @returns {Record<string, unknown>}
311
+ */
312
+ function resourceitem(resource) {
313
+ const ui = uiitem(resource);
314
+ return {
315
+ type: 'resource',
316
+ name: typeof resource.name === 'string' ? resource.name : 'resource',
317
+ title: typeof resource.title === 'string' ? resource.title : undefined,
318
+ description: typeof resource.description === 'string' ? resource.description : undefined,
319
+ meta: meta(resource),
320
+ detail: {
321
+ uri: typeof resource.uri === 'string' ? resource.uri : undefined,
322
+ mimeType: typeof resource.mimeType === 'string' ? resource.mimeType : undefined,
323
+ size: typeof resource.size === 'number' ? resource.size : undefined,
324
+ ui
325
+ }
326
+ };
327
+ }
328
+
329
+ /**
330
+ * Normalize a resource template definition into the unified schema.
331
+ * @param {Record<string, unknown>} template
332
+ * @returns {Record<string, unknown>}
333
+ */
334
+ function templateitem(template) {
335
+ return {
336
+ type: 'resource-template',
337
+ name: typeof template.name === 'string' ? template.name : 'resource-template',
338
+ title: typeof template.title === 'string' ? template.title : undefined,
339
+ description: typeof template.description === 'string' ? template.description : undefined,
340
+ meta: meta(template),
341
+ detail: {
342
+ uriTemplate: typeof template.uriTemplate === 'string' ? template.uriTemplate : undefined,
343
+ mimeType: typeof template.mimeType === 'string' ? template.mimeType : undefined
344
+ }
345
+ };
346
+ }
347
+
348
+ /**
349
+ * Build a JSON schema from prompt arguments so prompts share the input schema shape.
350
+ * @param {Record<string, unknown>} prompt
351
+ * @returns {{ schema: import('zod').ZodTypeAny, json: Record<string, unknown> | undefined, error?: string }}
352
+ */
353
+ function promptinput(prompt) {
354
+ if (!Array.isArray(prompt.arguments)) {
355
+ return wrapschema(undefined);
356
+ }
357
+
358
+ const properties = {};
359
+ const required = [];
360
+
361
+ for (const item of prompt.arguments) {
362
+ if (!isrecord(item)) {
363
+ continue;
364
+ }
365
+ if (typeof item.name !== 'string' || item.name.length === 0) {
366
+ continue;
367
+ }
368
+ properties[item.name] = {
369
+ description: typeof item.description === 'string' ? item.description : undefined
370
+ };
371
+ if (item.required === true) {
372
+ required.push(item.name);
373
+ }
374
+ }
375
+
376
+ const json = {
377
+ type: 'object',
378
+ properties,
379
+ required: required.length > 0 ? required : undefined,
380
+ additionalProperties: true
381
+ };
382
+
383
+ return wrapschema(json);
384
+ }
385
+
386
+ /**
387
+ * Normalize a prompt definition into the unified schema.
388
+ * @param {Record<string, unknown>} prompt
389
+ * @returns {Record<string, unknown>}
390
+ */
391
+ function promptitem(prompt) {
392
+ const input = promptinput(prompt);
393
+ return {
394
+ type: 'prompt',
395
+ name: typeof prompt.name === 'string' ? prompt.name : 'prompt',
396
+ title: typeof prompt.title === 'string' ? prompt.title : undefined,
397
+ description: typeof prompt.description === 'string' ? prompt.description : undefined,
398
+ meta: meta(prompt),
399
+ detail: {
400
+ input
401
+ }
402
+ };
403
+ }
404
+
405
+ /**
406
+ * 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
408
+ * @returns {Array<Record<string, unknown>>}
409
+ */
410
+ function normalize(data) {
411
+ const list = [];
412
+
413
+ if (Array.isArray(data.tools)) {
414
+ for (const item of data.tools) {
415
+ list.push(toolitem(item));
416
+ }
417
+ }
418
+
419
+ if (Array.isArray(data.resources)) {
420
+ for (const item of data.resources) {
421
+ list.push(resourceitem(item));
422
+ }
423
+ }
424
+
425
+ if (Array.isArray(data.templates)) {
426
+ for (const item of data.templates) {
427
+ list.push(templateitem(item));
428
+ }
429
+ }
430
+
431
+ if (Array.isArray(data.prompts)) {
432
+ for (const item of data.prompts) {
433
+ list.push(promptitem(item));
434
+ }
435
+ }
436
+
437
+ return list;
438
+ }
439
+
440
+ /**
441
+ * Extract MCP tool/resource/prompt schemas into a unified Zod-backed format.
442
+ * @param {import('@mcp-layer/session').Session} link
443
+ * @returns {Promise<{ server: { info: Record<string, unknown> | undefined, capabilities: Record<string, unknown> | undefined, instructions: string | undefined }, items: Array<Record<string, unknown>> }>}
444
+ */
445
+ export async function extract(link) {
446
+ if (!link || !link.client) {
447
+ throw new Error('Expected a Session instance.');
448
+ }
449
+
450
+ const client = link.client;
451
+ const caps = client.getServerCapabilities();
452
+ const info = client.getServerVersion();
453
+ const instructions = client.getInstructions();
454
+
455
+ const data = {
456
+ tools: undefined,
457
+ resources: undefined,
458
+ templates: undefined,
459
+ prompts: undefined
460
+ };
461
+
462
+ if (caps && caps.tools) {
463
+ data.tools = await tools(client);
464
+ }
465
+
466
+ if (caps && caps.resources) {
467
+ data.resources = await resources(client);
468
+ data.templates = await templates(client);
469
+ }
470
+
471
+ if (caps && caps.prompts) {
472
+ data.prompts = await prompts(client);
473
+ }
474
+
475
+ const items = normalize(data);
476
+
477
+ return {
478
+ server: {
479
+ info,
480
+ capabilities: caps,
481
+ instructions
482
+ },
483
+ items
484
+ };
485
+ }