mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-17 19:54:00 +02:00
single tool for normal and error path
This commit is contained in:
@@ -67,16 +67,15 @@ describe('Registration Index Files', () => {
|
||||
|
||||
registerConditionalTools(mockServerWithCapabilities);
|
||||
|
||||
// Should register 5 conditional tools + 3 task-based tools when all capabilities present
|
||||
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(5);
|
||||
// Should register 4 conditional tools + 3 task-based tools when all capabilities present
|
||||
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(4);
|
||||
|
||||
const registeredTools = (
|
||||
mockServerWithCapabilities.registerTool as any
|
||||
).mock.calls.map((call: any[]) => call[0]);
|
||||
expect(registeredTools).toContain('get-roots-list');
|
||||
expect(registeredTools).toContain('trigger-elicitation-request');
|
||||
expect(registeredTools).toContain('trigger-url-elicitation-request');
|
||||
expect(registeredTools).toContain('trigger-url-elicitation-required-error');
|
||||
expect(registeredTools).toContain('trigger-url-elicitation');
|
||||
expect(registeredTools).toContain('trigger-sampling-request');
|
||||
|
||||
// Task-based tools are registered via experimental.tasks.registerToolTask
|
||||
|
||||
@@ -13,8 +13,7 @@ import { registerToggleSimulatedLoggingTool } from '../tools/toggle-simulated-lo
|
||||
import { registerToggleSubscriberUpdatesTool } from '../tools/toggle-subscriber-updates.js';
|
||||
import { registerTriggerSamplingRequestTool } from '../tools/trigger-sampling-request.js';
|
||||
import { registerTriggerElicitationRequestTool } from '../tools/trigger-elicitation-request.js';
|
||||
import { registerTriggerUrlElicitationRequestTool } from '../tools/trigger-url-elicitation-request.js';
|
||||
import { registerTriggerUrlElicitationRequiredErrorTool } from '../tools/trigger-url-elicitation-required-error.js';
|
||||
import { registerTriggerUrlElicitationTool } from '../tools/trigger-url-elicitation.js';
|
||||
import { registerGetRootsListTool } from '../tools/get-roots-list.js';
|
||||
import { registerGZipFileAsResourceTool } from '../tools/gzip-file-as-resource.js';
|
||||
|
||||
@@ -708,7 +707,7 @@ describe('Tools', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('trigger-url-elicitation-request', () => {
|
||||
describe('trigger-url-elicitation', () => {
|
||||
it('should not register when client does not support URL elicitation', () => {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const mockServer = {
|
||||
@@ -720,7 +719,7 @@ describe('Tools', () => {
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequestTool(mockServer);
|
||||
registerTriggerUrlElicitationTool(mockServer);
|
||||
|
||||
expect(mockServer.registerTool).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -733,28 +732,26 @@ describe('Tools', () => {
|
||||
}),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({ elicitation: { url: {} } })),
|
||||
createElicitationCompletionNotifier: vi.fn(() => vi.fn()),
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequestTool(mockServer);
|
||||
registerTriggerUrlElicitationTool(mockServer);
|
||||
|
||||
expect(mockServer.registerTool).toHaveBeenCalledWith(
|
||||
'trigger-url-elicitation-request',
|
||||
'trigger-url-elicitation',
|
||||
expect.objectContaining({
|
||||
title: 'Trigger URL Elicitation Request Tool',
|
||||
title: 'Trigger URL Elicitation Tool',
|
||||
description: expect.stringContaining('URL elicitation'),
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('should send URL-mode elicitation request and notify completion when requested', async () => {
|
||||
it('should send URL-mode elicitation request when errorPath is false', async () => {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const mockSendRequest = vi.fn().mockResolvedValue({
|
||||
action: 'accept',
|
||||
});
|
||||
const mockNotifyComplete = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
const mockServer = {
|
||||
registerTool: vi.fn((name: string, config: any, handler: Function) => {
|
||||
@@ -762,21 +759,18 @@ describe('Tools', () => {
|
||||
}),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({ elicitation: { url: {} } })),
|
||||
createElicitationCompletionNotifier: vi
|
||||
.fn()
|
||||
.mockReturnValue(mockNotifyComplete),
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequestTool(mockServer);
|
||||
registerTriggerUrlElicitationTool(mockServer);
|
||||
|
||||
const handler = handlers.get('trigger-url-elicitation-request')!;
|
||||
const handler = handlers.get('trigger-url-elicitation')!;
|
||||
const result = await handler(
|
||||
{
|
||||
url: 'https://example.com/verify',
|
||||
message: 'Open this page to verify your identity',
|
||||
elicitationId: 'elicitation-123',
|
||||
sendCompletionNotification: true,
|
||||
errorPath: false,
|
||||
},
|
||||
{ sendRequest: mockSendRequest }
|
||||
);
|
||||
@@ -795,32 +789,10 @@ describe('Tools', () => {
|
||||
expect.anything()
|
||||
);
|
||||
|
||||
expect(mockServer.server.createElicitationCompletionNotifier).toHaveBeenCalledWith(
|
||||
'elicitation-123'
|
||||
);
|
||||
expect(mockNotifyComplete).toHaveBeenCalledTimes(1);
|
||||
expect(result.content[0].text).toContain('URL elicitation action: accept');
|
||||
});
|
||||
});
|
||||
|
||||
describe('trigger-url-elicitation-required-error', () => {
|
||||
it('should not register when client does not support URL elicitation', () => {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const mockServer = {
|
||||
registerTool: vi.fn((name: string, config: any, handler: Function) => {
|
||||
handlers.set(name, handler);
|
||||
}),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({ elicitation: { form: {} } })),
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequiredErrorTool(mockServer);
|
||||
|
||||
expect(mockServer.registerTool).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should register when client supports URL elicitation', () => {
|
||||
it('should throw MCP error -32042 with required URL elicitation data when errorPath is true', async () => {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const mockServer = {
|
||||
registerTool: vi.fn((name: string, config: any, handler: Function) => {
|
||||
@@ -831,40 +803,22 @@ describe('Tools', () => {
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequiredErrorTool(mockServer);
|
||||
registerTriggerUrlElicitationTool(mockServer);
|
||||
|
||||
expect(mockServer.registerTool).toHaveBeenCalledWith(
|
||||
'trigger-url-elicitation-required-error',
|
||||
expect.objectContaining({
|
||||
title: 'Trigger URL Elicitation Required Error Tool',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw MCP error -32042 with required URL elicitation data', async () => {
|
||||
const handlers: Map<string, Function> = new Map();
|
||||
const mockServer = {
|
||||
registerTool: vi.fn((name: string, config: any, handler: Function) => {
|
||||
handlers.set(name, handler);
|
||||
}),
|
||||
server: {
|
||||
getClientCapabilities: vi.fn(() => ({ elicitation: { url: {} } })),
|
||||
},
|
||||
} as unknown as McpServer;
|
||||
|
||||
registerTriggerUrlElicitationRequiredErrorTool(mockServer);
|
||||
|
||||
const handler = handlers.get('trigger-url-elicitation-required-error')!;
|
||||
const handler = handlers.get('trigger-url-elicitation')!;
|
||||
|
||||
expect.assertions(2);
|
||||
|
||||
try {
|
||||
await handler({
|
||||
url: 'https://example.com/connect',
|
||||
message: 'Authorization is required to continue.',
|
||||
elicitationId: 'elicitation-xyz',
|
||||
});
|
||||
await handler(
|
||||
{
|
||||
url: 'https://example.com/connect',
|
||||
message: 'Authorization is required to continue.',
|
||||
elicitationId: 'elicitation-xyz',
|
||||
errorPath: true,
|
||||
},
|
||||
{}
|
||||
);
|
||||
} catch (error: any) {
|
||||
expect(error.code).toBe(-32042);
|
||||
expect(error.data.elicitations[0]).toEqual({
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
- `get-structured-content` (tools/get-structured-content.ts): Demonstrates structured responses. Accepts `location` input and returns both backward‑compatible `content` (a `text` block containing JSON) and `structuredContent` validated by an `outputSchema` (temperature, conditions, humidity).
|
||||
- `get-sum` (tools/get-sum.ts): For two numbers `a` and `b` calculates and returns their sum. Uses Zod to validate inputs.
|
||||
- `get-tiny-image` (tools/get-tiny-image.ts): Returns a tiny PNG MCP logo as an `image` content item with brief descriptive text before and after.
|
||||
- `trigger-long-running-operation` (tools/trigger-trigger-long-running-operation.ts): Simulates a multi-step operation over a given `duration` and number of `steps`; reports progress via `notifications/progress` when a `progressToken` is provided by the client.
|
||||
- `trigger-long-running-operation` (tools/trigger-long-running-operation.ts): Simulates a multi-step operation over a given `duration` and number of `steps`; reports progress via `notifications/progress` when a `progressToken` is provided by the client.
|
||||
- `toggle-simulated-logging` (tools/toggle-simulated-logging.ts): Starts or stops simulated, random‑leveled logging for the invoking session. Respects the client’s selected minimum logging level.
|
||||
- `toggle-subscriber-updates` (tools/toggle-subscriber-updates.ts): Starts or stops simulated resource update notifications for URIs the invoking session has subscribed to.
|
||||
- `trigger-elicitation-request` (tools/trigger-elicitation-request.ts): Issues an `elicitation/create` request using form-mode fields (strings, numbers, booleans, enums, and format validation) and returns the resulting action/content.
|
||||
- `trigger-url-elicitation-request` (tools/trigger-url-elicitation-request.ts): Issues an `elicitation/create` request in URL mode (`mode: "url"`) with an `elicitationId`, and can optionally emit `notifications/elicitation/complete` after acceptance. Requires client capability `elicitation.url`.
|
||||
- `trigger-url-elicitation-required-error` (tools/trigger-url-elicitation-required-error.ts): Throws MCP error `-32042` (`UrlElicitationRequiredError`) with one or more required URL-mode elicitations in `error.data.elicitations`, demonstrating the retry-after-elicitation flow.
|
||||
- `trigger-url-elicitation` (tools/trigger-url-elicitation.ts): Issues an `elicitation/create` request in URL mode (`mode: "url"`) with an `elicitationId`, or throws MCP error `-32042` (`UrlElicitationRequiredError`) when `errorPath=true`. Requires client capability `elicitation.url`.
|
||||
- `trigger-sampling-request` (tools/trigger-sampling-request.ts): Issues a `sampling/createMessage` request to the client/LLM using provided `prompt` and optional generation controls; returns the LLM's response payload.
|
||||
- `simulate-research-query` (tools/simulate-research-query.ts): Demonstrates MCP Tasks (SEP-1686) with a simulated multi-stage research operation. Accepts `topic` and `ambiguous` parameters. Returns a task that progresses through stages with status updates. If `ambiguous` is true and client supports elicitation, sends an elicitation request directly to gather clarification before completing.
|
||||
- `trigger-sampling-request-async` (tools/trigger-sampling-request-async.ts): Demonstrates bidirectional tasks where the server sends a sampling request that the client executes as a background task. Server polls for status and retrieves the LLM result when complete. Requires client to support `tasks.requests.sampling.createMessage`.
|
||||
|
||||
@@ -48,13 +48,15 @@ src/everything
|
||||
│ ├── get-sum.ts
|
||||
│ ├── get-tiny-image.ts
|
||||
│ ├── gzip-file-as-resource.ts
|
||||
│ ├── simulate-research-query.ts
|
||||
│ ├── toggle-simulated-logging.ts
|
||||
│ ├── toggle-subscriber-updates.ts
|
||||
│ ├── trigger-elicitation-request.ts
|
||||
│ ├── trigger-elicitation-request-async.ts
|
||||
│ ├── trigger-long-running-operation.ts
|
||||
│ ├── trigger-url-elicitation-required-error.ts
|
||||
│ ├── trigger-url-elicitation-request.ts
|
||||
│ └── trigger-sampling-request.ts
|
||||
│ ├── trigger-sampling-request.ts
|
||||
│ ├── trigger-sampling-request-async.ts
|
||||
│ └── trigger-url-elicitation.ts
|
||||
└── transports
|
||||
├── sse.ts
|
||||
├── stdio.ts
|
||||
@@ -84,8 +86,8 @@ src/everything
|
||||
|
||||
- `architecture.md`
|
||||
- This document.
|
||||
- `server-instructions.md`
|
||||
- Human‑readable instructions intended to be passed to the client/LLM as for guidance on server use. Loaded by the server at startup and returned in the "initialize" exchange.
|
||||
- `instructions.md`
|
||||
- Human‑readable instructions intended to be passed to the client/LLM as guidance on server use. Loaded by the server at startup and returned in the initialize exchange.
|
||||
|
||||
### `prompts/`
|
||||
|
||||
@@ -151,12 +153,16 @@ src/everything
|
||||
- `GZIP_ALLOWED_DOMAINS` (comma-separated allowlist; empty means all domains allowed)
|
||||
- `trigger-elicitation-request.ts`
|
||||
- Registers a `trigger-elicitation-request` tool that sends an `elicitation/create` request to the client/LLM and returns the elicitation result.
|
||||
- `trigger-url-elicitation-request.ts`
|
||||
- Registers a `trigger-url-elicitation-request` tool that sends an out-of-band URL-mode `elicitation/create` request (`mode: "url"`) including an `elicitationId`, and can optionally emit `notifications/elicitation/complete` after acceptance.
|
||||
- `trigger-url-elicitation-required-error.ts`
|
||||
- Registers a `trigger-url-elicitation-required-error` tool that throws MCP error `-32042` (`UrlElicitationRequiredError`) with required URL-mode elicitation params in `error.data.elicitations`.
|
||||
- `trigger-url-elicitation.ts`
|
||||
- Registers a `trigger-url-elicitation` tool that either sends an out-of-band URL-mode `elicitation/create` request (`mode: "url"`) including an `elicitationId` (request path) or throws `UrlElicitationRequiredError` (`-32042`) for client-handled URL elicitation (error path).
|
||||
- `trigger-sampling-request.ts`
|
||||
- Registers a `trigger-sampling-request` tool that sends a `sampling/createMessage` request to the client/LLM and returns the sampling result.
|
||||
- `trigger-sampling-request-async.ts`
|
||||
- Registers a `trigger-sampling-request-async` tool that demonstrates bidirectional tasks where the server sends a sampling request that the client executes asynchronously, then polls task status and fetches the result.
|
||||
- `trigger-elicitation-request-async.ts`
|
||||
- Registers a `trigger-elicitation-request-async` tool that demonstrates bidirectional tasks where the server sends an elicitation request that the client executes asynchronously, then polls task status and fetches the result.
|
||||
- `simulate-research-query.ts`
|
||||
- Registers a `simulate-research-query` tool that demonstrates server-side MCP Tasks with staged progress updates and optional elicitation for ambiguous queries.
|
||||
- `get-structured-content.ts`
|
||||
- Registers a `get-structured-content` tool that demonstrates structuredContent block responses.
|
||||
- `get-sum.ts`
|
||||
@@ -164,7 +170,7 @@ src/everything
|
||||
- `get-tiny-image.ts`
|
||||
- Registers a `get-tiny-image` tool, which returns a tiny PNG MCP logo as an `image` content item, along with surrounding descriptive `text` items.
|
||||
- `trigger-long-running-operation.ts`
|
||||
- Registers a `long-running-operation` tool that simulates a long-running task over a specified `duration` (seconds) and number of `steps`; emits `notifications/progress` updates when the client supplies a `progressToken`.
|
||||
- Registers a `trigger-long-running-operation` tool that simulates a long-running task over a specified `duration` (seconds) and number of `steps`; emits `notifications/progress` updates when the client supplies a `progressToken`.
|
||||
- `toggle-simulated-logging.ts`
|
||||
- Registers a `toggle-simulated-logging` tool, which starts or stops simulated logging for the invoking session.
|
||||
- `toggle-subscriber-updates.ts`
|
||||
|
||||
1338
src/everything/pnpm-lock.yaml
generated
Normal file
1338
src/everything/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,8 +17,7 @@ import { registerTriggerSamplingRequestTool } from "./trigger-sampling-request.j
|
||||
import { registerTriggerSamplingRequestAsyncTool } from "./trigger-sampling-request-async.js";
|
||||
import { registerTriggerElicitationRequestAsyncTool } from "./trigger-elicitation-request-async.js";
|
||||
import { registerSimulateResearchQueryTool } from "./simulate-research-query.js";
|
||||
import { registerTriggerUrlElicitationRequestTool } from "./trigger-url-elicitation-request.js";
|
||||
import { registerTriggerUrlElicitationRequiredErrorTool } from "./trigger-url-elicitation-required-error.js";
|
||||
import { registerTriggerUrlElicitationTool } from "./trigger-url-elicitation.js";
|
||||
|
||||
/**
|
||||
* Register the tools with the MCP server.
|
||||
@@ -46,8 +45,7 @@ export const registerTools = (server: McpServer) => {
|
||||
export const registerConditionalTools = (server: McpServer) => {
|
||||
registerGetRootsListTool(server);
|
||||
registerTriggerElicitationRequestTool(server);
|
||||
registerTriggerUrlElicitationRequestTool(server);
|
||||
registerTriggerUrlElicitationRequiredErrorTool(server);
|
||||
registerTriggerUrlElicitationTool(server);
|
||||
registerTriggerSamplingRequestTool(server);
|
||||
// Task-based research tool (uses experimental tasks API)
|
||||
registerSimulateResearchQueryTool(server);
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import {
|
||||
CallToolResult,
|
||||
ElicitRequestURLParams,
|
||||
UrlElicitationRequiredError,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
|
||||
// Tool input schema
|
||||
const TriggerUrlElicitationRequiredErrorSchema = z.object({
|
||||
url: z.string().url().describe("The URL the user should open"),
|
||||
message: z
|
||||
.string()
|
||||
.default("This request requires more information.")
|
||||
.describe("Message shown to the user for the URL elicitation"),
|
||||
elicitationId: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Optional explicit elicitation ID. Defaults to a random UUID."),
|
||||
});
|
||||
|
||||
// Tool configuration
|
||||
const name = "trigger-url-elicitation-required-error";
|
||||
const config = {
|
||||
title: "Trigger URL Elicitation Required Error Tool",
|
||||
description:
|
||||
"Returns MCP error -32042 (URL elicitation required) so clients can handle URL-mode elicitations via the error path.",
|
||||
inputSchema: TriggerUrlElicitationRequiredErrorSchema,
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers the 'trigger-url-elicitation-required-error' tool.
|
||||
*
|
||||
* This tool demonstrates the MCP error path for URL elicitation by throwing
|
||||
* UrlElicitationRequiredError (code -32042) from a tool handler.
|
||||
*
|
||||
* @param {McpServer} server - The McpServer instance where the tool will be registered.
|
||||
*/
|
||||
export const registerTriggerUrlElicitationRequiredErrorTool = (
|
||||
server: McpServer
|
||||
) => {
|
||||
const clientCapabilities = server.server.getClientCapabilities() || {};
|
||||
const clientElicitationCapabilities = clientCapabilities.elicitation as
|
||||
| {
|
||||
url?: object;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
const clientSupportsUrlElicitation =
|
||||
clientElicitationCapabilities?.url !== undefined;
|
||||
|
||||
if (clientSupportsUrlElicitation) {
|
||||
server.registerTool(
|
||||
name,
|
||||
config,
|
||||
async (args): Promise<CallToolResult> => {
|
||||
const validatedArgs = TriggerUrlElicitationRequiredErrorSchema.parse(args);
|
||||
const { url, message, elicitationId: requestedElicitationId } =
|
||||
validatedArgs;
|
||||
|
||||
const elicitationId = requestedElicitationId ?? randomUUID();
|
||||
|
||||
const requiredElicitation: ElicitRequestURLParams = {
|
||||
mode: "url",
|
||||
url,
|
||||
message,
|
||||
elicitationId,
|
||||
};
|
||||
|
||||
throw new UrlElicitationRequiredError(
|
||||
[requiredElicitation],
|
||||
"This request requires more information."
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,11 +4,12 @@ import {
|
||||
CallToolResult,
|
||||
ElicitRequestURLParams,
|
||||
ElicitResultSchema,
|
||||
UrlElicitationRequiredError,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
|
||||
// Tool input schema
|
||||
const TriggerUrlElicitationRequestSchema = z.object({
|
||||
const TriggerUrlElicitationSchema = z.object({
|
||||
url: z.string().url().describe("The URL the user should open"),
|
||||
message: z
|
||||
.string()
|
||||
@@ -18,32 +19,43 @@ const TriggerUrlElicitationRequestSchema = z.object({
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Optional explicit elicitation ID. Defaults to a random UUID."),
|
||||
sendCompletionNotification: z
|
||||
errorPath: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe(
|
||||
"If true, sends notifications/elicitation/complete after an accepted URL elicitation."
|
||||
"Controls which elicitation mechanism is used. " +
|
||||
"When false (default), sends an elicitation/create request (request path). " +
|
||||
"When true, throws a UrlElicitationRequiredError (MCP error code -32042) so the client handles " +
|
||||
"the URL elicitation via the error path rather than waiting for a response."
|
||||
),
|
||||
});
|
||||
|
||||
// Tool configuration
|
||||
const name = "trigger-url-elicitation-request";
|
||||
const name = "trigger-url-elicitation";
|
||||
const config = {
|
||||
title: "Trigger URL Elicitation Request Tool",
|
||||
title: "Trigger URL Elicitation Tool",
|
||||
description:
|
||||
"Trigger an out-of-band URL elicitation request so the client can direct the user to a browser flow.",
|
||||
inputSchema: TriggerUrlElicitationRequestSchema,
|
||||
"Trigger a URL elicitation so the client can direct the user to a browser flow. " +
|
||||
"Supports two mechanisms: the request path (elicitation/create, default) which awaits the user's " +
|
||||
"response, and the error path (UrlElicitationRequiredError, -32042) which signals the client " +
|
||||
"to handle URL elicitation via the error response. Set errorPath=true to use the error path.",
|
||||
inputSchema: TriggerUrlElicitationSchema,
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers the 'trigger-url-elicitation-request' tool.
|
||||
* Registers the 'trigger-url-elicitation' tool.
|
||||
*
|
||||
* This tool only registers when the client advertises URL-mode elicitation
|
||||
* capability (clientCapabilities.elicitation.url).
|
||||
*
|
||||
* Depending on the `errorPath` argument it either:
|
||||
* - Sends an `elicitation/create` request and awaits the result (request path), or
|
||||
* - Throws a `UrlElicitationRequiredError` (MCP error -32042) for the client to
|
||||
* handle via the error path.
|
||||
*
|
||||
* @param {McpServer} server - The McpServer instance where the tool will be registered.
|
||||
*/
|
||||
export const registerTriggerUrlElicitationRequestTool = (server: McpServer) => {
|
||||
export const registerTriggerUrlElicitationTool = (server: McpServer) => {
|
||||
const clientCapabilities = server.server.getClientCapabilities() || {};
|
||||
const clientElicitationCapabilities = clientCapabilities.elicitation as
|
||||
| {
|
||||
@@ -59,27 +71,36 @@ export const registerTriggerUrlElicitationRequestTool = (server: McpServer) => {
|
||||
name,
|
||||
config,
|
||||
async (args, extra): Promise<CallToolResult> => {
|
||||
const validatedArgs = TriggerUrlElicitationRequestSchema.parse(args);
|
||||
const validatedArgs = TriggerUrlElicitationSchema.parse(args);
|
||||
const {
|
||||
url,
|
||||
message,
|
||||
elicitationId: requestedElicitationId,
|
||||
sendCompletionNotification,
|
||||
errorPath,
|
||||
} = validatedArgs;
|
||||
|
||||
const elicitationId = requestedElicitationId ?? randomUUID();
|
||||
|
||||
const params: ElicitRequestURLParams = {
|
||||
const elicitationParams: ElicitRequestURLParams = {
|
||||
mode: "url",
|
||||
message,
|
||||
url,
|
||||
message,
|
||||
elicitationId,
|
||||
};
|
||||
|
||||
// Error path: throw UrlElicitationRequiredError (-32042) for the client to handle
|
||||
if (errorPath) {
|
||||
throw new UrlElicitationRequiredError(
|
||||
[elicitationParams],
|
||||
"This request requires browser-based authorization."
|
||||
);
|
||||
}
|
||||
|
||||
// Request path: send elicitation/create and await the user's response
|
||||
const elicitationResult = await extra.sendRequest(
|
||||
{
|
||||
method: "elicitation/create",
|
||||
params,
|
||||
params: elicitationParams,
|
||||
},
|
||||
ElicitResultSchema,
|
||||
{ timeout: 10 * 60 * 1000 /* 10 minutes */ }
|
||||
@@ -95,19 +116,6 @@ export const registerTriggerUrlElicitationRequestTool = (server: McpServer) => {
|
||||
},
|
||||
];
|
||||
|
||||
if (
|
||||
sendCompletionNotification &&
|
||||
elicitationResult.action === "accept"
|
||||
) {
|
||||
const notifyElicitationComplete =
|
||||
server.server.createElicitationCompletionNotifier(elicitationId);
|
||||
await notifyElicitationComplete();
|
||||
content.push({
|
||||
type: "text",
|
||||
text: `Sent notifications/elicitation/complete for ${elicitationId}.`,
|
||||
});
|
||||
}
|
||||
|
||||
content.push({
|
||||
type: "text",
|
||||
text: `Raw result: ${JSON.stringify(elicitationResult, null, 2)}`,
|
||||
@@ -118,4 +126,3 @@ export const registerTriggerUrlElicitationRequestTool = (server: McpServer) => {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user