@evantahler/mcpx 0.18.3 → 0.18.6
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/package.json +63 -63
- package/src/cli.ts +46 -54
- package/src/client/browser.ts +36 -15
- package/src/client/debug-fetch.ts +64 -56
- package/src/client/elicitation.ts +279 -291
- package/src/client/http.ts +1 -1
- package/src/client/manager.ts +481 -514
- package/src/client/oauth.ts +272 -282
- package/src/client/sse.ts +1 -1
- package/src/client/stdio.ts +7 -7
- package/src/client/trace.ts +146 -152
- package/src/client/transport-options.ts +20 -20
- package/src/commands/add.ts +160 -165
- package/src/commands/allow.ts +141 -142
- package/src/commands/auth.ts +86 -90
- package/src/commands/check-update.ts +49 -53
- package/src/commands/deny.ts +114 -117
- package/src/commands/exec.ts +218 -222
- package/src/commands/index.ts +41 -41
- package/src/commands/info.ts +48 -50
- package/src/commands/list.ts +49 -49
- package/src/commands/ping.ts +47 -50
- package/src/commands/prompt.ts +40 -50
- package/src/commands/remove.ts +54 -56
- package/src/commands/resource.ts +31 -36
- package/src/commands/search.ts +35 -39
- package/src/commands/servers.ts +44 -48
- package/src/commands/skill.ts +89 -95
- package/src/commands/task.ts +50 -60
- package/src/commands/upgrade.ts +191 -208
- package/src/commands/with-command.ts +27 -29
- package/src/config/env.ts +26 -28
- package/src/config/loader.ts +103 -103
- package/src/config/schemas.ts +78 -87
- package/src/constants.ts +17 -17
- package/src/context.ts +51 -51
- package/src/lib/client-settings.ts +127 -140
- package/src/lib/input.ts +23 -26
- package/src/output/format-output.ts +12 -16
- package/src/output/format-table.ts +39 -42
- package/src/output/formatter.ts +794 -815
- package/src/output/logger.ts +140 -152
- package/src/sdk.ts +283 -291
- package/src/search/index.ts +50 -54
- package/src/search/indexer.ts +65 -65
- package/src/search/keyword.ts +54 -54
- package/src/search/semantic.ts +39 -39
- package/src/search/staleness.ts +3 -3
- package/src/search/types.ts +4 -4
- package/src/update/background.ts +51 -51
- package/src/update/cache.ts +21 -21
- package/src/update/checker.ts +81 -86
- package/src/validation/schema.ts +53 -58
|
@@ -1,38 +1,35 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
ElicitRequest,
|
|
4
|
+
ElicitRequestFormParams,
|
|
5
|
+
ElicitRequestURLParams,
|
|
6
|
+
ElicitResult,
|
|
7
|
+
PrimitiveSchemaDefinition,
|
|
8
8
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import { openBrowser } from "./browser.ts";
|
|
10
|
-
import { validateElicitationResponse } from "../validation/schema.ts";
|
|
11
9
|
import ansis from "ansis";
|
|
10
|
+
import { validateElicitationResponse } from "../validation/schema.ts";
|
|
11
|
+
import { openBrowser } from "./browser.ts";
|
|
12
12
|
|
|
13
13
|
export interface ElicitationOptions {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
noInteractive: boolean;
|
|
15
|
+
json: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
type ElicitAction = "accept" | "cancel" | "decline";
|
|
19
19
|
|
|
20
20
|
/** Top-level elicitation request handler, registered on the MCP Client */
|
|
21
|
-
export async function handleElicitation(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return handleUrlElicitation(params as ElicitRequestURLParams, options);
|
|
34
|
-
}
|
|
35
|
-
return handleFormElicitation(params as ElicitRequestFormParams, options);
|
|
21
|
+
export async function handleElicitation(request: ElicitRequest, options: ElicitationOptions): Promise<ElicitResult> {
|
|
22
|
+
if (options.noInteractive) {
|
|
23
|
+
return { action: "decline" };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const params = request.params;
|
|
27
|
+
const mode = (params as { mode?: string }).mode ?? "form";
|
|
28
|
+
|
|
29
|
+
if (mode === "url") {
|
|
30
|
+
return handleUrlElicitation(params as ElicitRequestURLParams, options);
|
|
31
|
+
}
|
|
32
|
+
return handleFormElicitation(params as ElicitRequestFormParams, options);
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
// ---------------------------------------------------------------------------
|
|
@@ -40,253 +37,245 @@ export async function handleElicitation(
|
|
|
40
37
|
// ---------------------------------------------------------------------------
|
|
41
38
|
|
|
42
39
|
async function handleFormElicitation(
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
params: ElicitRequestFormParams,
|
|
41
|
+
options: ElicitationOptions,
|
|
45
42
|
): Promise<ElicitResult> {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
if (options.json) {
|
|
44
|
+
return handleFormJson(params);
|
|
45
|
+
}
|
|
46
|
+
return handleFormInteractive(params);
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
/** JSON mode: write request to stdout, read ElicitResult from stdin */
|
|
53
50
|
async function handleFormJson(params: ElicitRequestFormParams): Promise<ElicitResult> {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
51
|
+
const request = {
|
|
52
|
+
type: "elicitation",
|
|
53
|
+
mode: "form",
|
|
54
|
+
message: params.message,
|
|
55
|
+
requestedSchema: params.requestedSchema,
|
|
56
|
+
};
|
|
57
|
+
console.log(JSON.stringify(request));
|
|
58
|
+
|
|
59
|
+
const response = await readStdinLine();
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(response);
|
|
62
|
+
return {
|
|
63
|
+
action: parsed.action ?? "cancel",
|
|
64
|
+
content: parsed.content,
|
|
65
|
+
};
|
|
66
|
+
} catch {
|
|
67
|
+
return { action: "cancel" };
|
|
68
|
+
}
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/** Interactive TTY: prompt user for each field */
|
|
75
72
|
async function handleFormInteractive(params: ElicitRequestFormParams): Promise<ElicitResult> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return { action: "accept", content };
|
|
113
|
-
} finally {
|
|
114
|
-
rl.close();
|
|
115
|
-
}
|
|
73
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
74
|
+
const question = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
process.stderr.write(`\n${ansis.bold("Server requests input:")} ${params.message}\n`);
|
|
78
|
+
|
|
79
|
+
const schema = params.requestedSchema;
|
|
80
|
+
const properties = schema.properties ?? {};
|
|
81
|
+
const required = new Set((schema as { required?: string[] }).required ?? []);
|
|
82
|
+
const content: Record<string, string | number | boolean | string[]> = {};
|
|
83
|
+
|
|
84
|
+
for (const [key, fieldSchema] of Object.entries(properties)) {
|
|
85
|
+
const isRequired = required.has(key);
|
|
86
|
+
const value = await promptField(key, fieldSchema, isRequired, question);
|
|
87
|
+
if (value === undefined) {
|
|
88
|
+
if (isRequired) {
|
|
89
|
+
process.stderr.write(ansis.yellow("Cancelled.\n"));
|
|
90
|
+
return { action: "cancel" };
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
content[key] = value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Validate collected values against the full schema
|
|
98
|
+
const validation = validateElicitationResponse(schema as unknown as Record<string, unknown>, content);
|
|
99
|
+
if (!validation.valid) {
|
|
100
|
+
const msgs = validation.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
|
|
101
|
+
process.stderr.write(ansis.red(`Validation failed:\n${msgs}\n`));
|
|
102
|
+
return { action: "cancel" };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { action: "accept", content };
|
|
106
|
+
} finally {
|
|
107
|
+
rl.close();
|
|
108
|
+
}
|
|
116
109
|
}
|
|
117
110
|
|
|
118
111
|
async function promptField(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
key: string,
|
|
113
|
+
schema: PrimitiveSchemaDefinition,
|
|
114
|
+
isRequired: boolean,
|
|
115
|
+
question: (prompt: string) => Promise<string>,
|
|
123
116
|
): Promise<string | number | boolean | string[] | undefined> {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
117
|
+
const label = (schema as { title?: string }).title ?? key;
|
|
118
|
+
const desc = (schema as { description?: string }).description;
|
|
119
|
+
const marker = isRequired ? ansis.red("*") : "";
|
|
120
|
+
const type = (schema as { type?: string }).type;
|
|
121
|
+
|
|
122
|
+
// Show description if present
|
|
123
|
+
if (desc) {
|
|
124
|
+
process.stderr.write(ansis.dim(` ${desc}\n`));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Enum (single-select)
|
|
128
|
+
if (type === "string" && "enum" in schema) {
|
|
129
|
+
return promptEnum(label, schema, marker, question);
|
|
130
|
+
}
|
|
131
|
+
if (type === "string" && "oneOf" in schema) {
|
|
132
|
+
return promptOneOfEnum(label, schema, marker, question);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Multi-select enum (array type)
|
|
136
|
+
if (type === "array") {
|
|
137
|
+
return promptMultiSelect(label, schema, marker, question);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Boolean
|
|
141
|
+
if (type === "boolean") {
|
|
142
|
+
return promptBoolean(label, schema, marker, question);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Number / integer
|
|
146
|
+
if (type === "number" || type === "integer") {
|
|
147
|
+
return promptNumber(label, schema, marker, question);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// String (default)
|
|
151
|
+
return promptString(label, schema, marker, question);
|
|
159
152
|
}
|
|
160
153
|
|
|
161
154
|
async function promptString(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
label: string,
|
|
156
|
+
schema: PrimitiveSchemaDefinition,
|
|
157
|
+
marker: string,
|
|
158
|
+
question: (prompt: string) => Promise<string>,
|
|
166
159
|
): Promise<string | undefined> {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
160
|
+
const def = (schema as { default?: string }).default;
|
|
161
|
+
const defHint = def !== undefined ? ` [${def}]` : "";
|
|
162
|
+
const answer = await question(` ${marker}${label} (string)${defHint}: `);
|
|
163
|
+
if (!answer && def !== undefined) return def;
|
|
164
|
+
if (!answer) return undefined;
|
|
165
|
+
return answer;
|
|
173
166
|
}
|
|
174
167
|
|
|
175
168
|
async function promptNumber(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
label: string,
|
|
170
|
+
schema: PrimitiveSchemaDefinition,
|
|
171
|
+
marker: string,
|
|
172
|
+
question: (prompt: string) => Promise<string>,
|
|
180
173
|
): Promise<number | undefined> {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
return num;
|
|
174
|
+
const def = (schema as { default?: number }).default;
|
|
175
|
+
const defHint = def !== undefined ? ` [${def}]` : "";
|
|
176
|
+
const answer = await question(` ${marker}${label} (${(schema as { type: string }).type})${defHint}: `);
|
|
177
|
+
if (!answer && def !== undefined) return def;
|
|
178
|
+
if (!answer) return undefined;
|
|
179
|
+
const num = Number(answer);
|
|
180
|
+
if (Number.isNaN(num)) {
|
|
181
|
+
process.stderr.write(ansis.red(` Invalid number: ${answer}\n`));
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
return num;
|
|
194
185
|
}
|
|
195
186
|
|
|
196
187
|
async function promptBoolean(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
188
|
+
label: string,
|
|
189
|
+
schema: PrimitiveSchemaDefinition,
|
|
190
|
+
marker: string,
|
|
191
|
+
question: (prompt: string) => Promise<string>,
|
|
201
192
|
): Promise<boolean | undefined> {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
193
|
+
const def = (schema as { default?: boolean }).default;
|
|
194
|
+
const defHint = def !== undefined ? ` [${def ? "Y/n" : "y/N"}]` : " [y/n]";
|
|
195
|
+
const answer = await question(` ${marker}${label}${defHint}: `);
|
|
196
|
+
if (!answer && def !== undefined) return def;
|
|
197
|
+
if (!answer) return undefined;
|
|
198
|
+
return ["y", "yes", "true", "1"].includes(answer.toLowerCase());
|
|
208
199
|
}
|
|
209
200
|
|
|
210
201
|
async function promptEnum(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
202
|
+
label: string,
|
|
203
|
+
schema: PrimitiveSchemaDefinition,
|
|
204
|
+
marker: string,
|
|
205
|
+
question: (prompt: string) => Promise<string>,
|
|
215
206
|
): Promise<string | undefined> {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
207
|
+
const values = (schema as { enum: string[] }).enum;
|
|
208
|
+
const def = (schema as { default?: string }).default;
|
|
209
|
+
process.stderr.write(` ${marker}${label}:\n`);
|
|
210
|
+
values.forEach((v, i) => {
|
|
211
|
+
const defMark = v === def ? ansis.dim(" (default)") : "";
|
|
212
|
+
process.stderr.write(` [${i + 1}] ${v}${defMark}\n`);
|
|
213
|
+
});
|
|
214
|
+
const answer = await question(" > ");
|
|
215
|
+
if (!answer && def !== undefined) return def;
|
|
216
|
+
const idx = parseInt(answer, 10) - 1;
|
|
217
|
+
if (idx >= 0 && idx < values.length) return values[idx];
|
|
218
|
+
// Try matching by value directly
|
|
219
|
+
if (values.includes(answer)) return answer;
|
|
220
|
+
return undefined;
|
|
230
221
|
}
|
|
231
222
|
|
|
232
223
|
async function promptOneOfEnum(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
224
|
+
label: string,
|
|
225
|
+
schema: PrimitiveSchemaDefinition,
|
|
226
|
+
marker: string,
|
|
227
|
+
question: (prompt: string) => Promise<string>,
|
|
237
228
|
): Promise<string | undefined> {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
229
|
+
const options = (schema as { oneOf: { const: string; title: string }[] }).oneOf;
|
|
230
|
+
const def = (schema as { default?: string }).default;
|
|
231
|
+
process.stderr.write(` ${marker}${label}:\n`);
|
|
232
|
+
options.forEach((opt, i) => {
|
|
233
|
+
const defMark = opt.const === def ? ansis.dim(" (default)") : "";
|
|
234
|
+
process.stderr.write(` [${i + 1}] ${opt.title} (${opt.const})${defMark}\n`);
|
|
235
|
+
});
|
|
236
|
+
const answer = await question(" > ");
|
|
237
|
+
if (!answer && def !== undefined) return def;
|
|
238
|
+
const idx = parseInt(answer, 10) - 1;
|
|
239
|
+
if (idx >= 0 && idx < options.length) return options[idx]?.const;
|
|
240
|
+
// Try matching by const value directly
|
|
241
|
+
const match = options.find((o) => o.const === answer);
|
|
242
|
+
if (match) return match.const;
|
|
243
|
+
return undefined;
|
|
253
244
|
}
|
|
254
245
|
|
|
255
246
|
async function promptMultiSelect(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
247
|
+
label: string,
|
|
248
|
+
schema: PrimitiveSchemaDefinition,
|
|
249
|
+
marker: string,
|
|
250
|
+
question: (prompt: string) => Promise<string>,
|
|
260
251
|
): Promise<string[] | undefined> {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const selected = indices.filter((i) => i >= 0 && i < values.length).map((i) => values[i]!);
|
|
289
|
-
return selected.length > 0 ? selected : undefined;
|
|
252
|
+
const items = (schema as { items?: { enum?: string[]; anyOf?: { const: string; title: string }[] } }).items;
|
|
253
|
+
const def = (schema as { default?: string[] }).default;
|
|
254
|
+
|
|
255
|
+
let values: string[];
|
|
256
|
+
let titles: string[] | undefined;
|
|
257
|
+
|
|
258
|
+
if (items?.anyOf) {
|
|
259
|
+
values = items.anyOf.map((o) => o.const);
|
|
260
|
+
titles = items.anyOf.map((o) => o.title);
|
|
261
|
+
} else if (items?.enum) {
|
|
262
|
+
values = items.enum;
|
|
263
|
+
} else {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
process.stderr.write(` ${marker}${label} (select multiple, comma-separated):\n`);
|
|
268
|
+
values.forEach((v, i) => {
|
|
269
|
+
const display = titles ? `${titles[i]} (${v})` : v;
|
|
270
|
+
process.stderr.write(` [${i + 1}] ${display}\n`);
|
|
271
|
+
});
|
|
272
|
+
const answer = await question(" > ");
|
|
273
|
+
if (!answer && def !== undefined) return def;
|
|
274
|
+
if (!answer) return undefined;
|
|
275
|
+
|
|
276
|
+
const indices = answer.split(",").map((s) => parseInt(s.trim(), 10) - 1);
|
|
277
|
+
const selected = indices.filter((i) => i >= 0 && i < values.length).map((i) => values[i]!);
|
|
278
|
+
return selected.length > 0 ? selected : undefined;
|
|
290
279
|
}
|
|
291
280
|
|
|
292
281
|
// ---------------------------------------------------------------------------
|
|
@@ -294,62 +283,61 @@ async function promptMultiSelect(
|
|
|
294
283
|
// ---------------------------------------------------------------------------
|
|
295
284
|
|
|
296
285
|
async function handleUrlElicitation(
|
|
297
|
-
|
|
298
|
-
|
|
286
|
+
params: ElicitRequestURLParams,
|
|
287
|
+
options: ElicitationOptions,
|
|
299
288
|
): Promise<ElicitResult> {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
289
|
+
if (options.json) {
|
|
290
|
+
return handleUrlJson(params);
|
|
291
|
+
}
|
|
292
|
+
return handleUrlInteractive(params);
|
|
304
293
|
}
|
|
305
294
|
|
|
306
295
|
async function handleUrlJson(params: ElicitRequestURLParams): Promise<ElicitResult> {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
296
|
+
const request = {
|
|
297
|
+
type: "elicitation",
|
|
298
|
+
mode: "url",
|
|
299
|
+
message: params.message,
|
|
300
|
+
url: params.url,
|
|
301
|
+
elicitationId: params.elicitationId,
|
|
302
|
+
};
|
|
303
|
+
console.log(JSON.stringify(request));
|
|
304
|
+
|
|
305
|
+
const response = await readStdinLine();
|
|
306
|
+
try {
|
|
307
|
+
const parsed = JSON.parse(response);
|
|
308
|
+
return { action: (parsed.action as ElicitAction) ?? "cancel" };
|
|
309
|
+
} catch {
|
|
310
|
+
return { action: "cancel" };
|
|
311
|
+
}
|
|
323
312
|
}
|
|
324
313
|
|
|
325
314
|
async function handleUrlInteractive(params: ElicitRequestURLParams): Promise<ElicitResult> {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
315
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
316
|
+
const question = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const domain = (() => {
|
|
320
|
+
try {
|
|
321
|
+
return new URL(params.url).hostname;
|
|
322
|
+
} catch {
|
|
323
|
+
return "unknown";
|
|
324
|
+
}
|
|
325
|
+
})();
|
|
326
|
+
|
|
327
|
+
process.stderr.write(`\n${ansis.bold("Server requests URL interaction:")}\n`);
|
|
328
|
+
process.stderr.write(` ${params.message}\n`);
|
|
329
|
+
process.stderr.write(` ${ansis.yellow("Domain:")} ${domain}\n`);
|
|
330
|
+
process.stderr.write(` ${ansis.yellow("URL:")} ${params.url}\n`);
|
|
331
|
+
|
|
332
|
+
const answer = await question(` Open in browser? [y/n]: `);
|
|
333
|
+
if (["y", "yes"].includes(answer.toLowerCase())) {
|
|
334
|
+
await openBrowser(params.url);
|
|
335
|
+
return { action: "accept" };
|
|
336
|
+
}
|
|
337
|
+
return { action: "decline" };
|
|
338
|
+
} finally {
|
|
339
|
+
rl.close();
|
|
340
|
+
}
|
|
353
341
|
}
|
|
354
342
|
|
|
355
343
|
// ---------------------------------------------------------------------------
|
|
@@ -357,12 +345,12 @@ async function handleUrlInteractive(params: ElicitRequestURLParams): Promise<Eli
|
|
|
357
345
|
// ---------------------------------------------------------------------------
|
|
358
346
|
|
|
359
347
|
function readStdinLine(): Promise<string> {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
348
|
+
return new Promise((resolve) => {
|
|
349
|
+
const rl = createInterface({ input: process.stdin });
|
|
350
|
+
rl.once("line", (line) => {
|
|
351
|
+
rl.close();
|
|
352
|
+
resolve(line);
|
|
353
|
+
});
|
|
354
|
+
rl.once("close", () => resolve(""));
|
|
355
|
+
});
|
|
368
356
|
}
|
package/src/client/http.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
2
2
|
import { buildTransportInit, type TransportDeps } from "./transport-options.ts";
|
|
3
3
|
|
|
4
4
|
export function createHttpTransport(deps: TransportDeps): StreamableHTTPClientTransport {
|
|
5
|
-
|
|
5
|
+
return new StreamableHTTPClientTransport(new URL(deps.config.url), buildTransportInit(deps));
|
|
6
6
|
}
|