@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
@@ -4,13 +4,13 @@ import { cyan, dim, green, red, yellow } from "ansis";
4
4
  import { logger } from "../output/logger.ts";
5
5
 
6
6
  export interface TraceOptions {
7
- json: boolean;
8
- serverName: string;
7
+ json: boolean;
8
+ serverName: string;
9
9
  }
10
10
 
11
11
  interface PendingRequest {
12
- method: string;
13
- sentAt: number;
12
+ method: string;
13
+ sentAt: number;
14
14
  }
15
15
 
16
16
  /**
@@ -19,166 +19,160 @@ interface PendingRequest {
19
19
  * Uses a Proxy so all other transport properties pass through transparently.
20
20
  */
21
21
  export function wrapTransportWithTrace(transport: Transport, options: TraceOptions): Transport {
22
- const pending = new Map<string | number, PendingRequest>();
23
- const isTTY = process.stderr.isTTY ?? false;
24
-
25
- let clientOnMessage: ((message: JSONRPCMessage, extra?: unknown) => void) | undefined;
26
-
27
- return new Proxy(transport, {
28
- get(target, prop, receiver) {
29
- if (prop === "send") {
30
- return async (message: JSONRPCMessage) => {
31
- logOutgoing(message, pending, options, isTTY);
32
- return target.send(message);
33
- };
34
- }
35
- if (prop === "onmessage") {
36
- return clientOnMessage;
37
- }
38
- return Reflect.get(target, prop, receiver);
39
- },
40
- set(target, prop, value) {
41
- if (prop === "onmessage") {
42
- clientOnMessage = value;
43
- target.onmessage = (message: JSONRPCMessage, extra?: unknown) => {
44
- logIncoming(message, pending, options, isTTY);
45
- clientOnMessage?.(message, extra);
46
- };
47
- return true;
48
- }
49
- return Reflect.set(target, prop, value);
50
- },
51
- });
22
+ const pending = new Map<string | number, PendingRequest>();
23
+ const isTTY = process.stderr.isTTY ?? false;
24
+
25
+ let clientOnMessage: ((message: JSONRPCMessage, extra?: unknown) => void) | undefined;
26
+
27
+ return new Proxy(transport, {
28
+ get(target, prop, receiver) {
29
+ if (prop === "send") {
30
+ return async (message: JSONRPCMessage) => {
31
+ logOutgoing(message, pending, options, isTTY);
32
+ return target.send(message);
33
+ };
34
+ }
35
+ if (prop === "onmessage") {
36
+ return clientOnMessage;
37
+ }
38
+ return Reflect.get(target, prop, receiver);
39
+ },
40
+ set(target, prop, value) {
41
+ if (prop === "onmessage") {
42
+ clientOnMessage = value;
43
+ target.onmessage = (message: JSONRPCMessage, extra?: unknown) => {
44
+ logIncoming(message, pending, options, isTTY);
45
+ clientOnMessage?.(message, extra);
46
+ };
47
+ return true;
48
+ }
49
+ return Reflect.set(target, prop, value);
50
+ },
51
+ });
52
52
  }
53
53
 
