@x-code-cli/core 0.2.9 → 0.3.0
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/dist/agent/compression.d.ts +12 -2
- package/dist/agent/compression.d.ts.map +1 -1
- package/dist/agent/compression.js +51 -2
- package/dist/agent/compression.js.map +1 -1
- package/dist/agent/file-ingest.js +2 -2
- package/dist/agent/file-ingest.js.map +1 -1
- package/dist/agent/loop-state.d.ts +3 -2
- package/dist/agent/loop-state.d.ts.map +1 -1
- package/dist/agent/loop-state.js.map +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +140 -9
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory-extractor.js +5 -5
- package/dist/agent/memory-extractor.js.map +1 -1
- package/dist/agent/plan-storage.js +1 -1
- package/dist/agent/plan-storage.js.map +1 -1
- package/dist/agent/sub-agents/index.d.ts +2 -1
- package/dist/agent/sub-agents/index.d.ts.map +1 -1
- package/dist/agent/sub-agents/index.js +1 -1
- package/dist/agent/sub-agents/index.js.map +1 -1
- package/dist/agent/sub-agents/loader.d.ts +13 -3
- package/dist/agent/sub-agents/loader.d.ts.map +1 -1
- package/dist/agent/sub-agents/loader.js +36 -9
- package/dist/agent/sub-agents/loader.js.map +1 -1
- package/dist/agent/sub-agents/registry.d.ts +18 -1
- package/dist/agent/sub-agents/registry.d.ts.map +1 -1
- package/dist/agent/sub-agents/registry.js +38 -5
- package/dist/agent/sub-agents/registry.js.map +1 -1
- package/dist/agent/sub-agents/runner.d.ts.map +1 -1
- package/dist/agent/sub-agents/runner.js +45 -1
- package/dist/agent/sub-agents/runner.js.map +1 -1
- package/dist/agent/sub-agents/types.d.ts +4 -1
- package/dist/agent/sub-agents/types.d.ts.map +1 -1
- package/dist/agent/system-prompt.d.ts +21 -0
- package/dist/agent/system-prompt.d.ts.map +1 -1
- package/dist/agent/system-prompt.js +68 -2
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/tool-execution.d.ts.map +1 -1
- package/dist/agent/tool-execution.js +220 -1
- package/dist/agent/tool-execution.js.map +1 -1
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +3 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/loader.d.ts +13 -0
- package/dist/commands/loader.d.ts.map +1 -0
- package/dist/commands/loader.js +93 -0
- package/dist/commands/loader.js.map +1 -0
- package/dist/commands/registry.d.ts +44 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +102 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/types.d.ts +23 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +12 -10
- package/dist/config/index.js.map +1 -1
- package/dist/hooks/bus.d.ts +54 -0
- package/dist/hooks/bus.d.ts.map +1 -0
- package/dist/hooks/bus.js +165 -0
- package/dist/hooks/bus.js.map +1 -0
- package/dist/hooks/config-schema.d.ts +854 -0
- package/dist/hooks/config-schema.d.ts.map +1 -0
- package/dist/hooks/config-schema.js +79 -0
- package/dist/hooks/config-schema.js.map +1 -0
- package/dist/hooks/executor.d.ts +16 -0
- package/dist/hooks/executor.d.ts.map +1 -0
- package/dist/hooks/executor.js +183 -0
- package/dist/hooks/executor.js.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/registry.d.ts +23 -0
- package/dist/hooks/registry.d.ts.map +1 -0
- package/dist/hooks/registry.js +49 -0
- package/dist/hooks/registry.js.map +1 -0
- package/dist/hooks/types.d.ts +165 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +25 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/hooks/variables.d.ts +22 -0
- package/dist/hooks/variables.d.ts.map +1 -0
- package/dist/hooks/variables.js +80 -0
- package/dist/hooks/variables.js.map +1 -0
- package/dist/index.d.ts +56 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/auto-memory.d.ts +1 -1
- package/dist/knowledge/auto-memory.d.ts.map +1 -1
- package/dist/knowledge/auto-memory.js +10 -10
- package/dist/knowledge/auto-memory.js.map +1 -1
- package/dist/knowledge/loader.js +12 -12
- package/dist/knowledge/loader.js.map +1 -1
- package/dist/mcp/arg-parser.d.ts +49 -0
- package/dist/mcp/arg-parser.d.ts.map +1 -0
- package/dist/mcp/arg-parser.js +357 -0
- package/dist/mcp/arg-parser.js.map +1 -0
- package/dist/mcp/client.d.ts +73 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +376 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config-schema.d.ts +64 -0
- package/dist/mcp/config-schema.d.ts.map +1 -0
- package/dist/mcp/config-schema.js +86 -0
- package/dist/mcp/config-schema.js.map +1 -0
- package/dist/mcp/config-writer.d.ts +41 -0
- package/dist/mcp/config-writer.d.ts.map +1 -0
- package/dist/mcp/config-writer.js +138 -0
- package/dist/mcp/config-writer.js.map +1 -0
- package/dist/mcp/env-safety.d.ts +12 -0
- package/dist/mcp/env-safety.d.ts.map +1 -0
- package/dist/mcp/env-safety.js +80 -0
- package/dist/mcp/env-safety.js.map +1 -0
- package/dist/mcp/expand-env.d.ts +14 -0
- package/dist/mcp/expand-env.d.ts.map +1 -0
- package/dist/mcp/expand-env.js +52 -0
- package/dist/mcp/expand-env.js.map +1 -0
- package/dist/mcp/loader.d.ts +81 -0
- package/dist/mcp/loader.d.ts.map +1 -0
- package/dist/mcp/loader.js +223 -0
- package/dist/mcp/loader.js.map +1 -0
- package/dist/mcp/name-mangling.d.ts +11 -0
- package/dist/mcp/name-mangling.d.ts.map +1 -0
- package/dist/mcp/name-mangling.js +82 -0
- package/dist/mcp/name-mangling.js.map +1 -0
- package/dist/mcp/oauth/callback-server.d.ts +25 -0
- package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
- package/dist/mcp/oauth/callback-server.js +118 -0
- package/dist/mcp/oauth/callback-server.js.map +1 -0
- package/dist/mcp/oauth/provider.d.ts +80 -0
- package/dist/mcp/oauth/provider.d.ts.map +1 -0
- package/dist/mcp/oauth/provider.js +292 -0
- package/dist/mcp/oauth/provider.js.map +1 -0
- package/dist/mcp/oauth/token-storage.d.ts +42 -0
- package/dist/mcp/oauth/token-storage.d.ts.map +1 -0
- package/dist/mcp/oauth/token-storage.js +121 -0
- package/dist/mcp/oauth/token-storage.js.map +1 -0
- package/dist/mcp/permissions.d.ts +28 -0
- package/dist/mcp/permissions.d.ts.map +1 -0
- package/dist/mcp/permissions.js +105 -0
- package/dist/mcp/permissions.js.map +1 -0
- package/dist/mcp/registry.d.ts +150 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +334 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/resources.d.ts +7 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +40 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/tool-bridge.d.ts +16 -0
- package/dist/mcp/tool-bridge.d.ts.map +1 -0
- package/dist/mcp/tool-bridge.js +56 -0
- package/dist/mcp/tool-bridge.js.map +1 -0
- package/dist/mcp/trust.d.ts +31 -0
- package/dist/mcp/trust.d.ts.map +1 -0
- package/dist/mcp/trust.js +103 -0
- package/dist/mcp/trust.js.map +1 -0
- package/dist/mcp/types.d.ts +73 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +13 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/permissions/session-store.d.ts +13 -2
- package/dist/permissions/session-store.d.ts.map +1 -1
- package/dist/permissions/session-store.js +264 -62
- package/dist/permissions/session-store.js.map +1 -1
- package/dist/plugins/consent.d.ts +87 -0
- package/dist/plugins/consent.d.ts.map +1 -0
- package/dist/plugins/consent.js +181 -0
- package/dist/plugins/consent.js.map +1 -0
- package/dist/plugins/enable-state.d.ts +34 -0
- package/dist/plugins/enable-state.d.ts.map +1 -0
- package/dist/plugins/enable-state.js +159 -0
- package/dist/plugins/enable-state.js.map +1 -0
- package/dist/plugins/installer.d.ts +64 -0
- package/dist/plugins/installer.d.ts.map +1 -0
- package/dist/plugins/installer.js +416 -0
- package/dist/plugins/installer.js.map +1 -0
- package/dist/plugins/integration.d.ts +91 -0
- package/dist/plugins/integration.d.ts.map +1 -0
- package/dist/plugins/integration.js +233 -0
- package/dist/plugins/integration.js.map +1 -0
- package/dist/plugins/loader.d.ts +69 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +243 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manifest.d.ts +23 -0
- package/dist/plugins/manifest.d.ts.map +1 -0
- package/dist/plugins/manifest.js +143 -0
- package/dist/plugins/manifest.js.map +1 -0
- package/dist/plugins/marketplace.d.ts +100 -0
- package/dist/plugins/marketplace.d.ts.map +1 -0
- package/dist/plugins/marketplace.js +529 -0
- package/dist/plugins/marketplace.js.map +1 -0
- package/dist/plugins/paths.d.ts +44 -0
- package/dist/plugins/paths.d.ts.map +1 -0
- package/dist/plugins/paths.js +89 -0
- package/dist/plugins/paths.js.map +1 -0
- package/dist/plugins/refresh.d.ts +61 -0
- package/dist/plugins/refresh.d.ts.map +1 -0
- package/dist/plugins/refresh.js +98 -0
- package/dist/plugins/refresh.js.map +1 -0
- package/dist/plugins/registry.d.ts +40 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +80 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/types.d.ts +225 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +16 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/plugins/user-config.d.ts +22 -0
- package/dist/plugins/user-config.d.ts.map +1 -0
- package/dist/plugins/user-config.js +96 -0
- package/dist/plugins/user-config.js.map +1 -0
- package/dist/providers/cache-control.d.ts +9 -0
- package/dist/providers/cache-control.d.ts.map +1 -1
- package/dist/providers/cache-control.js +31 -4
- package/dist/providers/cache-control.js.map +1 -1
- package/dist/skills/loader.d.ts +19 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +197 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/registry.d.ts +74 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +136 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/settings.d.ts +13 -0
- package/dist/skills/settings.d.ts.map +1 -0
- package/dist/skills/settings.js +100 -0
- package/dist/skills/settings.js.map +1 -0
- package/dist/tools/activate-skill.d.ts +5 -0
- package/dist/tools/activate-skill.d.ts.map +1 -0
- package/dist/tools/activate-skill.js +33 -0
- package/dist/tools/activate-skill.js.map +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/todo-write.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +2 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/types/index.d.ts +46 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils.d.ts +23 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +76 -20
- package/dist/utils.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +47 -0
- package/dist/version.js.map +1 -0
- 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"}
|