diff --git a/src/everything/prompts/resource.ts b/src/everything/prompts/resource.ts index 03989aaa25..21201e21bf 100644 --- a/src/everything/prompts/resource.ts +++ b/src/everything/prompts/resource.ts @@ -11,6 +11,7 @@ import { RESOURCE_TYPE_BLOB, RESOURCE_TYPE_TEXT, RESOURCE_TYPES, + validateResourceId, } from "../resources/templates.js"; /** @@ -49,16 +50,7 @@ export const registerEmbeddedResourcePrompt = (server: McpServer) => { } // Validate resourceId argument - const resourceId = Number(args?.resourceId); - if ( - !Number.isFinite(resourceId) || - !Number.isInteger(resourceId) || - resourceId < 1 - ) { - throw new Error( - `Invalid resourceId: ${args?.resourceId}. Must be a finite positive integer.` - ); - } + const resourceId = validateResourceId(args?.resourceId); // Get resource based on the resource type const uri = diff --git a/src/everything/resources/templates.ts b/src/everything/resources/templates.ts index 6d4903f74c..fdc714ee45 100644 --- a/src/everything/resources/templates.ts +++ b/src/everything/resources/templates.ts @@ -71,6 +71,24 @@ export const resourceIdForResourceTemplateCompleter: CompleteResourceTemplateCal return Number.isInteger(resourceId) && resourceId > 0 ? [value] : []; }; +/** + * Validate and convert a value to a positive integer resource ID. + * Shared by resource template callbacks and prompt handlers. + * + * @param value - The value to validate (typically a string from user input) + * @returns The validated resource ID as a positive integer + * @throws {Error} If the value is not a finite positive integer + */ +export const validateResourceId = (value: unknown): number => { + const idx = Number(value); + if (Number.isFinite(idx) && Number.isInteger(idx) && idx > 0) { + return idx; + } + throw new Error( + `Invalid resourceId: ${value}. Must be a finite positive integer.` + ); +}; + const uriBase: string = "demo://resource/dynamic"; const textUriBase: string = `${uriBase}/text`; const blobUriBase: string = `${uriBase}/blob`; @@ -144,11 +162,9 @@ const parseResourceId = (uri: URL, variables: Record) => { ) { throw new Error(uriError); } else { - const idxStr = String((variables as any).resourceId ?? ""); - const idx = Number(idxStr); - if (Number.isFinite(idx) && Number.isInteger(idx) && idx > 0) { - return idx; - } else { + try { + return validateResourceId((variables as any).resourceId); + } catch { throw new Error(uriError); } } diff --git a/src/everything/tools/trigger-elicitation-request-async.ts b/src/everything/tools/trigger-elicitation-request-async.ts index b2d0da7ad8..fc83573bed 100644 --- a/src/everything/tools/trigger-elicitation-request-async.ts +++ b/src/everything/tools/trigger-elicitation-request-async.ts @@ -131,6 +131,8 @@ export const registerTriggerElicitationRequestAsyncTool = ( } const taskId = elicitResponse.task.taskId; + const pollInterval = + elicitResponse.task.pollInterval ?? POLL_INTERVAL; const statusMessages: string[] = []; statusMessages.push(`Task created: ${taskId}`); @@ -146,7 +148,7 @@ export const registerTriggerElicitationRequestAsyncTool = ( attempts < MAX_POLL_ATTEMPTS ) { // Wait before polling - await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)); + await new Promise((resolve) => setTimeout(resolve, pollInterval)); attempts++; // Get task status from client diff --git a/src/everything/tools/trigger-elicitation-request.ts b/src/everything/tools/trigger-elicitation-request.ts index 4de7993e9a..29fb905bda 100644 --- a/src/everything/tools/trigger-elicitation-request.ts +++ b/src/everything/tools/trigger-elicitation-request.ts @@ -187,9 +187,10 @@ export const registerTriggerElicitationRequestTool = (server: McpServer) => { const userData = elicitationResult.content; const lines = []; if (userData.name) lines.push(`- Name: ${userData.name}`); - if (userData.check !== undefined) - lines.push(`- Agreed to terms: ${userData.check}`); - if (userData.color) lines.push(`- Favorite Color: ${userData.color}`); + if (userData.agreeToTerms !== undefined) + lines.push(`- Agreed to terms: ${userData.agreeToTerms}`); + if (userData.firstLine) + lines.push(`- First Line: ${userData.firstLine}`); if (userData.email) lines.push(`- Email: ${userData.email}`); if (userData.homepage) lines.push(`- Homepage: ${userData.homepage}`); if (userData.birthdate) @@ -198,7 +199,24 @@ export const registerTriggerElicitationRequestTool = (server: McpServer) => { lines.push(`- Favorite Integer: ${userData.integer}`); if (userData.number !== undefined) lines.push(`- Favorite Number: ${userData.number}`); - if (userData.petType) lines.push(`- Pet Type: ${userData.petType}`); + if (userData.untitledSingleSelectEnum) + lines.push( + `- Untitled Single Select: ${userData.untitledSingleSelectEnum}` + ); + if (userData.untitledMultipleSelectEnum) + lines.push( + `- Untitled Multiple Select: ${JSON.stringify(userData.untitledMultipleSelectEnum)}` + ); + if (userData.titledSingleSelectEnum) + lines.push( + `- Titled Single Select: ${userData.titledSingleSelectEnum}` + ); + if (userData.titledMultipleSelectEnum) + lines.push( + `- Titled Multiple Select: ${JSON.stringify(userData.titledMultipleSelectEnum)}` + ); + if (userData.legacyTitledEnum) + lines.push(`- Legacy Titled Enum: ${userData.legacyTitledEnum}`); content.push({ type: "text", diff --git a/src/everything/tools/trigger-sampling-request-async.ts b/src/everything/tools/trigger-sampling-request-async.ts index c5e5f1a858..0eca57ee9d 100644 --- a/src/everything/tools/trigger-sampling-request-async.ts +++ b/src/everything/tools/trigger-sampling-request-async.ts @@ -135,6 +135,8 @@ export const registerTriggerSamplingRequestAsyncTool = (server: McpServer) => { } const taskId = samplingResponse.task.taskId; + const pollInterval = + samplingResponse.task.pollInterval ?? POLL_INTERVAL; const statusMessages: string[] = []; statusMessages.push(`Task created: ${taskId}`); @@ -150,7 +152,7 @@ export const registerTriggerSamplingRequestAsyncTool = (server: McpServer) => { attempts < MAX_POLL_ATTEMPTS ) { // Wait before polling - await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)); + await new Promise((resolve) => setTimeout(resolve, pollInterval)); attempts++; // Get task status from client