54
54
  function logOutgoing(
55
- message: JSONRPCMessage,
56
- pending: Map<string | number, PendingRequest>,
57
- options: TraceOptions,
58
- isTTY: boolean,
55
+ message: JSONRPCMessage,
56
+ pending: Map<string | number, PendingRequest>,
57
+ options: TraceOptions,
58
+ isTTY: boolean,
59
59
  ): void {
60
- // Track pending requests for timing (needed in both modes)
61
- if ("id" in message && "method" in message) {
62
- const m = message as { id: string | number; method: string };
63
- pending.set(m.id, { method: m.method, sentAt: performance.now() });
64
- }
65
-
66
- if (options.json) {
67
- logger.writeRaw(
68
- JSON.stringify({ trace: "outgoing", server: options.serverName, message }) + "\n",
69
- );
70
- return;
71
- }
72
-
73
- if ("id" in message && "method" in message) {
74
- const m = message as { id: string | number; method: string; params?: unknown };
75
- const arrow = isTTY ? cyan("→") : "";
76
- const detail = summarizeParams(m.method, m.params);
77
- const detailStr = detail ? ` ${detail}` : "";
78
- logger.writeRaw(`${arrow} ${dim(`${m.method} (id: ${m.id})${detailStr}`)}\n`);
79
- } else if ("method" in message) {
80
- const m = message as { method: string };
81
- const arrow = isTTY ? cyan("→") : "→";
82
- logger.writeRaw(`${arrow} ${dim(m.method)}\n`);
83
- }
60
+ // Track pending requests for timing (needed in both modes)
61
+ if ("id" in message && "method" in message) {
62
+ const m = message as { id: string | number; method: string };
63
+ pending.set(m.id, { method: m.method, sentAt: performance.now() });
64
+ }
65
+
66
+ if (options.json) {
67
+ logger.writeRaw(`${JSON.stringify({ trace: "outgoing", server: options.serverName, message })}\n`);
68
+ return;
69
+ }
70
+
71
+ if ("id" in message && "method" in message) {
72
+ const m = message as { id: string | number; method: string; params?: unknown };
73
+ const arrow = isTTY ? cyan("") : "→";
74
+ const detail = summarizeParams(m.method, m.params);
75
+ const detailStr = detail ? ` ${detail}` : "";
76
+ logger.writeRaw(`${arrow} ${dim(`${m.method} (id: ${m.id})${detailStr}`)}\n`);
77
+ } else if ("method" in message) {
78
+ const m = message as { method: string };
79
+ const arrow = isTTY ? cyan("") : "→";
80
+ logger.writeRaw(`${arrow} ${dim(m.method)}\n`);
81
+ }
84
82
  }
85
83
 
