@x-code-cli/core 0.2.10 → 0.3.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.
Files changed (258) hide show
  1. package/dist/agent/compression.d.ts +12 -2
  2. package/dist/agent/compression.d.ts.map +1 -1
  3. package/dist/agent/compression.js +51 -2
  4. package/dist/agent/compression.js.map +1 -1
  5. package/dist/agent/file-ingest.js +2 -2
  6. package/dist/agent/file-ingest.js.map +1 -1
  7. package/dist/agent/loop-state.d.ts +3 -2
  8. package/dist/agent/loop-state.d.ts.map +1 -1
  9. package/dist/agent/loop-state.js.map +1 -1
  10. package/dist/agent/loop.d.ts.map +1 -1
  11. package/dist/agent/loop.js +134 -5
  12. package/dist/agent/loop.js.map +1 -1
  13. package/dist/agent/memory-extractor.js +5 -5
  14. package/dist/agent/memory-extractor.js.map +1 -1
  15. package/dist/agent/plan-storage.js +1 -1
  16. package/dist/agent/plan-storage.js.map +1 -1
  17. package/dist/agent/sub-agents/index.d.ts +2 -1
  18. package/dist/agent/sub-agents/index.d.ts.map +1 -1
  19. package/dist/agent/sub-agents/index.js +1 -1
  20. package/dist/agent/sub-agents/index.js.map +1 -1
  21. package/dist/agent/sub-agents/loader.d.ts +13 -3
  22. package/dist/agent/sub-agents/loader.d.ts.map +1 -1
  23. package/dist/agent/sub-agents/loader.js +36 -9
  24. package/dist/agent/sub-agents/loader.js.map +1 -1
  25. package/dist/agent/sub-agents/registry.d.ts +18 -1
  26. package/dist/agent/sub-agents/registry.d.ts.map +1 -1
  27. package/dist/agent/sub-agents/registry.js +38 -5
  28. package/dist/agent/sub-agents/registry.js.map +1 -1
  29. package/dist/agent/sub-agents/runner.d.ts.map +1 -1
  30. package/dist/agent/sub-agents/runner.js +45 -1
  31. package/dist/agent/sub-agents/runner.js.map +1 -1
  32. package/dist/agent/sub-agents/types.d.ts +4 -1
  33. package/dist/agent/sub-agents/types.d.ts.map +1 -1
  34. package/dist/agent/system-prompt.d.ts +21 -0
  35. package/dist/agent/system-prompt.d.ts.map +1 -1
  36. package/dist/agent/system-prompt.js +68 -2
  37. package/dist/agent/system-prompt.js.map +1 -1
  38. package/dist/agent/tool-execution.d.ts.map +1 -1
  39. package/dist/agent/tool-execution.js +220 -1
  40. package/dist/agent/tool-execution.js.map +1 -1
  41. package/dist/commands/index.d.ts +6 -0
  42. package/dist/commands/index.d.ts.map +1 -0
  43. package/dist/commands/index.js +3 -0
  44. package/dist/commands/index.js.map +1 -0
  45. package/dist/commands/loader.d.ts +19 -0
  46. package/dist/commands/loader.d.ts.map +1 -0
  47. package/dist/commands/loader.js +109 -0
  48. package/dist/commands/loader.js.map +1 -0
  49. package/dist/commands/registry.d.ts +44 -0
  50. package/dist/commands/registry.d.ts.map +1 -0
  51. package/dist/commands/registry.js +102 -0
  52. package/dist/commands/registry.js.map +1 -0
  53. package/dist/commands/types.d.ts +23 -0
  54. package/dist/commands/types.d.ts.map +1 -0
  55. package/dist/commands/types.js +26 -0
  56. package/dist/commands/types.js.map +1 -0
  57. package/dist/config/index.d.ts +9 -0
  58. package/dist/config/index.d.ts.map +1 -1
  59. package/dist/config/index.js +12 -10
  60. package/dist/config/index.js.map +1 -1
  61. package/dist/hooks/bus.d.ts +54 -0
  62. package/dist/hooks/bus.d.ts.map +1 -0
  63. package/dist/hooks/bus.js +165 -0
  64. package/dist/hooks/bus.js.map +1 -0
  65. package/dist/hooks/config-schema.d.ts +854 -0
  66. package/dist/hooks/config-schema.d.ts.map +1 -0
  67. package/dist/hooks/config-schema.js +79 -0
  68. package/dist/hooks/config-schema.js.map +1 -0
  69. package/dist/hooks/executor.d.ts +16 -0
  70. package/dist/hooks/executor.d.ts.map +1 -0
  71. package/dist/hooks/executor.js +183 -0
  72. package/dist/hooks/executor.js.map +1 -0
  73. package/dist/hooks/index.d.ts +10 -0
  74. package/dist/hooks/index.d.ts.map +1 -0
  75. package/dist/hooks/index.js +6 -0
  76. package/dist/hooks/index.js.map +1 -0
  77. package/dist/hooks/registry.d.ts +23 -0
  78. package/dist/hooks/registry.d.ts.map +1 -0
  79. package/dist/hooks/registry.js +49 -0
  80. package/dist/hooks/registry.js.map +1 -0
  81. package/dist/hooks/types.d.ts +165 -0
  82. package/dist/hooks/types.d.ts.map +1 -0
  83. package/dist/hooks/types.js +25 -0
  84. package/dist/hooks/types.js.map +1 -0
  85. package/dist/hooks/variables.d.ts +22 -0
  86. package/dist/hooks/variables.d.ts.map +1 -0
  87. package/dist/hooks/variables.js +80 -0
  88. package/dist/hooks/variables.js.map +1 -0
  89. package/dist/index.d.ts +57 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +38 -2
  92. package/dist/index.js.map +1 -1
  93. package/dist/knowledge/auto-memory.d.ts +1 -1
  94. package/dist/knowledge/auto-memory.d.ts.map +1 -1
  95. package/dist/knowledge/auto-memory.js +10 -10
  96. package/dist/knowledge/auto-memory.js.map +1 -1
  97. package/dist/knowledge/loader.js +12 -12
  98. package/dist/knowledge/loader.js.map +1 -1
  99. package/dist/mcp/arg-parser.d.ts +49 -0
  100. package/dist/mcp/arg-parser.d.ts.map +1 -0
  101. package/dist/mcp/arg-parser.js +357 -0
  102. package/dist/mcp/arg-parser.js.map +1 -0
  103. package/dist/mcp/client.d.ts +73 -0
  104. package/dist/mcp/client.d.ts.map +1 -0
  105. package/dist/mcp/client.js +376 -0
  106. package/dist/mcp/client.js.map +1 -0
  107. package/dist/mcp/config-schema.d.ts +64 -0
  108. package/dist/mcp/config-schema.d.ts.map +1 -0
  109. package/dist/mcp/config-schema.js +86 -0
  110. package/dist/mcp/config-schema.js.map +1 -0
  111. package/dist/mcp/config-writer.d.ts +41 -0
  112. package/dist/mcp/config-writer.d.ts.map +1 -0
  113. package/dist/mcp/config-writer.js +138 -0
  114. package/dist/mcp/config-writer.js.map +1 -0
  115. package/dist/mcp/env-safety.d.ts +12 -0
  116. package/dist/mcp/env-safety.d.ts.map +1 -0
  117. package/dist/mcp/env-safety.js +80 -0
  118. package/dist/mcp/env-safety.js.map +1 -0
  119. package/dist/mcp/expand-env.d.ts +14 -0
  120. package/dist/mcp/expand-env.d.ts.map +1 -0
  121. package/dist/mcp/expand-env.js +52 -0
  122. package/dist/mcp/expand-env.js.map +1 -0
  123. package/dist/mcp/loader.d.ts +81 -0
  124. package/dist/mcp/loader.d.ts.map +1 -0
  125. package/dist/mcp/loader.js +223 -0
  126. package/dist/mcp/loader.js.map +1 -0
  127. package/dist/mcp/name-mangling.d.ts +11 -0
  128. package/dist/mcp/name-mangling.d.ts.map +1 -0
  129. package/dist/mcp/name-mangling.js +82 -0
  130. package/dist/mcp/name-mangling.js.map +1 -0
  131. package/dist/mcp/oauth/callback-server.d.ts +25 -0
  132. package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
  133. package/dist/mcp/oauth/callback-server.js +118 -0
  134. package/dist/mcp/oauth/callback-server.js.map +1 -0
  135. package/dist/mcp/oauth/provider.d.ts +80 -0
  136. package/dist/mcp/oauth/provider.d.ts.map +1 -0
  137. package/dist/mcp/oauth/provider.js +292 -0
  138. package/dist/mcp/oauth/provider.js.map +1 -0
  139. package/dist/mcp/oauth/token-storage.d.ts +42 -0
  140. package/dist/mcp/oauth/token-storage.d.ts.map +1 -0
  141. package/dist/mcp/oauth/token-storage.js +121 -0
  142. package/dist/mcp/oauth/token-storage.js.map +1 -0
  143. package/dist/mcp/permissions.d.ts +28 -0
  144. package/dist/mcp/permissions.d.ts.map +1 -0
  145. package/dist/mcp/permissions.js +105 -0
  146. package/dist/mcp/permissions.js.map +1 -0
  147. package/dist/mcp/registry.d.ts +150 -0
  148. package/dist/mcp/registry.d.ts.map +1 -0
  149. package/dist/mcp/registry.js +334 -0
  150. package/dist/mcp/registry.js.map +1 -0
  151. package/dist/mcp/resources.d.ts +7 -0
  152. package/dist/mcp/resources.d.ts.map +1 -0
  153. package/dist/mcp/resources.js +40 -0
  154. package/dist/mcp/resources.js.map +1 -0
  155. package/dist/mcp/tool-bridge.d.ts +16 -0
  156. package/dist/mcp/tool-bridge.d.ts.map +1 -0
  157. package/dist/mcp/tool-bridge.js +56 -0
  158. package/dist/mcp/tool-bridge.js.map +1 -0
  159. package/dist/mcp/trust.d.ts +31 -0
  160. package/dist/mcp/trust.d.ts.map +1 -0
  161. package/dist/mcp/trust.js +103 -0
  162. package/dist/mcp/trust.js.map +1 -0
  163. package/dist/mcp/types.d.ts +73 -0
  164. package/dist/mcp/types.d.ts.map +1 -0
  165. package/dist/mcp/types.js +13 -0
  166. package/dist/mcp/types.js.map +1 -0
  167. package/dist/permissions/index.d.ts +1 -1
  168. package/dist/permissions/index.d.ts.map +1 -1
  169. package/dist/permissions/index.js +10 -4
  170. package/dist/permissions/index.js.map +1 -1
  171. package/dist/permissions/session-store.d.ts +79 -17
  172. package/dist/permissions/session-store.d.ts.map +1 -1
  173. package/dist/permissions/session-store.js +298 -38
  174. package/dist/permissions/session-store.js.map +1 -1
  175. package/dist/plugins/consent.d.ts +87 -0
  176. package/dist/plugins/consent.d.ts.map +1 -0
  177. package/dist/plugins/consent.js +181 -0
  178. package/dist/plugins/consent.js.map +1 -0
  179. package/dist/plugins/enable-state.d.ts +34 -0
  180. package/dist/plugins/enable-state.d.ts.map +1 -0
  181. package/dist/plugins/enable-state.js +159 -0
  182. package/dist/plugins/enable-state.js.map +1 -0
  183. package/dist/plugins/installer.d.ts +64 -0
  184. package/dist/plugins/installer.d.ts.map +1 -0
  185. package/dist/plugins/installer.js +416 -0
  186. package/dist/plugins/installer.js.map +1 -0
  187. package/dist/plugins/integration.d.ts +91 -0
  188. package/dist/plugins/integration.d.ts.map +1 -0
  189. package/dist/plugins/integration.js +233 -0
  190. package/dist/plugins/integration.js.map +1 -0
  191. package/dist/plugins/loader.d.ts +69 -0
  192. package/dist/plugins/loader.d.ts.map +1 -0
  193. package/dist/plugins/loader.js +243 -0
  194. package/dist/plugins/loader.js.map +1 -0
  195. package/dist/plugins/manifest.d.ts +23 -0
  196. package/dist/plugins/manifest.d.ts.map +1 -0
  197. package/dist/plugins/manifest.js +143 -0
  198. package/dist/plugins/manifest.js.map +1 -0
  199. package/dist/plugins/marketplace.d.ts +103 -0
  200. package/dist/plugins/marketplace.d.ts.map +1 -0
  201. package/dist/plugins/marketplace.js +532 -0
  202. package/dist/plugins/marketplace.js.map +1 -0
  203. package/dist/plugins/paths.d.ts +44 -0
  204. package/dist/plugins/paths.d.ts.map +1 -0
  205. package/dist/plugins/paths.js +89 -0
  206. package/dist/plugins/paths.js.map +1 -0
  207. package/dist/plugins/refresh.d.ts +61 -0
  208. package/dist/plugins/refresh.d.ts.map +1 -0
  209. package/dist/plugins/refresh.js +98 -0
  210. package/dist/plugins/refresh.js.map +1 -0
  211. package/dist/plugins/registry.d.ts +40 -0
  212. package/dist/plugins/registry.d.ts.map +1 -0
  213. package/dist/plugins/registry.js +80 -0
  214. package/dist/plugins/registry.js.map +1 -0
  215. package/dist/plugins/types.d.ts +225 -0
  216. package/dist/plugins/types.d.ts.map +1 -0
  217. package/dist/plugins/types.js +16 -0
  218. package/dist/plugins/types.js.map +1 -0
  219. package/dist/plugins/user-config.d.ts +22 -0
  220. package/dist/plugins/user-config.d.ts.map +1 -0
  221. package/dist/plugins/user-config.js +96 -0
  222. package/dist/plugins/user-config.js.map +1 -0
  223. package/dist/skills/loader.d.ts +19 -0
  224. package/dist/skills/loader.d.ts.map +1 -0
  225. package/dist/skills/loader.js +197 -0
  226. package/dist/skills/loader.js.map +1 -0
  227. package/dist/skills/registry.d.ts +74 -0
  228. package/dist/skills/registry.d.ts.map +1 -0
  229. package/dist/skills/registry.js +136 -0
  230. package/dist/skills/registry.js.map +1 -0
  231. package/dist/skills/settings.d.ts +13 -0
  232. package/dist/skills/settings.d.ts.map +1 -0
  233. package/dist/skills/settings.js +100 -0
  234. package/dist/skills/settings.js.map +1 -0
  235. package/dist/tools/activate-skill.d.ts +5 -0
  236. package/dist/tools/activate-skill.d.ts.map +1 -0
  237. package/dist/tools/activate-skill.js +33 -0
  238. package/dist/tools/activate-skill.js.map +1 -0
  239. package/dist/tools/index.d.ts +1 -1
  240. package/dist/tools/shell-utils.d.ts.map +1 -1
  241. package/dist/tools/shell-utils.js +157 -6
  242. package/dist/tools/shell-utils.js.map +1 -1
  243. package/dist/tools/todo-write.d.ts +1 -1
  244. package/dist/tools/web-fetch.d.ts.map +1 -1
  245. package/dist/tools/web-fetch.js +2 -1
  246. package/dist/tools/web-fetch.js.map +1 -1
  247. package/dist/types/index.d.ts +46 -1
  248. package/dist/types/index.d.ts.map +1 -1
  249. package/dist/types/index.js.map +1 -1
  250. package/dist/utils.d.ts +23 -2
  251. package/dist/utils.d.ts.map +1 -1
  252. package/dist/utils.js +76 -20
  253. package/dist/utils.js.map +1 -1
  254. package/dist/version.d.ts +2 -0
  255. package/dist/version.d.ts.map +1 -0
  256. package/dist/version.js +47 -0
  257. package/dist/version.js.map +1 -0
  258. package/package.json +2 -1
