Skip to content

Commit 3953dc4

Browse files
committed
feat(everything): add URL elicitation example (SEP-1036)
Add a trigger-url-elicitation-request tool to the Everything server that demonstrates SEP-1036 URL Elicitation. The tool sends an elicitation/create request with mode "url", asking the client to navigate the user to an external URL for out-of-band interaction (e.g., third-party OAuth authorization). The tool is conditionally registered only when the client declares elicitation.url capability, consistent with existing elicitation tools. Closes #3034
1 parent f424458 commit 3953dc4

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

src/everything/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { registerTriggerLongRunningOperationTool } from "./trigger-long-running-
1616
import { registerTriggerSamplingRequestTool } from "./trigger-sampling-request.js";
1717
import { registerTriggerSamplingRequestAsyncTool } from "./trigger-sampling-request-async.js";
1818
import { registerTriggerElicitationRequestAsyncTool } from "./trigger-elicitation-request-async.js";
19+
import { registerTriggerUrlElicitationRequestTool } from "./trigger-url-elicitation-request.js";
1920
import { registerSimulateResearchQueryTool } from "./simulate-research-query.js";
2021

2122
/**
@@ -50,4 +51,5 @@ export const registerConditionalTools = (server: McpServer) => {
5051
// Bidirectional task tools - server sends requests that client executes as tasks
5152
registerTriggerSamplingRequestAsyncTool(server);
5253
registerTriggerElicitationRequestAsyncTool(server);
54+
registerTriggerUrlElicitationRequestTool(server);
5355
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import {
3+
ElicitResultSchema,
4+
CallToolResult,
5+
} from "@modelcontextprotocol/sdk/types.js";
6+
7+
// Tool configuration
8+
const name = "trigger-url-elicitation-request";
9+
const config = {
10+
title: "Trigger URL Elicitation Request Tool",
11+
description:
12+
"Trigger a URL elicitation request (SEP-1036) that asks the client to " +
13+
"navigate the user to an external URL for out-of-band interaction, such " +
14+
"as OAuth authorization or payment flows.",
15+
inputSchema: {},
16+
};
17+
18+
/**
19+
* Registers the 'trigger-url-elicitation-request' tool.
20+
*
21+
* This tool demonstrates SEP-1036 URL Elicitation, where the server requests
22+
* the client to open an external URL for the user. This is used for flows
23+
* where sensitive data (credentials, payment info) must not transit through
24+
* the MCP client, such as third-party OAuth authorization.
25+
*
26+
* The client responds with accept (user consented to navigate), decline,
27+
* or cancel. The actual interaction happens out-of-band in the browser.
28+
*
29+
* Requires the client to declare `elicitation.url` capability.
30+
*
31+
* @param {McpServer} server - The McpServer instance where the tool will be registered.
32+
*/
33+
export const registerTriggerUrlElicitationRequestTool = (
34+
server: McpServer
35+
) => {
36+
const clientCapabilities = server.server.getClientCapabilities() || {};
37+
const elicitationCapability = clientCapabilities.elicitation as
38+
| { url?: object }
39+
| undefined;
40+
const clientSupportsUrlElicitation = elicitationCapability?.url !== undefined;
41+
42+
if (clientSupportsUrlElicitation) {
43+
server.registerTool(
44+
name,
45+
config,
46+
async (args, extra): Promise<CallToolResult> => {
47+
const elicitationId = crypto.randomUUID();
48+
const elicitationResult = await extra.sendRequest(
49+
{
50+
method: "elicitation/create",
51+
params: {
52+
mode: "url",
53+
elicitationId,
54+
message:
55+
"Please authorize access to your GitHub repositories. " +
56+
"You will be redirected to GitHub to complete the authorization.",
57+
url: "https://github.com/login/oauth/authorize?client_id=EXAMPLE_CLIENT_ID&scope=repo&state=example-state",
58+
},
59+
},
60+
ElicitResultSchema,
61+
{ timeout: 10 * 60 * 1000 /* 10 minutes */ }
62+
);
63+
64+
const content: CallToolResult["content"] = [];
65+
66+
if (elicitationResult.action === "accept") {
67+
content.push({
68+
type: "text",
69+
text: "✅ User accepted the URL elicitation request and was directed to the external URL.",
70+
});
71+
} else if (elicitationResult.action === "decline") {
72+
content.push({
73+
type: "text",
74+
text: "❌ User declined to navigate to the external URL.",
75+
});
76+
} else if (elicitationResult.action === "cancel") {
77+
content.push({
78+
type: "text",
79+
text: "⚠️ User cancelled the URL elicitation dialog.",
80+
});
81+
}
82+
83+
content.push({
84+
type: "text",
85+
text: `\nRaw result: ${JSON.stringify(elicitationResult, null, 2)}`,
86+
});
87+
88+
return { content };
89+
}
90+
);
91+
}
92+
};

0 commit comments

Comments
 (0)