Skip to content

Commit 0d607db

Browse files
authored
fix: search, max string length (#88)
1 parent 1d69fcd commit 0d607db

7 files changed

Lines changed: 48 additions & 5 deletions

src/__tests__/__snapshots__/options.defaults.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ exports[`options defaults should return specific properties: defaults 1`] = `
2222
"transport": "stdio",
2323
},
2424
"maxDocsToLoad": 500,
25+
"maxSearchLength": 256,
2526
"name": "@patternfly/patternfly-mcp",
2627
"nodeVersion": 22,
2728
"pfExternal": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content",

src/options.defaults.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { type ToolModule } from './server.toolsUser';
1818
* @property llmsFilesPath - Path to the LLMs files directory.
1919
* @property {LoggingOptions} logging - Logging options.
2020
* @property maxDocsToLoad - Maximum number of docs to load.
21+
* @property maxSearchLength - Maximum length for search strings.
2122
* @property recommendedMaxDocsToLoad - Recommended maximum number of docs to load.
2223
* @property name - Name of the package.
2324
* @property nodeVersion - Node.js major version.
@@ -52,6 +53,7 @@ interface DefaultOptions<TLogOptions = LoggingOptions> {
5253
llmsFilesPath: string;
5354
logging: TLogOptions;
5455
maxDocsToLoad: number;
56+
maxSearchLength: number;
5557
recommendedMaxDocsToLoad: number;
5658
name: string;
5759
nodeVersion: number;
@@ -368,6 +370,7 @@ const DEFAULT_OPTIONS: DefaultOptions = {
368370
llmsFilesPath: (process.env.NODE_ENV === 'local' && '/llms-files') || join(resolve(process.cwd()), 'llms-files'),
369371
logging: LOGGING_OPTIONS,
370372
maxDocsToLoad: 500,
373+
maxSearchLength: 256,
371374
recommendedMaxDocsToLoad: 15,
372375
name: packageJson.name,
373376
nodeVersion: (process.env.NODE_ENV === 'local' && 22) || getNodeMajorVersion(),

src/resource.patternFlyDocsTemplate.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ const patternFlyDocsTemplateResource = (options = getOptions()): McpResource =>
4949
);
5050
}
5151

52+
if (name.length > options.maxSearchLength) {
53+
throw new McpError(
54+
ErrorCode.InvalidParams,
55+
`Resource name exceeds maximum length of ${options.maxSearchLength} characters.`
56+
);
57+
}
58+
5259
const docResults = [];
5360
const docs = [];
5461
const { exactMatches, searchResults } = searchComponents.memo(name);

src/resource.patternFlySchemasTemplate.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
22
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
33
import { componentNames as pfComponentNames } from '@patternfly/patternfly-component-schemas/json';
44
import { type McpResource } from './server';
5+
import { getOptions } from './options.context';
56
import { getComponentSchema } from './tool.patternFlyDocs';
67
import { searchComponents } from './tool.searchPatternFlyDocs';
78

@@ -32,9 +33,10 @@ const CONFIG = {
3233
/**
3334
* Resource creator for the component schemas template.
3435
*
36+
* @param options - Global options
3537
* @returns {McpResource} The resource definition tuple
3638
*/
37-
const patternFlySchemasTemplateResource = (): McpResource => [
39+
const patternFlySchemasTemplateResource = (options = getOptions()): McpResource => [
3840
NAME,
3941
URI_TEMPLATE,
4042
CONFIG,
@@ -48,6 +50,13 @@ const patternFlySchemasTemplateResource = (): McpResource => [
4850
);
4951
}
5052

53+
if (name.length > options.maxSearchLength) {
54+
throw new McpError(
55+
ErrorCode.InvalidParams,
56+
`Resource name exceeds maximum length of ${options.maxSearchLength} characters.`
57+
);
58+
}
59+
5160
const { exactMatches, searchResults } = searchComponents.memo(name, { names: pfComponentNames });
5261
let result: ComponentSchema | undefined = undefined;
5362

src/tool.componentSchemas.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ const componentSchemasTool = (options = getOptions()): McpTool => {
3737
);
3838
}
3939

40+
if (componentName.length > options.maxSearchLength) {
41+
throw new McpError(
42+
ErrorCode.InvalidParams,
43+
`Component name exceeds maximum length of ${options.maxSearchLength} characters.`
44+
);
45+
}
46+
4047
// Use fuzzySearch with `isFuzzyMatch` to handle exact and intentional suggestions in one pass
4148
const results = fuzzySearch(componentName, componentNames, {
4249
maxDistance: 3,
@@ -89,7 +96,7 @@ const componentSchemasTool = (options = getOptions()): McpTool => {
8996
9097
Returns prop definitions, types, and validation rules. Use this for structured component metadata, not documentation.`,
9198
inputSchema: {
92-
componentName: z.string().describe('Name of the PatternFly component (e.g., "Button", "Table")')
99+
componentName: z.string().max(options.maxSearchLength).describe('Name of the PatternFly component (e.g., "Button", "Table")')
93100
}
94101
},
95102
callback