86
84
  function logIncoming(
87
- message: JSONRPCMessage,
88
- pending: Map<string | number, PendingRequest>,
89
- options: TraceOptions,
90
- isTTY: boolean,
85
+ message: JSONRPCMessage,
86
+ pending: Map<string | number, PendingRequest>,
87
+ options: TraceOptions,
88
+ isTTY: boolean,
91
89
  ): void {
92
- if ("id" in message && !("method" in message)) {
93
- // Response to a request
94
- const m = message as { id: string | number; result?: unknown; error?: unknown };
95
- const req = pending.get(m.id);
96
- pending.delete(m.id);
97
- const elapsed = req ? Math.round(performance.now() - req.sentAt) : undefined;
98
- const method = req?.method ?? "unknown";
99
-
100
- if (options.json) {
101
- logger.writeRaw(
102
- JSON.stringify({
103
- trace: "incoming",
104
- server: options.serverName,
105
- message,
106
- ...(elapsed !== undefined && { elapsed_ms: elapsed }),
107
- request_method: method,
108
- }) + "\n",
109
- );
110
- return;
111
- }
112
-
113
- const isError = m.error !== undefined;
114
- const arrow = isTTY ? (isError ? red("←") : green("←")) : "←";
115
- const timing = elapsed !== undefined ? ` [${elapsed}ms]` : "";
116
- const summary = summarizeResult(method, m.result);
117
- const summaryStr = summary ? ` — ${summary}` : "";
118
- logger.writeRaw(`${arrow} ${dim(`${method} (id: ${m.id})${timing}${summaryStr}`)}\n`);
119
- } else if ("method" in message) {
120
- // Notification (incoming)
121
- const m = message as { method: string; params?: unknown };
122
-
123
- if (options.json) {
124
- logger.writeRaw(
125
- JSON.stringify({ trace: "incoming", server: options.serverName, message }) + "\n",
126
- );
127
- return;
128
- }
129
-
130
- const arrow = isTTY ? yellow("←") : "←";
131
- const params = m.params ? ` ${JSON.stringify(m.params)}` : "";
132
- logger.writeRaw(`${arrow} ${dim(`${m.method}${params}`)}\n`);
133
- }
90
+ if ("id" in message && !("method" in message)) {
91
+ // Response to a request
92
+ const m = message as { id: string | number; result?: unknown; error?: unknown };
93
+ const req = pending.get(m.id);
94
+ pending.delete(m.id);
95
+ const elapsed = req ? Math.round(performance.now() - req.sentAt) : undefined;
96
+ const method = req?.method ?? "unknown";
97
+
98
+ if (options.json) {
99
+ logger.writeRaw(
100
+ `${JSON.stringify({
101
+ trace: "incoming",
102
+ server: options.serverName,
103
+ message,
104
+ ...(elapsed !== undefined && { elapsed_ms: elapsed }),
105
+ request_method: method,
106
+ })}\n`,
107
+ );
108
+ return;
109
+ }
110
+
111
+ const isError = m.error !== undefined;
112
+ const arrow = isTTY ? (isError ? red("←") : green("←")) : "←";
113
+ const timing = elapsed !== undefined ? ` [${elapsed}ms]` : "";
114
+ const summary = summarizeResult(method, m.result);
115
+ const summaryStr = summary ? ` — ${summary}` : "";
116
+ logger.writeRaw(`${arrow} ${dim(`${method} (id: ${m.id})${timing}${summaryStr}`)}\n`);
117
+ } else if ("method" in message) {
118
+ // Notification (incoming)
119
+ const m = message as { method: string; params?: unknown };
120
+
121
+ if (options.json) {
122
+ logger.writeRaw(`${JSON.stringify({ trace: "incoming", server: options.serverName, message })}\n`);
123
+ return;
124
+ }
125
+
126
+ const arrow = isTTY ? yellow("←") : "←";
127
+ const params = m.params ? ` ${JSON.stringify(m.params)}` : "";
128
+ logger.writeRaw(`${arrow} ${dim(`${m.method}${params}`)}\n`);
129
+ }
134
130
  }
135
131
 
136
132
  function summarizeParams(method: string, params: unknown): string | undefined {
137
- if (!params || typeof params !== "object") return undefined;
138
- const p = params as Record<string, unknown>;
139
-
140
- switch (method) {
141
- case "tools/call": {
142
- const name = p.name as string | undefined;
143
- const args = p.arguments;
144
- if (!name) return undefined;
145
- const argsStr = args ? ` ${JSON.stringify(args)}` : "";
146
- return `${name}${argsStr}`;
147
- }
148
- case "resources/read":
149
- return p.uri ? String(p.uri) : undefined;
150
- case "prompts/get":
151
- return p.name ? String(p.name) : undefined;
152
- default:
153
- return undefined;
154
- }
133
+ if (!params || typeof params !== "object") return undefined;
134
+ const p = params as Record<string, unknown>;
135
+
136
+ switch (method) {
137
+ case "tools/call": {
138
+ const name = p.name as string | undefined;
139
+ const args = p.arguments;
140
+ if (!name) return undefined;
141
+ const argsStr = args ? ` ${JSON.stringify(args)}` : "";
142
+ return `${name}${argsStr}`;
143
+ }
144
+ case "resources/read":
145
+ return p.uri ? String(p.uri) : undefined;
146
+ case "prompts/get":
147
+ return p.name ? String(p.name) : undefined;
148
+ default:
149
+ return undefined;
150
+ }
155
151
  }
156
152
 
