@mcpflo/server-everything 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/dist/createServer.js +42 -0
- package/dist/docs/architecture.md +19 -0
- package/dist/docs/extension.md +20 -0
- package/dist/docs/features.md +23 -0
- package/dist/docs/how-it-works.md +22 -0
- package/dist/docs/instructions.md +16 -0
- package/dist/docs/startup.md +20 -0
- package/dist/docs/structure.md +21 -0
- package/dist/index.js +10 -0
- package/dist/prompts/args.js +19 -0
- package/dist/prompts/completions.js +32 -0
- package/dist/prompts/index.js +19 -0
- package/dist/prompts/resource.js +35 -0
- package/dist/prompts/simple.js +16 -0
- package/dist/resources/docsDir.js +18 -0
- package/dist/resources/file-resources.js +52 -0
- package/dist/resources/index.js +24 -0
- package/dist/resources/session.js +34 -0
- package/dist/resources/subscriptions.js +38 -0
- package/dist/resources/templates.js +91 -0
- package/dist/server/logging.js +45 -0
- package/dist/server/roots.js +44 -0
- package/dist/tools/add.js +12 -0
- package/dist/tools/annotated-message.js +67 -0
- package/dist/tools/echo.js +12 -0
- package/dist/tools/get-resource-links.js +36 -0
- package/dist/tools/get-resource-reference.js +32 -0
- package/dist/tools/get-roots-list.js +51 -0
- package/dist/tools/get-structured-content.js +35 -0
- package/dist/tools/get-tiny-image.js +23 -0
- package/dist/tools/gzip-file-as-resource.js +115 -0
- package/dist/tools/index.js +63 -0
- package/dist/tools/print-env.js +10 -0
- package/dist/tools/simulate-research-query.js +173 -0
- package/dist/tools/toggle-simulated-logging.js +33 -0
- package/dist/tools/toggle-subscriber-updates.js +33 -0
- package/dist/tools/trigger-elicitation-request-async.js +136 -0
- package/dist/tools/trigger-elicitation-request.js +167 -0
- package/dist/tools/trigger-long-running-operation.js +36 -0
- package/dist/tools/trigger-sampling-request-async.js +112 -0
- package/dist/tools/trigger-sampling-request.js +39 -0
- package/dist/tools/trigger-url-elicitation.js +0 -0
- package/package.json +36 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerSimulateResearchQuery = registerSimulateResearchQuery;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
6
|
+
const STAGES = ['Gathering sources', 'Analyzing content', 'Synthesizing findings', 'Generating report'];
|
|
7
|
+
const STAGE_DURATION = 1000;
|
|
8
|
+
const researchStates = new Map();
|
|
9
|
+
async function runResearchProcess(taskId, taskStore, sendRequest) {
|
|
10
|
+
const state = researchStates.get(taskId);
|
|
11
|
+
if (!state)
|
|
12
|
+
return;
|
|
13
|
+
for (let i = state.currentStage; i < STAGES.length; i++) {
|
|
14
|
+
state.currentStage = i;
|
|
15
|
+
if (state.completed)
|
|
16
|
+
return;
|
|
17
|
+
await taskStore.updateTaskStatus(taskId, 'working', `${STAGES[i]}...`);
|
|
18
|
+
if (i === 2 && state.ambiguous && !state.clarification) {
|
|
19
|
+
await taskStore.updateTaskStatus(taskId, 'input_required', `Found multiple interpretations for "${state.topic}". Requesting clarification...`);
|
|
20
|
+
try {
|
|
21
|
+
const elicitResult = (await sendRequest({
|
|
22
|
+
method: 'elicitation/create',
|
|
23
|
+
params: {
|
|
24
|
+
message: `The research query "${state.topic}" could have multiple interpretations. Please clarify what you're looking for:`,
|
|
25
|
+
requestedSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
interpretation: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
title: 'Clarification',
|
|
31
|
+
description: 'Which interpretation of the topic do you mean?',
|
|
32
|
+
oneOf: getInterpretationsForTopic(state.topic)
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
required: ['interpretation']
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}, types_js_1.ElicitResultSchema));
|
|
39
|
+
if (elicitResult.action === 'accept' && elicitResult.content) {
|
|
40
|
+
state.clarification =
|
|
41
|
+
elicitResult.content.interpretation ||
|
|
42
|
+
'User accepted without selection';
|
|
43
|
+
}
|
|
44
|
+
else if (elicitResult.action === 'decline') {
|
|
45
|
+
state.clarification = 'User declined - using default interpretation';
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
state.clarification = 'User cancelled - using default interpretation';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.warn(`Elicitation failed for task ${taskId}:`, error instanceof Error ? error.message : String(error));
|
|
53
|
+
state.clarification = 'technical (default - elicitation unavailable)';
|
|
54
|
+
}
|
|
55
|
+
await taskStore.updateTaskStatus(taskId, 'working', `Continuing with interpretation: "${state.clarification}"...`);
|
|
56
|
+
}
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, STAGE_DURATION));
|
|
58
|
+
}
|
|
59
|
+
state.completed = true;
|
|
60
|
+
const result = generateResearchReport(state);
|
|
61
|
+
state.result = result;
|
|
62
|
+
await taskStore.storeTaskResult(taskId, 'completed', result);
|
|
63
|
+
}
|
|
64
|
+
function generateResearchReport(state) {
|
|
65
|
+
const topic = state.clarification ? `${state.topic} (${state.clarification})` : state.topic;
|
|
66
|
+
const report = `# Research Report: ${topic}
|
|
67
|
+
|
|
68
|
+
## Research Parameters
|
|
69
|
+
- **Topic**: ${state.topic}
|
|
70
|
+
${state.clarification ? `- **Clarification**: ${state.clarification}` : ''}
|
|
71
|
+
|
|
72
|
+
## Synthesis
|
|
73
|
+
This research query was processed through ${STAGES.length} stages:
|
|
74
|
+
${STAGES.map((s, i) => `- Stage ${i + 1}: ${s} ✓`).join('\n')}
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## About This Demo (SEP-1686: Tasks)
|
|
79
|
+
|
|
80
|
+
This tool demonstrates MCP's task-based execution pattern for long-running operations:
|
|
81
|
+
|
|
82
|
+
**Task Lifecycle Demonstrated:**
|
|
83
|
+
1. \`tools/call\` with \`task\` parameter → Server returns \`CreateTaskResult\` (not the final result)
|
|
84
|
+
2. Client polls \`tasks/get\` → Server returns current status and \`statusMessage\`
|
|
85
|
+
3. Status progressed: \`working\` → ${state.clarification ? '`input_required` → `working` → ' : ''}\`completed\`
|
|
86
|
+
4. Client calls \`tasks/result\` → Server returns this final result
|
|
87
|
+
|
|
88
|
+
${state.clarification
|
|
89
|
+
? `**Elicitation Flow:**
|
|
90
|
+
When the query was ambiguous, the server sent an \`elicitation/create\` request
|
|
91
|
+
to the client. The task status changed to \`input_required\` while awaiting user input.
|
|
92
|
+
${state.clarification.includes('unavailable')
|
|
93
|
+
? '**Note:** Elicitation failed and a default interpretation was used.'
|
|
94
|
+
: `After receiving clarification ("${state.clarification}"), the task resumed processing and completed.`}
|
|
95
|
+
`
|
|
96
|
+
: ''}
|
|
97
|
+
**Key Concepts:**
|
|
98
|
+
- Tasks enable "call now, fetch later" patterns
|
|
99
|
+
- \`statusMessage\` provides human-readable progress updates
|
|
100
|
+
- Tasks have TTL (time-to-live) for automatic cleanup
|
|
101
|
+
- \`pollInterval\` suggests how often to check status
|
|
102
|
+
- Elicitation requests use \`relatedTask\` to queue via tasks/result (works on all transports)
|
|
103
|
+
|
|
104
|
+
*This is a simulated research report. Demo/test fixture.*
|
|
105
|
+
`;
|
|
106
|
+
return { content: [{ type: 'text', text: report }] };
|
|
107
|
+
}
|
|
108
|
+
function getInterpretationsForTopic(topic) {
|
|
109
|
+
const lowerTopic = topic.toLowerCase();
|
|
110
|
+
if (lowerTopic.includes('python')) {
|
|
111
|
+
return [
|
|
112
|
+
{ const: 'programming', title: 'Python programming language' },
|
|
113
|
+
{ const: 'snake', title: 'Python snake species' },
|
|
114
|
+
{ const: 'comedy', title: 'Monty Python comedy group' }
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
return [
|
|
118
|
+
{ const: 'technical', title: 'Technical/scientific perspective' },
|
|
119
|
+
{ const: 'historical', title: 'Historical perspective' },
|
|
120
|
+
{ const: 'current', title: 'Current events/news perspective' }
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
function registerSimulateResearchQuery(server) {
|
|
124
|
+
// Called from server.server.oninitialized (see index.ts), so the client's
|
|
125
|
+
// capabilities are already known here — computed once, not per-call, since
|
|
126
|
+
// capabilities don't change for the life of a session.
|
|
127
|
+
const clientCapabilities = server.server.getClientCapabilities() ?? {};
|
|
128
|
+
const clientSupportsElicitation = clientCapabilities.elicitation !== undefined;
|
|
129
|
+
server.experimental.tasks.registerToolTask('simulate-research-query', {
|
|
130
|
+
description: 'Simulates a deep research operation that gathers, analyzes, and synthesizes information. ' +
|
|
131
|
+
"Demonstrates MCP task-based operations with progress through multiple stages. If 'ambiguous' " +
|
|
132
|
+
'is true and the client supports elicitation, sends an elicitation request for clarification. ' +
|
|
133
|
+
'Demo/test fixture.',
|
|
134
|
+
inputSchema: {
|
|
135
|
+
topic: zod_1.z.string().describe('The research topic to investigate'),
|
|
136
|
+
ambiguous: zod_1.z
|
|
137
|
+
.boolean()
|
|
138
|
+
.default(false)
|
|
139
|
+
.describe('Simulate an ambiguous query that requires clarification (triggers input_required status)')
|
|
140
|
+
},
|
|
141
|
+
execution: { taskSupport: 'required' },
|
|
142
|
+
annotations: {
|
|
143
|
+
readOnlyHint: false,
|
|
144
|
+
destructiveHint: false,
|
|
145
|
+
idempotentHint: false,
|
|
146
|
+
openWorldHint: false
|
|
147
|
+
}
|
|
148
|
+
}, {
|
|
149
|
+
createTask: async (args, extra) => {
|
|
150
|
+
const task = await extra.taskStore.createTask({ ttl: 300000, pollInterval: 1000 });
|
|
151
|
+
const state = {
|
|
152
|
+
topic: args.topic,
|
|
153
|
+
ambiguous: args.ambiguous && clientSupportsElicitation,
|
|
154
|
+
currentStage: 0,
|
|
155
|
+
completed: false
|
|
156
|
+
};
|
|
157
|
+
researchStates.set(task.taskId, state);
|
|
158
|
+
runResearchProcess(task.taskId, extra.taskStore, extra.sendRequest).catch((error) => {
|
|
159
|
+
console.error(`Research task ${task.taskId} failed:`, error);
|
|
160
|
+
extra.taskStore.updateTaskStatus(task.taskId, 'failed', String(error)).catch(console.error);
|
|
161
|
+
});
|
|
162
|
+
return { task };
|
|
163
|
+
},
|
|
164
|
+
getTask: async (_args, extra) => {
|
|
165
|
+
return await extra.taskStore.getTask(extra.taskId);
|
|
166
|
+
},
|
|
167
|
+
getTaskResult: async (_args, extra) => {
|
|
168
|
+
const result = await extra.taskStore.getTaskResult(extra.taskId);
|
|
169
|
+
researchStates.delete(extra.taskId);
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerToggleSimulatedLogging = registerToggleSimulatedLogging;
|
|
4
|
+
const logging_1 = require("../server/logging");
|
|
5
|
+
const activeSessions = new Set();
|
|
6
|
+
function registerToggleSimulatedLogging(server) {
|
|
7
|
+
server.registerTool('toggle-simulated-logging', {
|
|
8
|
+
description: 'Toggles simulated, random-leveled logging on or off. Demo/test fixture.',
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: false,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: false,
|
|
13
|
+
openWorldHint: false
|
|
14
|
+
}
|
|
15
|
+
}, async (extra) => {
|
|
16
|
+
const sessionId = extra.sessionId;
|
|
17
|
+
if (activeSessions.has(sessionId)) {
|
|
18
|
+
(0, logging_1.stopSimulatedLogging)(sessionId);
|
|
19
|
+
activeSessions.delete(sessionId);
|
|
20
|
+
return { content: [{ type: 'text', text: `Stopped simulated logging for session ${sessionId}` }] };
|
|
21
|
+
}
|
|
22
|
+
(0, logging_1.beginSimulatedLogging)(server, sessionId);
|
|
23
|
+
activeSessions.add(sessionId);
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: `Started simulated, random-leveled logging for session ${sessionId} at a 5 second pace. Client's selected logging level will be respected. If an interval elapses and the message to be sent is below the selected level, it will not be sent. Thus at higher chosen logging levels, messages should arrive further apart.`
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerToggleSubscriberUpdates = registerToggleSubscriberUpdates;
|
|
4
|
+
const subscriptions_1 = require("../resources/subscriptions");
|
|
5
|
+
const activeSessions = new Set();
|
|
6
|
+
function registerToggleSubscriberUpdates(server) {
|
|
7
|
+
server.registerTool('toggle-subscriber-updates', {
|
|
8
|
+
description: 'Toggles simulated resource subscription updates on or off. Demo/test fixture.',
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: false,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: false,
|
|
13
|
+
openWorldHint: false
|
|
14
|
+
}
|
|
15
|
+
}, async (extra) => {
|
|
16
|
+
const sessionId = extra.sessionId;
|
|
17
|
+
if (activeSessions.has(sessionId)) {
|
|
18
|
+
(0, subscriptions_1.stopSimulatedResourceUpdates)(sessionId);
|
|
19
|
+
activeSessions.delete(sessionId);
|
|
20
|
+
return { content: [{ type: 'text', text: `Stopped simulated resource updates for session ${sessionId}` }] };
|
|
21
|
+
}
|
|
22
|
+
(0, subscriptions_1.beginSimulatedResourceUpdates)(server, sessionId);
|
|
23
|
+
activeSessions.add(sessionId);
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: `Started simulated resource updated notifications for session ${sessionId} at a 5 second pace. Client will receive updates for any resources it is subscribed to.`
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerTriggerElicitationRequestAsync = registerTriggerElicitationRequestAsync;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const POLL_INTERVAL = 1000;
|
|
6
|
+
const MAX_POLL_ATTEMPTS = 600;
|
|
7
|
+
function registerTriggerElicitationRequestAsync(server) {
|
|
8
|
+
// Called from server.server.oninitialized (see index.ts), so the client's
|
|
9
|
+
// capabilities are already known here — this is not a registration-time
|
|
10
|
+
// race like it would be if called eagerly at server construction.
|
|
11
|
+
const clientCapabilities = server.server.getClientCapabilities() ?? {};
|
|
12
|
+
if (clientCapabilities.elicitation === undefined)
|
|
13
|
+
return;
|
|
14
|
+
server.registerTool('trigger-elicitation-request-async', {
|
|
15
|
+
description: 'Trigger an async elicitation request that the CLIENT executes as a background task. ' +
|
|
16
|
+
'Demonstrates bidirectional MCP tasks where the server sends an elicitation request and ' +
|
|
17
|
+
'the client handles user input asynchronously, allowing the server to poll for completion. ' +
|
|
18
|
+
'Falls back gracefully if the client only supports synchronous elicitation. Demo/test fixture.',
|
|
19
|
+
annotations: {
|
|
20
|
+
readOnlyHint: false,
|
|
21
|
+
destructiveHint: false,
|
|
22
|
+
idempotentHint: false,
|
|
23
|
+
openWorldHint: false
|
|
24
|
+
}
|
|
25
|
+
}, async (extra) => {
|
|
26
|
+
const elicitResponse = await extra.sendRequest({
|
|
27
|
+
method: 'elicitation/create',
|
|
28
|
+
params: {
|
|
29
|
+
task: { ttl: 600000 },
|
|
30
|
+
message: 'Please provide inputs for the following fields (async task demo):',
|
|
31
|
+
requestedSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
name: { title: 'Your Name', type: 'string', description: 'Your full name' },
|
|
35
|
+
favoriteColor: {
|
|
36
|
+
title: 'Favorite Color',
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'What is your favorite color?',
|
|
39
|
+
enum: ['Red', 'Blue', 'Green', 'Yellow', 'Purple']
|
|
40
|
+
},
|
|
41
|
+
agreeToTerms: {
|
|
42
|
+
title: 'Terms Agreement',
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
description: 'Do you agree to the terms and conditions?'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
required: ['name']
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}, zod_1.z.union([
|
|
51
|
+
zod_1.z.object({
|
|
52
|
+
task: zod_1.z.object({
|
|
53
|
+
taskId: zod_1.z.string(),
|
|
54
|
+
status: zod_1.z.string(),
|
|
55
|
+
pollInterval: zod_1.z.number().optional(),
|
|
56
|
+
statusMessage: zod_1.z.string().optional()
|
|
57
|
+
})
|
|
58
|
+
}),
|
|
59
|
+
zod_1.z.object({ action: zod_1.z.string(), content: zod_1.z.any().optional() })
|
|
60
|
+
]));
|
|
61
|
+
const isTaskResult = 'task' in elicitResponse && elicitResponse.task;
|
|
62
|
+
if (!isTaskResult) {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: `[SYNC] Client executed synchronously:\n${JSON.stringify(elicitResponse, null, 2)}`
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const taskId = elicitResponse.task.taskId;
|
|
73
|
+
const statusMessages = [`Task created: ${taskId}`];
|
|
74
|
+
let attempts = 0;
|
|
75
|
+
let taskStatus = elicitResponse.task.status;
|
|
76
|
+
let taskStatusMessage;
|
|
77
|
+
while (taskStatus !== 'completed' &&
|
|
78
|
+
taskStatus !== 'failed' &&
|
|
79
|
+
taskStatus !== 'cancelled' &&
|
|
80
|
+
attempts < MAX_POLL_ATTEMPTS) {
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
82
|
+
attempts++;
|
|
83
|
+
const pollResult = await extra.sendRequest({ method: 'tasks/get', params: { taskId } }, zod_1.z.looseObject({ status: zod_1.z.string(), statusMessage: zod_1.z.string().optional() }));
|
|
84
|
+
taskStatus = pollResult.status;
|
|
85
|
+
taskStatusMessage = pollResult.statusMessage;
|
|
86
|
+
if (attempts === 1 || attempts % 10 === 0 || taskStatus !== 'input_required') {
|
|
87
|
+
statusMessages.push(`Poll ${attempts}: ${taskStatus}${taskStatusMessage ? ` - ${taskStatusMessage}` : ''}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (attempts >= MAX_POLL_ATTEMPTS) {
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: `[TIMEOUT] Task timed out after ${MAX_POLL_ATTEMPTS} poll attempts\n\nProgress:\n${statusMessages.join('\n')}`
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (taskStatus === 'failed' || taskStatus === 'cancelled') {
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: 'text',
|
|
105
|
+
text: `[${taskStatus.toUpperCase()}] ${taskStatusMessage || 'No message'}\n\nProgress:\n${statusMessages.join('\n')}`
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const result = await extra.sendRequest({ method: 'tasks/result', params: { taskId } }, zod_1.z.any());
|
|
111
|
+
const content = [];
|
|
112
|
+
if (result.action === 'accept' && result.content) {
|
|
113
|
+
content.push({ type: 'text', text: '[COMPLETED] User provided the requested information!' });
|
|
114
|
+
const userData = result.content;
|
|
115
|
+
const lines = [];
|
|
116
|
+
if (userData.name)
|
|
117
|
+
lines.push(`- Name: ${userData.name}`);
|
|
118
|
+
if (userData.favoriteColor)
|
|
119
|
+
lines.push(`- Favorite Color: ${userData.favoriteColor}`);
|
|
120
|
+
if (userData.agreeToTerms !== undefined)
|
|
121
|
+
lines.push(`- Agreed to terms: ${userData.agreeToTerms}`);
|
|
122
|
+
content.push({ type: 'text', text: `User inputs:\n${lines.join('\n')}` });
|
|
123
|
+
}
|
|
124
|
+
else if (result.action === 'decline') {
|
|
125
|
+
content.push({ type: 'text', text: '[DECLINED] User declined to provide the requested information.' });
|
|
126
|
+
}
|
|
127
|
+
else if (result.action === 'cancel') {
|
|
128
|
+
content.push({ type: 'text', text: '[CANCELLED] User cancelled the elicitation dialog.' });
|
|
129
|
+
}
|
|
130
|
+
content.push({
|
|
131
|
+
type: 'text',
|
|
132
|
+
text: `\nProgress:\n${statusMessages.join('\n')}\n\nRaw result: ${JSON.stringify(result, null, 2)}`
|
|
133
|
+
});
|
|
134
|
+
return { content };
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerTriggerElicitationRequest = registerTriggerElicitationRequest;
|
|
4
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
5
|
+
function registerTriggerElicitationRequest(server) {
|
|
6
|
+
// Called from server.server.oninitialized (see index.ts), so the client's
|
|
7
|
+
// capabilities are already known here — this is not a registration-time
|
|
8
|
+
// race like it would be if called eagerly at server construction.
|
|
9
|
+
const clientCapabilities = server.server.getClientCapabilities() ?? {};
|
|
10
|
+
if (clientCapabilities.elicitation === undefined)
|
|
11
|
+
return;
|
|
12
|
+
server.registerTool('trigger-elicitation-request', {
|
|
13
|
+
description: 'Trigger a request from the server for user elicitation. Demo/test fixture.',
|
|
14
|
+
annotations: {
|
|
15
|
+
readOnlyHint: false,
|
|
16
|
+
destructiveHint: false,
|
|
17
|
+
idempotentHint: false,
|
|
18
|
+
openWorldHint: false
|
|
19
|
+
}
|
|
20
|
+
}, async (extra) => {
|
|
21
|
+
const elicitationResult = await extra.sendRequest({
|
|
22
|
+
method: 'elicitation/create',
|
|
23
|
+
params: {
|
|
24
|
+
message: 'Please provide inputs for the following fields:',
|
|
25
|
+
requestedSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
name: { title: 'String', type: 'string', description: 'Your full, legal name' },
|
|
29
|
+
check: { title: 'Boolean', type: 'boolean', description: 'Agree to the terms and conditions' },
|
|
30
|
+
firstLine: {
|
|
31
|
+
title: 'String with default',
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Favorite first line of a story',
|
|
34
|
+
default: 'It was a dark and stormy night.'
|
|
35
|
+
},
|
|
36
|
+
email: {
|
|
37
|
+
title: 'String with email format',
|
|
38
|
+
type: 'string',
|
|
39
|
+
format: 'email',
|
|
40
|
+
description: 'Your email address (will be verified, and never shared with anyone else)'
|
|
41
|
+
},
|
|
42
|
+
homepage: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
format: 'uri',
|
|
45
|
+
title: 'String with uri format',
|
|
46
|
+
description: 'Portfolio / personal website'
|
|
47
|
+
},
|
|
48
|
+
birthdate: {
|
|
49
|
+
title: 'String with date format',
|
|
50
|
+
type: 'string',
|
|
51
|
+
format: 'date',
|
|
52
|
+
description: 'Your date of birth'
|
|
53
|
+
},
|
|
54
|
+
integer: {
|
|
55
|
+
title: 'Integer',
|
|
56
|
+
type: 'integer',
|
|
57
|
+
description: 'Your favorite integer (do not give us your phone number, pin, or other sensitive info)',
|
|
58
|
+
minimum: 1,
|
|
59
|
+
maximum: 100,
|
|
60
|
+
default: 42
|
|
61
|
+
},
|
|
62
|
+
number: {
|
|
63
|
+
title: 'Number in range 1-1000',
|
|
64
|
+
type: 'number',
|
|
65
|
+
description: 'Favorite number (there are no wrong answers)',
|
|
66
|
+
minimum: 0,
|
|
67
|
+
maximum: 1000,
|
|
68
|
+
default: 3.14
|
|
69
|
+
},
|
|
70
|
+
untitledSingleSelectEnum: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
title: 'Untitled Single Select Enum',
|
|
73
|
+
description: 'Choose your favorite friend',
|
|
74
|
+
enum: ['Monica', 'Rachel', 'Joey', 'Chandler', 'Ross', 'Phoebe'],
|
|
75
|
+
default: 'Monica'
|
|
76
|
+
},
|
|
77
|
+
untitledMultipleSelectEnum: {
|
|
78
|
+
type: 'array',
|
|
79
|
+
title: 'Untitled Multiple Select Enum',
|
|
80
|
+
description: 'Choose your favorite instruments',
|
|
81
|
+
minItems: 1,
|
|
82
|
+
maxItems: 3,
|
|
83
|
+
items: { type: 'string', enum: ['Guitar', 'Piano', 'Violin', 'Drums', 'Bass'] },
|
|
84
|
+
default: ['Guitar']
|
|
85
|
+
},
|
|
86
|
+
titledSingleSelectEnum: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
title: 'Titled Single Select Enum',
|
|
89
|
+
description: 'Choose your favorite hero',
|
|
90
|
+
oneOf: [
|
|
91
|
+
{ const: 'hero-1', title: 'Superman' },
|
|
92
|
+
{ const: 'hero-2', title: 'Green Lantern' },
|
|
93
|
+
{ const: 'hero-3', title: 'Wonder Woman' }
|
|
94
|
+
],
|
|
95
|
+
default: 'hero-1'
|
|
96
|
+
},
|
|
97
|
+
titledMultipleSelectEnum: {
|
|
98
|
+
type: 'array',
|
|
99
|
+
title: 'Titled Multiple Select Enum',
|
|
100
|
+
description: 'Choose your favorite types of fish',
|
|
101
|
+
minItems: 1,
|
|
102
|
+
maxItems: 3,
|
|
103
|
+
items: {
|
|
104
|
+
anyOf: [
|
|
105
|
+
{ const: 'fish-1', title: 'Tuna' },
|
|
106
|
+
{ const: 'fish-2', title: 'Salmon' },
|
|
107
|
+
{ const: 'fish-3', title: 'Trout' }
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
default: ['fish-1']
|
|
111
|
+
},
|
|
112
|
+
legacyTitledEnum: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
title: 'Legacy Titled Single Select Enum',
|
|
115
|
+
description: 'Choose your favorite type of pet',
|
|
116
|
+
enum: ['pet-1', 'pet-2', 'pet-3', 'pet-4', 'pet-5'],
|
|
117
|
+
enumNames: ['Cats', 'Dogs', 'Birds', 'Fish', 'Reptiles'],
|
|
118
|
+
default: 'pet-1'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: ['name']
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}, types_js_1.ElicitResultSchema, { timeout: 10 * 60 * 1000 });
|
|
125
|
+
const content = [];
|
|
126
|
+
if (elicitationResult.action === 'accept' && elicitationResult.content) {
|
|
127
|
+
content.push({ type: 'text', text: '✅ User provided the requested information!' });
|
|
128
|
+
const userData = elicitationResult.content;
|
|
129
|
+
const lines = [];
|
|
130
|
+
if (userData.name)
|
|
131
|
+
lines.push(`- Name: ${userData.name}`);
|
|
132
|
+
if (userData.check !== undefined)
|
|
133
|
+
lines.push(`- Agreed to terms: ${userData.check}`);
|
|
134
|
+
if (userData.firstLine)
|
|
135
|
+
lines.push(`- Favorite first line: ${userData.firstLine}`);
|
|
136
|
+
if (userData.email)
|
|
137
|
+
lines.push(`- Email: ${userData.email}`);
|
|
138
|
+
if (userData.homepage)
|
|
139
|
+
lines.push(`- Homepage: ${userData.homepage}`);
|
|
140
|
+
if (userData.birthdate)
|
|
141
|
+
lines.push(`- Birthdate: ${userData.birthdate}`);
|
|
142
|
+
if (userData.integer !== undefined)
|
|
143
|
+
lines.push(`- Favorite Integer: ${userData.integer}`);
|
|
144
|
+
if (userData.number !== undefined)
|
|
145
|
+
lines.push(`- Favorite Number: ${userData.number}`);
|
|
146
|
+
if (userData.untitledSingleSelectEnum)
|
|
147
|
+
lines.push(`- Favorite friend: ${userData.untitledSingleSelectEnum}`);
|
|
148
|
+
if (userData.untitledMultipleSelectEnum)
|
|
149
|
+
lines.push(`- Favorite instruments: ${userData.untitledMultipleSelectEnum}`);
|
|
150
|
+
if (userData.titledSingleSelectEnum)
|
|
151
|
+
lines.push(`- Favorite hero: ${userData.titledSingleSelectEnum}`);
|
|
152
|
+
if (userData.titledMultipleSelectEnum)
|
|
153
|
+
lines.push(`- Favorite fish: ${userData.titledMultipleSelectEnum}`);
|
|
154
|
+
if (userData.legacyTitledEnum)
|
|
155
|
+
lines.push(`- Favorite pet type: ${userData.legacyTitledEnum}`);
|
|
156
|
+
content.push({ type: 'text', text: `User inputs:\n${lines.join('\n')}` });
|
|
157
|
+
}
|
|
158
|
+
else if (elicitationResult.action === 'decline') {
|
|
159
|
+
content.push({ type: 'text', text: '❌ User declined to provide the requested information.' });
|
|
160
|
+
}
|
|
161
|
+
else if (elicitationResult.action === 'cancel') {
|
|
162
|
+
content.push({ type: 'text', text: '⚠️ User cancelled the elicitation dialog.' });
|
|
163
|
+
}
|
|
164
|
+
content.push({ type: 'text', text: `\nRaw result: ${JSON.stringify(elicitationResult, null, 2)}` });
|
|
165
|
+
return { content };
|
|
166
|
+
});
|
|
167
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerTriggerLongRunningOperation = registerTriggerLongRunningOperation;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
function registerTriggerLongRunningOperation(server) {
|
|
6
|
+
server.registerTool('trigger-long-running-operation', {
|
|
7
|
+
description: 'Demonstrates a long running operation with progress updates. Demo/test fixture.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
duration: zod_1.z.number().default(10).describe('Duration of the operation in seconds'),
|
|
10
|
+
steps: zod_1.z.number().default(5).describe('Number of steps in the operation')
|
|
11
|
+
},
|
|
12
|
+
annotations: {
|
|
13
|
+
readOnlyHint: true,
|
|
14
|
+
destructiveHint: false,
|
|
15
|
+
idempotentHint: true,
|
|
16
|
+
openWorldHint: false
|
|
17
|
+
}
|
|
18
|
+
}, async ({ duration, steps }, extra) => {
|
|
19
|
+
const stepDuration = duration / steps;
|
|
20
|
+
const progressToken = extra._meta?.progressToken;
|
|
21
|
+
for (let i = 1; i < steps + 1; i++) {
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, stepDuration * 1000));
|
|
23
|
+
if (progressToken !== undefined) {
|
|
24
|
+
await server.server.notification({
|
|
25
|
+
method: 'notifications/progress',
|
|
26
|
+
params: { progress: i, total: steps, progressToken }
|
|
27
|
+
}, { relatedRequestId: extra.requestId });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{ type: 'text', text: `Long running operation completed. Duration: ${duration} seconds, Steps: ${steps}.` }
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|