@@ -0,0 +1,376 @@
1
+ // @x-code-cli/core — Per-server MCP client wrapper
2
+ //
3
+ // One McpClient instance == one server connection. The class hides the
4
+ // SDK's slightly awkward two-object setup (`new Client(...)` +
5
+ // `new XxxTransport(...)` + `client.connect(transport)`) behind one
6
+ // `connect()` method, owns transport teardown on `close()`, and exposes a
7
+ // narrow surface (listTools / callTool / listResources / readResource /
8
+ // close) that the registry actually needs.
9
+ //
10
+ // abortSignal threading: every server-bound RPC method takes an optional
11
+ // AbortSignal and forwards it via `RequestOptions.signal`. When the user
12
+ // hits Esc mid-tool-call the agent loop's signal aborts the SDK request,
13
+ // which closes the JSON-RPC future without killing the underlying
14
+ // connection — the next call can reuse the same transport.
15
+ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
16
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
17
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
18
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
19
+ import { debugLog } from '../utils.js';
20
+ import { VERSION } from '../version.js';
21
+ import { McpOAuthProvider } from './oauth/provider.js';
22
+ import { isHttpConfig, isStdioConfig, } from './types.js';
23
+ /** How many tail lines of stderr to keep around for diagnostics.
24
+ * When a stdio server dies on startup or fails mid-call, surfacing the
25
+ * last bit of its stderr in `/mcp list` is the difference between a
26
+ * meaningful error and a useless "exit code 1". */
27
+ const STDERR_TAIL_LINES = 20;
28
+ const CLIENT_INFO = { name: 'x-code-cli', version: VERSION };
29
+ /** Default first-connect timeout (ms). Overridable per-server via the
30
+ * config's `timeout` field. 30s is generous — community stdio servers
31
+ * are usually up in 100-500ms; the budget is for slow npx installs on
32
+ * cold cache, not normal operation. */
33
+ const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;
34
+ export class McpClient {
35
+ serverName;
36
+ config;
37
+ authProvider;
38
+ /** SDK client. Only present after a successful connect. */
39
+ client = null;
40
+ /** SDK transport. Owned by us so we can `close()` it cleanly. */
41
+ transport = null;
42
+ /** Rolling tail of stderr (stdio servers only). */
43
+ stderrTail = [];
44
+ /** Cached results from the last connect, served to the registry. */
45
+ cachedTools = [];
46
+ cachedResources = [];
47
+ constructor(serverName, config,
48
+ /** Optional OAuth provider for HTTP servers. Stdio servers ignore this. */
49
+ authProvider) {
50
+ this.serverName = serverName;
51
+ this.config = config;
52
+ this.authProvider = authProvider;
53
+ }
54
+ /** Spawn / dial the server and complete the MCP initialize handshake.
55
+ * On success, populates internal tool + resource caches. On failure,
56
+ * cleans up the transport (no zombie subprocess) and re-throws. */
57
+ async connect() {
58
+ const timeout = this.config.timeout ?? DEFAULT_CONNECT_TIMEOUT_MS;
59
+ this.transport = this.buildTransport();
60
+ this.client = new Client(CLIENT_INFO, { capabilities: {} });
61
+ // SDK's connect() runs the initialize roundtrip and resolves once the
62
+ // server has acknowledged. Race it against an explicit timer because
63
+ // a stuck stdio child (e.g. npx hanging on registry fetch) wouldn't
64
+ // surface as an error otherwise — it'd just sit there.
65
+ const ctrl = new AbortController();
66
+ const timer = setTimeout(() => ctrl.abort(), timeout);
67
+ try {
68
+ await this.client.connect(this.transport, { signal: ctrl.signal });
69
+ }
70
+ catch (err) {
71
+ // UnauthorizedError is the expected throw during an OAuth flow:
72
+ // the SDK has called redirectToAuthorization and now wants the
73
+ // caller to finishAuth(code) on the SAME transport. If we tear
74
+ // down here, runOAuthDance loses its handle and can't complete
75
+ // the exchange. Leave transport + client alive; the caller
76
+ // (runOAuthDance) or finally-shutdown path will clean up. For
77
+ // any other error we still safeClose to avoid leaking a child
78
+ // process / dangling HTTP connection.
79
+ if (!isUnauthorizedError(err)) {
80
+ await this.safeClose();
81
+ }
82
+ throw this.enrichError(err);
83
+ }
84
+ finally {
85
+ clearTimeout(timer);
86
+ }
87
+ // Discover capabilities. Tools/resources are independent — a server
88
+ // can offer one without the other — and we tolerate either listing
89
+ // throwing (some servers reject `listResources` if they have none).
90
+ try {
91
+ const tools = await this.client.listTools();
92
+ this.cachedTools = (tools.tools ?? []).map((t) => ({
93
+ name: t.name,
94
+ description: t.description ?? '',
95
+ inputSchema: t.inputSchema ?? {},
96
+ }));
97
+ }
98
+ catch (err) {
99
+ debugLog('mcp.listTools-failed', `${this.serverName}: ${String(err)}`);
100
+ this.cachedTools = [];
101
+ }
102
+ try {
103
+ const resources = await this.client.listResources();
104
+ this.cachedResources = (resources.resources ?? []).map((r) => ({
105
+ uri: r.uri,
106
+ name: r.name ?? r.uri,
107
+ description: r.description,
108
+ mimeType: r.mimeType,
109
+ serverName: this.serverName,
110
+ }));
111
+ }
112
+ catch (err) {
113
+ debugLog('mcp.listResources-failed', `${this.serverName}: ${String(err)}`);
114
+ this.cachedResources = [];
115
+ }
116
+ return {
117
+ toolCount: this.cachedTools.length,
118
+ resourceCount: this.cachedResources.length,
119
+ };
120
+ }
121
+ /** Tools discovered at connect time. Stable for the connection lifetime;
122
+ * refresh by calling connect() again on a fresh McpClient. */
123
+ tools() {
124
+ return this.cachedTools;
125
+ }
126
+ resources() {
127
+ return this.cachedResources;
128
+ }
129
+ /** Connect with a full interactive OAuth round-trip.
130
+ *
131
+ * The MCP SDK's StreamableHTTP transport handles auth lazily: a fresh
132
+ * connect with no stored token calls `authProvider.redirectToAuthorization`
133
+ * and then throws `UnauthorizedError` because the token-exchange step
134
+ * has to wait for the user. The caller is expected to wait for the
135
+ * redirect callback to land, hand the authorization code to
136
+ * `transport.finishAuth(code)`, then retry connect — at which point
137
+ * tokens are saved and the next attempt succeeds.
138
+ *
139
+ * We encapsulate that dance here so that the `/mcp auth` handler can
140
+ * opt into "drive OAuth to completion" without knowing about
141
+ * `finishAuth`. The default `connect()` path keeps the OAuth provider
142
+ * PASSIVE — `redirectToAuthorization` is a no-op until we flip
143
+ * `setInteractive(true)` here, so CLI boot doesn't accidentally pop a
144
+ * browser window for servers in `needs_auth`. */
145
+ async connectWithOAuth(hooks = {}) {
146
+ if (!this.authProvider) {
147
+ throw new Error(`MCP server "${this.serverName}" has no OAuth provider configured`);
148
+ }
149
+ if (!(this.authProvider instanceof McpOAuthProvider)) {
150
+ // Allow third-party providers but skip our `waitForAuthCode` hook —
151
+ // they're expected to handle the flow themselves.
152
+ return this.connect();
153
+ }
154
+ const provider = this.authProvider;
155
+ // Eagerly start the callback server so the real loopback port is
156
+ // bound to `clientMetadata.redirect_uris` and `redirectUrl` BEFORE
157
+ // the SDK builds the dynamic-registration request. Otherwise we
158
+ // register with a port-less placeholder and Sentry (and any other
159
+ // auth server that doesn't honour RFC 8252 §7.3 loopback any-port)
160
+ // rejects the auth URL's real-port redirect_uri as "Invalid".
161
+ await provider.prepareForAuth();
162
+ // Tee the browser-open notification through the caller's hook so the
163
+ // /mcp auth handler can print into the CLI scrollback alongside the
164
+ // provider's own onOpenBrowser callback. We monkey-patch the method
165
+ // for the lifetime of THIS call (try/finally restores it). The
166
+ // provider doesn't expose an event API, but patching one method on
167
+ // one instance for one flow is bounded enough to be safe.
168
+ const originalRedirect = provider.redirectToAuthorization.bind(provider);
169
+ if (hooks.onBrowserOpen) {
170
+ provider.redirectToAuthorization = async (url) => {
171
+ try {
172
+ hooks.onBrowserOpen?.(url.toString());
173
+ }
174
+ catch {
175
+ // Hook failures must not abort the OAuth flow.
176
+ }
177
+ return originalRedirect(url);
178
+ };
179
+ }
180
+ try {
181
+ return await this.runOAuthDance();
182
+ }
183
+ finally {
184
+ provider.setInteractive(false);
185
+ if (hooks.onBrowserOpen) {
186
+ provider.redirectToAuthorization = originalRedirect;
187
+ }
188
+ }
189
+ }
190
+ /** The actual two-phase connect: attempt-1 fires redirect, then we
191
+ * wait for the user, finish the auth, attempt-2 lands a real
192
+ * session. Both attempts share `cachedTools` / `cachedResources`. */
193
+ async runOAuthDance() {
194
+ const provider = this.authProvider;
195
+ // First attempt: most likely throws UnauthorizedError after the
196
+ // browser has been launched. If tokens were somehow already valid
197
+ // (stale state on disk) this succeeds and we short-circuit out.
198
+ try {
199
+ return await this.connect();
200
+ }
201
+ catch (err) {
202
+ // Anything that isn't "we need to wait for the user" propagates.
203
+ if (!isUnauthorizedError(err)) {
204
+ provider.cancel();
205
+ throw err;
206
+ }
207
+ }
208
+ // The provider has already called redirectToAuthorization (the SDK
209
+ // does that internally before throwing). Now wait for the user to
210
+ // come back via the callback server, then complete the exchange.
211
+ const { code } = await provider.waitForAuthCode();
212
+ const transport = this.transport;
213
+ if (!(transport instanceof StreamableHTTPClientTransport)) {
214
+ throw new Error(`Internal error: OAuth flow expected an HTTP transport for "${this.serverName}"`);
215
+ }
216
+ await transport.finishAuth(code);
217
+ // Tokens are now saved. The first attempt left the client + transport
218
+ // in a half-open state (the SDK's connect threw mid-handshake); we
219
+ // need a clean transport for the retry, so close and rebuild. This
220
+ // also means the SDK's initialize roundtrip happens against a fresh
221
+ // socket, avoiding any "already connected" / state-leak surprises.
222
+ await this.safeClose();
223
+ return this.connect();
224
+ }
225
+ async callTool(name, args, signal) {
226
+ if (!this.client)
227
+ throw new Error(`MCP server "${this.serverName}" is not connected`);
228
+ const result = await this.client.callTool({ name, arguments: args }, undefined, { signal });
229
+ return flattenCallResult(result);
230
+ }
231
+ async readResource(uri, signal) {
232
+ if (!this.client)
233
+ throw new Error(`MCP server "${this.serverName}" is not connected`);
234
+ const result = await this.client.readResource({ uri }, { signal });
235
+ // Resources return an array of content blocks; concatenate text
236
+ // representations, preserving the first mimeType for the caller.
237
+ const parts = [];
238
+ let mimeType;
239
+ for (const c of result.contents ?? []) {
240
+ mimeType ??= c.mimeType;
241
+ const text = c.text;
242
+ if (typeof text === 'string')
243
+ parts.push(text);
244
+ else if (c.blob !== undefined) {
245
+ parts.push(`[binary content omitted, mimeType=${mimeType ?? 'unknown'}]`);
246
+ }
247
+ }
248
+ return { text: parts.join('\n'), mimeType };
249
+ }
250
+ /** Snapshot the last N stderr lines for diagnostics. Empty for HTTP. */
251
+ stderr() {
252
+ return this.stderrTail.join('\n');
253
+ }
254
+ async close() {
255
+ await this.safeClose();
256
+ }
257
+ // ── internals ──────────────────────────────────────────────────────────
258
+ buildTransport() {
259
+ if (isStdioConfig(this.config)) {
260
+ const t = new StdioClientTransport({
261
+ command: this.config.command,
262
+ args: this.config.args,
263
+ env: this.config.env,
264
+ cwd: this.config.cwd,
265
+ // Pipe stderr so we can capture diagnostics. Default "inherit"
266
+ // would dump the child's noise into the parent CLI's terminal,
267
+ // scrambling our cell-buffer UI.
268
+ stderr: 'pipe',
269
+ });
270
+ const stderr = t.stderr;
271
+ if (stderr) {
272
+ stderr.on('data', (chunk) => {
273
+ const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
274
+ for (const line of text.split(/\r?\n/)) {
275
+ if (!line)
276
+ continue;
277
+ this.stderrTail.push(line);
278
+ if (this.stderrTail.length > STDERR_TAIL_LINES)
279
+ this.stderrTail.shift();
280
+ }
281
+ });
282
+ }
283
+ return t;
284
+ }
285
+ if (isHttpConfig(this.config)) {
286
+ return new StreamableHTTPClientTransport(new URL(this.config.url), {
287
+ requestInit: this.config.headers ? { headers: this.config.headers } : undefined,
288
+ authProvider: this.authProvider,
289
+ });
290
+ }
291
+ // Schema validation upstream should prevent this, but be defensive.
292
+ throw new Error(`mcp server "${this.serverName}": unrecognised config shape`);
293
+ }
294
+ async safeClose() {
295
+ // SDK's Client.close() also closes the transport. We try client first
296
+ // because it sends a proper shutdown notification; falling back to
297
+ // transport.close() if the client was never built (e.g. constructor
298
+ // threw before assignment).
299
+ try {
300
+ if (this.client) {
301
+ await this.client.close();
302
+ }
303
+ else if (this.transport) {
304
+ await this.transport.close();
305
+ }
306
+ }
307
+ catch (err) {
308
+ debugLog('mcp.close-error', `${this.serverName}: ${String(err)}`);
309
+ }
310
+ finally {
311
+ this.client = null;
312
+ this.transport = null;
313
+ }
314
+ }
315
+ /** Attach stderr tail (if any) to a connect error so /mcp list shows
316
+ * something more useful than "Connection closed". */
317
+ enrichError(err) {
318
+ const base = err instanceof Error ? err : new Error(String(err));
319
+ if (this.stderrTail.length === 0)
320
+ return base;
321
+ const tail = this.stderrTail.slice(-5).join(' | ');
322
+ const enriched = new Error(`${base.message} — stderr: ${tail}`);
323
+ enriched.stack = base.stack;
324
+ return enriched;
325
+ }
326
+ }
327
+ /** Pattern-match an UnauthorizedError from the SDK without depending
328
+ * on instanceof (which can be fragile across bundling boundaries when
329
+ * the SDK is duplicated under different esm/cjs roots). The SDK exports
330
+ * the class directly though, so we use both checks. */
331
+ function isUnauthorizedError(err) {
332
+ if (err instanceof UnauthorizedError)
333
+ return true;
334
+ if (err instanceof Error) {
335
+ if (err.name === 'UnauthorizedError')
336
+ return true;
337
+ if (/unauthorized|401/i.test(err.message))
338
+ return true;
339
+ }
340
+ return false;
341
+ }
342
+ /** Flatten MCP call result content blocks into a single string.
343
+ * MCP responses are an array of `{ type: "text" | "image" | ... }`
344
+ * blocks. For tool_result we only care about the text; images/audio are
345
+ * noted but not actually surfaced (the agent loop doesn't ingest images
346
+ * from tool results, only from user input). */
347
+ function flattenCallResult(result) {
348
+ const r = result;
349
+ const blocks = Array.isArray(r.content) ? r.content : [];
350
+ const parts = [];
351
+ for (const b of blocks) {
352
+ const block = b;
353
+ if (block.type === 'text' && typeof block.text === 'string') {
354
+ parts.push(block.text);
355
+ }
356
+ else if (block.type === 'image') {
357
+ parts.push(`[image content omitted, mimeType=${block.mimeType ?? 'unknown'}]`);
358
+ }
359
+ else if (block.type === 'resource') {
360
+ // Embedded resource — surface a one-line marker + any nested text.
361
+ const nested = block.resource;
362
+ if (nested?.text)
363
+ parts.push(nested.text);
364
+ else if (nested?.uri)
365
+ parts.push(`[resource: ${nested.uri}]`);
366
+ }
367
+ else if (block.type) {
368
+ parts.push(`[${block.type} content]`);
369
+ }
370
+ }
371
+ return {
372
+ text: parts.join('\n').trim() || '(empty response)',
373
+ isError: r.isError === true,
374
+ };
375
+ }
376
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { z } from 'zod';
2
+ import type { McpServerConfig } from './types.js';
3
+ export declare const mcpServersSchema: z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
4
+ command: z.ZodOptional<z.ZodString>;
5
+ args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
7
+ cwd: z.ZodOptional<z.ZodString>;
8
+ url: z.ZodOptional<z.ZodString>;
9
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
10
+ timeout: z.ZodOptional<z.ZodNumber>;
11
+ enabled: z.ZodOptional<z.ZodBoolean>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ timeout?: number | undefined;
14
+ env?: Record<string, string> | undefined;
15
+ cwd?: string | undefined;
16
+ args?: string[] | undefined;
17
+ url?: string | undefined;
18
+ command?: string | undefined;
19
+ headers?: Record<string, string> | undefined;
20
+ enabled?: boolean | undefined;
21
+ }, {
22
+ timeout?: number | undefined;
23
+ env?: Record<string, string> | undefined;
24
+ cwd?: string | undefined;
25
+ args?: string[] | undefined;
26
+ url?: string | undefined;
27
+ command?: string | undefined;
28
+ headers?: Record<string, string> | undefined;
29
+ enabled?: boolean | undefined;
30
+ }>, {
31
+ timeout?: number | undefined;
32
+ env?: Record<string, string> | undefined;
33
+ cwd?: string | undefined;
34
+ args?: string[] | undefined;
35
+ url?: string | undefined;
36
+ command?: string | undefined;
37
+ headers?: Record<string, string> | undefined;
38
+ enabled?: boolean | undefined;
39
+ }, {
40
+ timeout?: number | undefined;
41
+ env?: Record<string, string> | undefined;
42
+ cwd?: string | undefined;
43
+ args?: string[] | undefined;
44
+ url?: string | undefined;
45
+ command?: string | undefined;
46
+ headers?: Record<string, string> | undefined;
47
+ enabled?: boolean | undefined;
48
+ }>>;
49
+ /** Validate a single server config; throw with a context-tagged message
50
+ * if it fails. Server name is included so the error tells the user which
51
+ * entry in their config.json is broken. */
52
+ export declare function parseServerConfig(name: string, raw: unknown): McpServerConfig;
53
+ /** Validate the entire `mcpServers` block. Returns a partial result:
54
+ * every entry that parsed cleanly is included; broken ones surface in
55
+ * `errors` so the loader can mark them `failed` without aborting the
56
+ * whole config. */
57
+ export declare function parseServersBlock(raw: unknown): {
58
+ servers: Record<string, McpServerConfig>;
59
+ errors: Array<{
60
+ name: string;
61
+ message: string;
62
+ }>;
63
+ };
64
+ //# sourceMappingURL=config-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../src/mcp/config-schema.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA6CjD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAA4C,CAAA;AAEzE;;4CAE4C;AAC5C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,eAAe,CAO7E;AAED;;;oBAGoB;AACpB,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG;IAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACxC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD,CAmBA"}
@@ -0,0 +1,86 @@
1
+ // @x-code-cli/core — MCP config Zod schema
2
+ //
3
+ // Validates the `mcpServers` field of ~/.x-code/config.json (and the
4
+ // project-level .x-code/config.json). One schema covers both stdio and
5
+ // streamable-http servers; the union discriminator is field presence:
6
+ // `command` → stdio, `url` → http. Configs that have neither (or both)
7
+ // are rejected before we try to spawn anything.
8
+ import { z } from 'zod';
9
+ /** Single permissive schema covering both transports. Field presence
10
+ * (`command` vs `url`) is the discriminator, enforced via superRefine
11
+ * rather than z.union — union's per-variant validation hides our
12
+ * "exactly one of" rule when neither field is present (Zod just says
13
+ * "Invalid input" because no variant matched). With one flat schema
14
+ * + superRefine we get readable error messages for every misshape. */
15
+ const serverSchema = z
16
+ .object({
17
+ command: z.string().min(1).optional(),
18
+ args: z.array(z.string()).optional(),
19
+ env: z.record(z.string(), z.string()).optional(),
20
+ cwd: z.string().optional(),
21
+ url: z.string().url().optional(),
22
+ headers: z.record(z.string(), z.string()).optional(),
23
+ timeout: z.number().int().positive().optional(),
24
+ enabled: z.boolean().optional(),
25
+ })
26
+ .superRefine((v, ctx) => {
27
+ const hasCommand = typeof v.command === 'string';
28
+ const hasUrl = typeof v.url === 'string';
29
+ if (hasCommand && hasUrl) {
30
+ ctx.addIssue({
31
+ code: z.ZodIssueCode.custom,
32
+ message: 'mcpServers entry has both `command` and `url` — set only one',
33
+ });
34
+ }
35
+ if (!hasCommand && !hasUrl) {
36
+ ctx.addIssue({
37
+ code: z.ZodIssueCode.custom,
38
+ message: 'mcpServers entry must set either `command` (stdio) or `url` (http)',
39
+ });
40
+ }
41
+ // Cross-field validation: HTTP-only fields with stdio config, and
42
+ // vice versa. Not strictly required (extra fields are ignored at
43
+ // runtime) but the error message catches typos early.
44
+ if (hasCommand && typeof v.headers !== 'undefined') {
45
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: '`headers` is only valid for HTTP servers' });
46
+ }
47
+ if (hasUrl && (v.args || v.env || v.cwd)) {
48
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: '`args`/`env`/`cwd` are only valid for stdio servers' });
49
+ }
50
+ });
51
+ export const mcpServersSchema = z.record(z.string().min(1), serverSchema);
52
+ /** Validate a single server config; throw with a context-tagged message
53
+ * if it fails. Server name is included so the error tells the user which
54
+ * entry in their config.json is broken. */
55
+ export function parseServerConfig(name, raw) {
56
+ const result = serverSchema.safeParse(raw);
57
+ if (!result.success) {
58
+ const issues = result.error.issues.map((i) => i.message).join('; ');
59
+ throw new Error(`mcpServers.${name}: ${issues}`);
60
+ }
61
+ return result.data;
62
+ }
63
+ /** Validate the entire `mcpServers` block. Returns a partial result:
64
+ * every entry that parsed cleanly is included; broken ones surface in
65
+ * `errors` so the loader can mark them `failed` without aborting the
66
+ * whole config. */
67
+ export function parseServersBlock(raw) {
68
+ const servers = {};
69
+ const errors = [];
70
+ if (raw === undefined || raw === null)
71
+ return { servers, errors };
72
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
73
+ errors.push({ name: '(root)', message: 'mcpServers must be an object' });
74
+ return { servers, errors };
75
+ }
76
+ for (const [name, entry] of Object.entries(raw)) {
77
+ try {
78
+ servers[name] = parseServerConfig(name, entry);
79
+ }
80
+ catch (err) {
81
+ errors.push({ name, message: err instanceof Error ? err.message : String(err) });
82
+ }
83
+ }
84
+ return { servers, errors };
85
+ }
86
+ //# sourceMappingURL=config-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/mcp/config-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { type McpServerConfig } from './types.js';
2
+ export type ConfigScope = 'user' | 'project';
3
+ /** Where each scope's config.json lives. Mirrors the same paths the loader
4
+ * reads from, so a write here is guaranteed to be picked up on the next
5
+ * load (or `/mcp refresh`). */
6
+ export declare function getConfigPath(scope: ConfigScope, cwd: string): string;
7
+ /** Where a given server name currently lives. Returned to the App.tsx
8
+ * caller so `/mcp remove` can auto-target the right scope (and detect
9
+ * the rare both-scopes ambiguity that forces an explicit --scope). */
10
+ export type DetectScopeResult = {
11
+ kind: 'not-found';
12
+ } | {
13
+ kind: 'user';
14
+ } | {
15
+ kind: 'project';
16
+ } | {
17
+ kind: 'both';
18
+ };
19
+ export declare function detectScope(name: string, cwd: string): Promise<DetectScopeResult>;
20
+ export declare function serverExists(name: string, scope: ConfigScope, cwd: string): Promise<boolean>;
21
+ /** Add a server to the given scope's config.json. Refuses to overwrite —
22
+ * caller must check duplicates first via `serverExists` and surface a
23
+ * helpful error including current vs. attempted config. */
24
+ export declare function writeServerToConfig(name: string, config: McpServerConfig, scope: ConfigScope, cwd: string): Promise<{
25
+ path: string;
26
+ }>;
27
+ /** Remove a server from the given scope's config.json. Idempotent: returns
28
+ * `removed: false` when the name wasn't present (or the file didn't exist).
29
+ * Leaves the file with an empty `mcpServers: {}` rather than deleting the
30
+ * field — preserves the spot for future adds and avoids churn that would
31
+ * surprise users diffing the file in git. */
32
+ export declare function removeServerFromConfig(name: string, scope: ConfigScope, cwd: string): Promise<{
33
+ path: string;
34
+ removed: boolean;
35
+ }>;
36
+ /** Read the current config for `name` from the given scope, for the
37
+ * "already exists, here's what's there" path of /mcp add. Returns null
38
+ * if not present. Best-effort: a malformed entry returns null rather
39
+ * than throwing — the duplicate-check use case shouldn't crash. */
40
+ export declare function readServerConfig(name: string, scope: ConfigScope, cwd: string): Promise<unknown | null>;
41
+ //# sourceMappingURL=config-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-writer.d.ts","sourceRoot":"","sources":["../../src/mcp/config-writer.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5C;;gCAEgC;AAChC,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGrE;AAqCD;;uEAEuE;AACvE,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAMvF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKlG;AAED;;4DAE4D;AAC5D,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB3B;AAED;;;;8CAI8C;AAC9C,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAkB7C;AAED;;;oEAGoE;AACpE,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAU7G"}