@evantahler/mcpx 0.18.2 → 0.18.5

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