src/tool.patternFlyDocs.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => {
6161
);
6262
}
6363

64+
if (isName && name.length > options.maxSearchLength) {
65+
throw new McpError(
66+
ErrorCode.InvalidParams,
67+
`String "name" exceeds maximum length of ${options.maxSearchLength} characters.`
68+
);
69+
}
70+
6471
const updatedUrlList = isUrlList ? urlList.slice(0, options.recommendedMaxDocsToLoad) : [];
6572

6673
if (isUrlList && urlList.length > options.recommendedMaxDocsToLoad) {
@@ -171,7 +178,7 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => {
171178
`,
172179
inputSchema: {
173180
urlList: z.array(z.string()).max(options.recommendedMaxDocsToLoad).optional().describe(`The list of URLs to fetch the documentation from (max ${options.recommendedMaxDocsToLoad} at a time`),
174-
name: z.string().optional().describe('The name of a PatternFly component to fetch documentation for (e.g., "Button", "Table")')
181+
name: z.string().max(options.maxSearchLength).optional().describe('The name of a PatternFly component to fetch documentation for (e.g., "Button", "Table")')
175182
}
176183
},
177184
callback

src/tool.searchPatternFlyDocs.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { LAYOUT_DOCS } from './docs.layout';
77
import { CHART_DOCS } from './docs.chart';
88
import { getLocalDocs } from './docs.local';
99
import { fuzzySearch, type FuzzySearchResult } from './server.search';
10+
import { getOptions } from './options.context';
1011
import { memo } from './server.caching';
1112
import { stringJoin } from './server.helpers';
1213
import { DEFAULT_OPTIONS } from './options.defaults';
@@ -194,9 +195,10 @@ searchComponents.memo = memo(searchComponents, DEFAULT_OPTIONS.toolMemoOptions.s
194195
* Searches for PatternFly component documentation URLs using fuzzy search.
195196
* Returns URLs only (does not fetch content). Use usePatternFlyDocs to fetch the actual content.
196197
*
198+
* @param options - Optional configuration options (defaults to OPTIONS)
197199
* @returns MCP tool tuple [name, schema, callback]
198200
*/
199-
const searchPatternFlyDocsTool = (): McpTool => {
201+
const searchPatternFlyDocsTool = (options = getOptions()): McpTool => {
200202
const callback = async (args: any = {}) => {
201203
const { searchQuery } = args;
202204

@@ -207,6 +209,13 @@ const searchPatternFlyDocsTool = (): McpTool => {
207209
);
208210
}
209211

212+
if (searchQuery.length > options.maxSearchLength) {
213+
throw new McpError(
214+
ErrorCode.InvalidParams,
215+
`Search query exceeds ${options.maxSearchLength} character max length.`
216+
);
217+
}
218+
210219
const { isSearchWildCardAll, searchResults } = searchComponents.memo(searchQuery, { allowWildCardAll: true });
211220

212221
if (!isSearchWildCardAll && searchResults.length === 0) {
@@ -271,7 +280,7 @@ const searchPatternFlyDocsTool = (): McpTool => {
271280
- Documentation URLs that can be used with "usePatternFlyDocs"
272281
`,
273282
inputSchema: {
274-
searchQuery: z.string().describe('Full or partial component name to search for (e.g., "button", "table", "*")')
283+
searchQuery: z.string().max(options.maxSearchLength).describe('Full or partial component name to search for (e.g., "button", "table", "*")')
275284
}
276285
},
277286
callback

0 commit comments

Comments
 (0)