Skip to content

Commit 21a1c5c

Browse files
committed
fix: review update
1 parent 0379df2 commit 21a1c5c

File tree

7 files changed

+191
-60
lines changed

7 files changed

+191
-60
lines changed

src/__tests__/__snapshots__/server.resourceMeta.test.ts.snap

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,14 @@ exports[`setMetaResources should attempt to return a resource, metaConfig is a t
7676
]
7777
`;
7878

79-
exports[`setMetaResources should attempt to return a resource, metaConfig is a template string 1`] = `
79+
exports[`setMetaResources should attempt to return a resource, metaConfig is a template string with complete 1`] = `
8080
[
8181
"test-resource-meta",
8282
ResourceTemplate {
8383
"_callbacks": {
84+
"complete": {
85+
"version": [MockFunction],
86+
},
8487
"list": undefined,
8588
},
8689
"_uriTemplate": UriTemplate {
@@ -107,6 +110,29 @@ exports[`setMetaResources should attempt to return a resource, metaConfig is a t
107110
]
108111
`;
109112

113+
exports[`setMetaResources should attempt to return a resource, metaConfig is a template string with complete undefined 1`] = `
114+
[
115+
"test-resource-meta",
116+
ResourceTemplate {
117+
"_callbacks": {
118+
"list": undefined,
119+
},
120+
"_uriTemplate": UriTemplate {
121+
"parts": [
122+
"test://uri/meta",
123+
],
124+
"template": "test://uri/meta",
125+
},
126+
},
127+
{
128+
"description": "Discovery manual for Test.",
129+
"mimeType": "text/markdown",
130+
"title": "Test Metadata",
131+
},
132+
[Function],
133+
]
134+
`;
135+
110136
exports[`setMetaResources should attempt to return a resource, metaConfig is almost a template 1`] = `
111137
[
112138
"test-resource-meta",

src/__tests__/server.resourceMeta.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,17 @@ describe('setMetaResources', () => {
162162
expected: 'test://lorem-ipsum/meta'
163163
},
164164
{
165-
description: 'metaConfig is a template string',
165+
description: 'metaConfig is a template string with complete undefined',
166166
uri: 'test://uri{?version}',
167167
complete: undefined,
168168
metaConfig: {},
169+
expected: 'test://uri/meta'
170+
},
171+
{
172+
description: 'metaConfig is a template string with complete',
173+
uri: 'test://uri{?version}',
174+
complete: { version: jest.fn() },
175+
metaConfig: {},
169176
expected: 'test://uri/meta{?version}'
170177
},
171178
{

src/server.resourceMeta.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ const getUriVariations = (baseUri: string, params: string[], allCombos = false):
151151
* If a provided handler is not a function, a default fallback async handler is used,
152152
* see type `McpResourceMetadataMetaConfig`.
153153
*
154+
* @note The generated `metaHandler` attempts to run any related "completion" callbacks. If they
155+
* fail, the handler silently ignores the error and continues execution. This is by design and
156+
* related to our concept of meta-resources providing an alternative avenue for MCP clients
157+
* lacking completion.
158+
*
154159
* @param {SetMetadataOptions} settings - Settings for configuring metadata options.
155160
* @returns An object containing the configured metadata options.
156161
*/
@@ -248,7 +253,7 @@ const getUriBreakdown = ({ uriOrTemplate, configUri, complete }: {
248253
baseUri = base;
249254
} else if (baseOriginalUri) {
250255
baseUri = `${baseOriginalUri}/meta`;
251-
metaUri = isMetaTemplate ? `${baseUri}{?version}` : baseUri;
256+
metaUri = isMetaTemplate && completeKeys.includes('version') ? `${baseUri}{?version}` : baseUri;
252257
}
253258

254259
return {
@@ -319,15 +324,21 @@ const setMetaResources = (resources: McpResourceCreator[], options = getOptions(
319324
registerAllSearchCombinations: metadata.registerAllSearchCombinations
320325
});
321326

327+
// Resolve and serialize meta handler output
328+
const resolveMetaText = async (version: string | undefined) => {
329+
const resourceText = await metaHandler(version);
330+
331+
return isPlainObject(resourceText) || Array.isArray(resourceText)
332+
? JSON.stringify(resourceText, null, 2)
333+
: String(resourceText);
334+
};
335+
322336
// Create a new meta-resource
323337
const metaResource = (opts = options): McpResource => {
324338
const metaCallback: McpResource[3] = async (passedUri, variables) =>
325339
runWithOptions(opts, async () => {
326340
const { version } = variables || {};
327-
const resourceText = await metaHandler(version);
328-
const updatedText = isPlainObject(resourceText) || Array.isArray(resourceText)
329-
? JSON.stringify(resourceText, null, 2)
330-
: String(resourceText);
341+
const updatedText = await resolveMetaText(version);
331342

332343
return {
333344
contents: [
@@ -360,10 +371,7 @@ const setMetaResources = (resources: McpResourceCreator[], options = getOptions(
360371
const { version } = variables || {};
361372

362373
if (result.contents) {
363-
const resourceText = await metaHandler(version);
364-
const updatedText = isPlainObject(resourceText) || Array.isArray(resourceText)
365-
? JSON.stringify(resourceText, null, 2)
366-
: String(resourceText);
374+
const updatedText = await resolveMetaText(version);
367375

368376
result.contents.push({
369377
uri: `${uriBreakdown.baseUri}${version ? `?version=${version}` : ''}`,

tests/e2e/__snapshots__/httpTransport.test.ts.snap

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

3-
exports[`Builtin resources, HTTP transport should read the patternfly-docs-meta: meta output 1`] = `
3+
exports[`Builtin resources, HTTP transport should read meta resources, patternfly-components-meta 1`] = `
4+
{
5+
"mimeType": "text/markdown",
6+
"text": "# PatternFly Components Index Metadata
7+
Use these parameters to filter the list of PatternFly components.
8+
9+
## Available Parameters
10+
11+
| Parameter | Valid Values | Description |
12+
| :--- | :--- | :--- |
13+
| \`category\` | \`accessibility\`, \`design-guidelines\`, \`react\` | Filter by category |
14+
| \`version\` | \`v5\`, \`v6\` | Filter by version |
15+
16+
## Available Patterns
17+
- **Base View**: \`patternfly://components/index\`
18+
- **Filtered View (version=...)**: \`patternfly://components/index?version=...\`
19+
- **Filtered View (version=...&category=...)**: \`patternfly://components/index?version=...&category=...\`",
20+
"uri": "patternfly://components/meta",
21+
}
22+
`;
23+
24+
exports[`Builtin resources, HTTP transport should read meta resources, patternfly-docs-meta 1`] = `
425
{
526
"mimeType": "text/markdown",
627
"text": "# PatternFly Documentation Index Metadata
@@ -27,6 +48,27 @@ Use these parameters to filter the PatternFly documentation index.
2748
}
2849
`;
2950
51+
exports[`Builtin resources, HTTP transport should read meta resources, patternfly-schemas-meta 1`] = `
52+
{
53+
"mimeType": "text/markdown",
54+
"text": "# PatternFly Component Schemas Index Metadata
55+
Use these parameters to filter the list of PatternFly component schemas.
56+
57+
## Available Parameters
58+
59+
| Parameter | Valid Values | Description |
60+
| :--- | :--- | :--- |
61+
| \`category\` | \`accessibility\`, \`design-guidelines\`, \`react\` | Filter by category |
62+
| \`version\` | \`v5\`, \`v6\` | Filter by version |
63+
64+
## Available Patterns
65+
- **Base View**: \`patternfly://schemas/index\`
66+
- **Filtered View (version=...)**: \`patternfly://schemas/index?version=...\`
67+
- **Filtered View (version=...&category=...)**: \`patternfly://schemas/index?version=...&category=...\`",
68+
"uri": "patternfly://schemas/meta",
69+
}
70+
`;
71+
3072
exports[`Builtin tools, HTTP transport should concatenate headers and separator with two remote files 1`] = `
3173
"# Content for https://www.patternfly.org/notARealPath/AboutModal.md
3274
Source: https://www.patternfly.org/notARealPath/AboutModal.md

tests/e2e/__snapshots__/stdioTransport.test.ts.snap

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

3-
exports[`Builtin resources, STDIO should read the patternfly-docs-meta: meta output 1`] = `
3+
exports[`Builtin resources, STDIO should read meta resources, patternfly-components-meta 1`] = `
4+
{
5+
"mimeType": "text/markdown",
6+
"text": "# PatternFly Components Index Metadata
7+
Use these parameters to filter the list of PatternFly components.
8+
9+
## Available Parameters
10+
11+
| Parameter | Valid Values | Description |
12+
| :--- | :--- | :--- |
13+
| \`category\` | \`accessibility\`, \`design-guidelines\`, \`react\` | Filter by category |
14+
| \`version\` | \`v5\`, \`v6\` | Filter by version |
15+
16+
## Available Patterns
17+
- **Base View**: \`patternfly://components/index\`
18+
- **Filtered View (version=...)**: \`patternfly://components/index?version=...\`
19+
- **Filtered View (version=...&category=...)**: \`patternfly://components/index?version=...&category=...\`",
20+
"uri": "patternfly://components/meta",
21+
}
22+
`;
23+
24+
exports[`Builtin resources, STDIO should read meta resources, patternfly-docs-meta 1`] = `
425
{
526
"mimeType": "text/markdown",
627
"text": "# PatternFly Documentation Index Metadata
@@ -27,6 +48,27 @@ Use these parameters to filter the PatternFly documentation index.
2748
}
2849
`;
2950
51+
exports[`Builtin resources, STDIO should read meta resources, patternfly-schemas-meta 1`] = `
52+
{
53+
"mimeType": "text/markdown",
54+
"text": "# PatternFly Component Schemas Index Metadata
55+
Use these parameters to filter the list of PatternFly component schemas.
56+
57+
## Available Parameters
58+
59+
| Parameter | Valid Values | Description |
60+
| :--- | :--- | :--- |
61+
| \`category\` | \`accessibility\`, \`design-guidelines\`, \`react\` | Filter by category |
62+
| \`version\` | \`v5\`, \`v6\` | Filter by version |
63+
64+
## Available Patterns
65+
- **Base View**: \`patternfly://schemas/index\`
66+
- **Filtered View (version=...)**: \`patternfly://schemas/index?version=...\`
67+
- **Filtered View (version=...&category=...)**: \`patternfly://schemas/index?version=...&category=...\`",
68+
"uri": "patternfly://schemas/meta",
69+
}
70+
`;
71+
3072
exports[`Builtin tools, STDIO should concatenate headers and separator with two remote files 1`] = `
3173
"# Content for http://127.0.0.1:5010/notARealPath/AboutModal.md
3274
Source: http://127.0.0.1:5010/notARealPath/AboutModal.md

tests/e2e/httpTransport.test.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -204,28 +204,43 @@ describe('Builtin resources, HTTP transport', () => {
204204
expect(templateNames).toContain('patternfly://schemas/{name}{?version,category}');
205205
});
206206

207-
it('should read the patternfly-context resource', async () => {
207+
it.each([
208+
{
209+
description: 'patternfly-components-meta',
210+
uri: 'patternfly://components/meta',
211+
expected: 'PatternFly Components Index Metadata'
212+
},
213+
{
214+
description: 'patternfly-docs-meta',
215+
uri: 'patternfly://docs/meta',
216+
expected: 'PatternFly Documentation Index Metadata'
217+
},
218+
{
219+
description: 'patternfly-schemas-meta',
220+
uri: 'patternfly://schemas/meta',
221+
expected: 'PatternFly Component Schemas Index Metadata'
222+
}
223+
])('should read meta resources, $description', async ({ uri, expected }) => {
208224
const response = await CLIENT?.send({
209225
method: 'resources/read',
210-
params: { uri: 'patternfly://context' }
226+
params: { uri }
211227
});
212228
const content = response?.result.contents[0];
213229

214-
expect(content.text).toContain('PatternFly is an open-source design system');
215-
expect(content.mimeType).toBe('text/markdown');
230+
expect(content.uri).toBe(uri);
231+
expect(content.text).toContain(expected);
232+
expect(content).toMatchSnapshot();
216233
});
217234

218-
it('should read the patternfly-docs-meta', async () => {
219-
const uri = 'patternfly://docs/meta';
235+
it('should read the patternfly-context resource', async () => {
220236
const response = await CLIENT?.send({
221237
method: 'resources/read',
222-
params: { uri }
238+
params: { uri: 'patternfly://context' }
223239
});
224240
const content = response?.result.contents[0];
225241

226-
expect(content.uri).toBe(uri);
227-
expect(content.text).toContain('PatternFly Documentation Index Metadata');
228-
expect(content).toMatchSnapshot('meta output');
242+
expect(content.text).toContain('PatternFly is an open-source design system');
243+
expect(content.mimeType).toBe('text/markdown');
229244
});
230245

231246
it('should read the patternfly-docs-index with query params', async () => {
@@ -263,18 +278,6 @@ describe('Builtin resources, HTTP transport', () => {
263278
expect(content.uri).toBe(uri);
264279
expect(content.text).toContain('PatternFly Component JSON Schemas Index');
265280
});
266-
267-
it('should read the patternfly-schemas-meta', async () => {
268-
const uri = 'patternfly://schemas/meta';
269-
const response = await CLIENT?.send({
270-
method: 'resources/read',
271-
params: { uri }
272-
});
273-
const content = response?.result.contents[0];
274-
275-
expect(content.uri).toBe(uri);
276-
expect(content.text).toContain('PatternFly Component Schemas Index Metadata');
277-
});
278281
});
279282

280283
describe('Inline tools, HTTP transport', () => {

tests/e2e/stdioTransport.test.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -201,40 +201,55 @@ describe('Builtin resources, STDIO', () => {
201201
expect(templateNames).toContain('patternfly://schemas/{name}{?version,category}');
202202
});
203203

204-
it('should read the patternfly-context resource', async () => {
204+
it.each([
205+
{
206+
description: 'patternfly-components-meta',
207+
uri: 'patternfly://components/meta',
208+
expected: 'PatternFly Components Index Metadata'
209+
},
210+
{
211+
description: 'patternfly-docs-meta',
212+
uri: 'patternfly://docs/meta',
213+
expected: 'PatternFly Documentation Index Metadata'
214+
},
215+
{
216+
description: 'patternfly-schemas-meta',
217+
uri: 'patternfly://schemas/meta',
218+
expected: 'PatternFly Component Schemas Index Metadata'
219+
}
220+
])('should read meta resources, $description', async ({ uri, expected }) => {
205221
const response = await CLIENT.send({
206222
method: 'resources/read',
207-
params: { uri: 'patternfly://context' }
223+
params: { uri }
208224
});
209225
const content = response?.result.contents[0];
210226

211-
expect(content.text).toContain('PatternFly is an open-source design system');
212-
expect(content.mimeType).toBe('text/markdown');
227+
expect(content.uri).toBe(uri);
228+
expect(content.text).toContain(expected);
229+
expect(content).toMatchSnapshot();
213230
});
214231

215-
it('should read the patternfly-docs-index with query params', async () => {
216-
const uri = 'patternfly://docs/index?version=v6&category=accessibility&section=components';
232+
it('should read the patternfly-context resource', async () => {
217233
const response = await CLIENT.send({
218234
method: 'resources/read',
219-
params: { uri }
235+
params: { uri: 'patternfly://context' }
220236
});
221237
const content = response?.result.contents[0];
222238

223-
expect(content.uri).toBe(uri);
224-
expect(content.text).toContain('PatternFly Documentation Index');
239+
expect(content.text).toContain('PatternFly is an open-source design system');
240+
expect(content.mimeType).toBe('text/markdown');
225241
});
226242

227-
it('should read the patternfly-docs-meta', async () => {
228-
const uri = 'patternfly://docs/meta';
243+
it('should read the patternfly-docs-index with query params', async () => {
244+
const uri = 'patternfly://docs/index?version=v6&category=accessibility&section=components';
229245
const response = await CLIENT.send({
230246
method: 'resources/read',
231247
params: { uri }
232248
});
233249
const content = response?.result.contents[0];
234250

235251
expect(content.uri).toBe(uri);
236-
expect(content.text).toContain('PatternFly Documentation Index Metadata');
237-
expect(content).toMatchSnapshot('meta output');
252+
expect(content.text).toContain('PatternFly Documentation Index');
238253
});
239254

240255
it('should read a doc through a template', async () => {
@@ -260,18 +275,6 @@ describe('Builtin resources, STDIO', () => {
260275
expect(content.uri).toBe(uri);
261276
expect(content.text).toContain('PatternFly Component JSON Schemas Index');
262277
});
263-
264-
it('should read the patternfly-schemas-meta', async () => {
265-
const uri = 'patternfly://schemas/meta';
266-
const response = await CLIENT.send({
267-
method: 'resources/read',
268-
params: { uri }
269-
});
270-
const content = response?.result.contents[0];
271-
272-
expect(content.uri).toBe(uri);
273-
expect(content.text).toContain('PatternFly Component Schemas Index Metadata');
274-
});
275278
});
276279

277280
describe('Logging', () => {

0 commit comments

Comments
 (0)