157
153
  function summarizeResult(method: string, result: unknown): string | undefined {
158
- if (!result || typeof result !== "object") return undefined;
159
- const r = result as Record<string, unknown>;
160
-
161
- switch (method) {
162
- case "tools/list":
163
- return Array.isArray(r.tools) ? `${r.tools.length} tools` : undefined;
164
- case "resources/list":
165
- return Array.isArray(r.resources) ? `${r.resources.length} resources` : undefined;
166
- case "resources/templates/list":
167
- return Array.isArray(r.resourceTemplates)
168
- ? `${r.resourceTemplates.length} templates`
169
- : undefined;
170
- case "prompts/list":
171
- return Array.isArray(r.prompts) ? `${r.prompts.length} prompts` : undefined;
172
- case "initialize": {
173
- const info = r.serverInfo as { name?: string; version?: string } | undefined;
174
- if (info?.name) return info.version ? `${info.name} v${info.version}` : info.name;
175
- return undefined;
176
- }
177
- case "tools/call":
178
- return r.isError ? "error" : "ok";
179
- case "ping":
180
- return "pong";
181
- default:
182
- return "ok";
183
- }
154
+ if (!result || typeof result !== "object") return undefined;
155
+ const r = result as Record<string, unknown>;
156
+
157
+ switch (method) {
158
+ case "tools/list":
159
+ return Array.isArray(r.tools) ? `${r.tools.length} tools` : undefined;
160
+ case "resources/list":
161
+ return Array.isArray(r.resources) ? `${r.resources.length} resources` : undefined;
162
+ case "resources/templates/list":
163
+ return Array.isArray(r.resourceTemplates) ? `${r.resourceTemplates.length} templates` : undefined;
164
+ case "prompts/list":
165
+ return Array.isArray(r.prompts) ? `${r.prompts.length} prompts` : undefined;
166
+ case "initialize": {
167
+ const info = r.serverInfo as { name?: string; version?: string } | undefined;
168
+ if (info?.name) return info.version ? `${info.name} v${info.version}` : info.name;
169
+ return undefined;
170
+ }
171
+ case "tools/call":
172
+ return r.isError ? "error" : "ok";
173
+ case "ping":
174
+ return "pong";
175
+ default:
176
+ return "ok";
177
+ }
184
178
  }
@@ -1,31 +1,31 @@
1
1
  import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
2
+ import pkg from "../../package.json";
2
3
  import type { HttpServerConfig } from "../config/schemas.ts";
3
4
  import { createDebugFetch, type FetchLike } from "./debug-fetch.ts";
4
- import pkg from "../../package.json";
5
5
 
6
6
  export interface TransportDeps {
7
- config: HttpServerConfig;
8
- authProvider?: OAuthClientProvider;
9
- verbose?: boolean;
10
- showSecrets?: boolean;
7
+ config: HttpServerConfig;
8
+ authProvider?: OAuthClientProvider;
9
+ verbose?: boolean;
10
+ showSecrets?: boolean;
11
11
  }
12
12
 
13
13
  /** Build shared transport init options (auth, headers, User-Agent, debug fetch) */
14
14
  export function buildTransportInit(deps: TransportDeps): {
15
- authProvider?: OAuthClientProvider;
16
- requestInit: RequestInit;
17
- fetch?: FetchLike;
15
+ authProvider?: OAuthClientProvider;
16
+ requestInit: RequestInit;
17
+ fetch?: FetchLike;
18
18
  } {
19
- const { config, authProvider, verbose = false, showSecrets = false } = deps;
20
- const userAgent = `${pkg.name}/${pkg.version}`;
21
- return {
22
- authProvider,
23
- requestInit: {
24
- headers: {
25
- "User-Agent": userAgent,
26
- ...config.headers,
27
- },
28
- },
29
- fetch: verbose ? createDebugFetch(showSecrets) : undefined,
30
- };
19
+ const { config, authProvider, verbose = false, showSecrets = false } = deps;
20
+ const userAgent = `${pkg.name}/${pkg.version}`;
21
+ return {
22
+ authProvider,
23
+ requestInit: {
24
+ headers: {
25
+ "User-Agent": userAgent,
26
+ ...config.headers,
27
+ },
28
+ },
29
+ fetch: verbose ? createDebugFetch(showSecrets) : undefined,
30
+ };
31
31
  }