@schoolai/shipyard 1.2.0 → 2.0.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.
Files changed (45) hide show
  1. package/dist/{auth-XINVA3ZW.js → auth-V6KVU7VA.js} +4 -3
  2. package/dist/chunk-2H7UOFLK.js +11 -0
  3. package/dist/chunk-2H7UOFLK.js.map +1 -0
  4. package/dist/{chunk-FY3DRRGT.js → chunk-5OHYOIOG.js} +3 -3
  5. package/dist/chunk-5OHYOIOG.js.map +1 -0
  6. package/dist/{chunk-HS57GMAL.js → chunk-6AQKMCGK.js} +15 -9
  7. package/dist/chunk-6AQKMCGK.js.map +1 -0
  8. package/dist/chunk-BYFLMOF7.js +591 -0
  9. package/dist/chunk-BYFLMOF7.js.map +1 -0
  10. package/dist/{login-Q6PDH6HF.js → chunk-C4SKAWEG.js} +104 -40
  11. package/dist/chunk-C4SKAWEG.js.map +1 -0
  12. package/dist/chunk-DPMRSLYJ.js +109 -0
  13. package/dist/chunk-DPMRSLYJ.js.map +1 -0
  14. package/dist/{chunk-K3GFMEBF.js → chunk-JFEQEK53.js} +3 -3
  15. package/dist/chunk-JFEQEK53.js.map +1 -0
  16. package/dist/{chunk-U647WKG5.js → chunk-L6BFDLLZ.js} +264 -48
  17. package/dist/chunk-L6BFDLLZ.js.map +1 -0
  18. package/dist/{chunk-6VAYVPEL.js → chunk-ZU4YUN33.js} +8 -8
  19. package/dist/chunk-ZU4YUN33.js.map +1 -0
  20. package/dist/index.js +41 -49812
  21. package/dist/index.js.map +1 -1
  22. package/dist/login-JWAG3GPR.js +18 -0
  23. package/dist/login-JWAG3GPR.js.map +1 -0
  24. package/dist/{logout-XX5ULFHB.js → logout-PIKY2YCJ.js} +7 -6
  25. package/dist/logout-PIKY2YCJ.js.map +1 -0
  26. package/dist/mcp-servers-2MAQ6WKL.js +15 -0
  27. package/dist/mcp-servers-2MAQ6WKL.js.map +1 -0
  28. package/dist/plugin-mcp-linear.d.ts +2 -0
  29. package/dist/plugin-mcp-linear.js +1683 -0
  30. package/dist/plugin-mcp-linear.js.map +1 -0
  31. package/dist/serve-2FNONIDL.js +69583 -0
  32. package/dist/serve-2FNONIDL.js.map +1 -0
  33. package/dist/skills-NCKYNLUS.js +9 -0
  34. package/dist/skills-NCKYNLUS.js.map +1 -0
  35. package/dist/start-CQC22BQF.js +35 -0
  36. package/dist/start-CQC22BQF.js.map +1 -0
  37. package/package.json +8 -3
  38. package/dist/chunk-6VAYVPEL.js.map +0 -1
  39. package/dist/chunk-FY3DRRGT.js.map +0 -1
  40. package/dist/chunk-HS57GMAL.js.map +0 -1
  41. package/dist/chunk-K3GFMEBF.js.map +0 -1
  42. package/dist/chunk-U647WKG5.js.map +0 -1
  43. package/dist/login-Q6PDH6HF.js.map +0 -1
  44. package/dist/logout-XX5ULFHB.js.map +0 -1
  45. /package/dist/{auth-XINVA3ZW.js.map → auth-V6KVU7VA.js.map} +0 -0
@@ -0,0 +1,591 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-5OHYOIOG.js";
5
+ import {
6
+ external_exports
7
+ } from "./chunk-6AQKMCGK.js";
8
+
9
+ // src/shared/capabilities/mcp-servers.ts
10
+ import { readFile as readFile2 } from "fs/promises";
11
+ import { homedir as homedir2 } from "os";
12
+ import { join as join2 } from "path";
13
+
14
+ // src/shared/mcp/claude-code-credentials.ts
15
+ import { execFile as execFileCb } from "child_process";
16
+ import { readFile } from "fs/promises";
17
+ import { homedir } from "os";
18
+ import { join } from "path";
19
+ import { promisify } from "util";
20
+
21
+ // src/shared/mcp/schemas.ts
22
+ import {
23
+ OAuthMetadataSchema,
24
+ OAuthProtectedResourceMetadataSchema,
25
+ OpenIdProviderDiscoveryMetadataSchema
26
+ } from "@modelcontextprotocol/sdk/shared/auth.js";
27
+ var AuthorizationServerMetadataSchema = external_exports.union([
28
+ OAuthMetadataSchema,
29
+ OpenIdProviderDiscoveryMetadataSchema
30
+ ]);
31
+ var DiscoveryStateSchema = external_exports.object({
32
+ authorizationServerUrl: external_exports.string(),
33
+ authorizationServerMetadata: AuthorizationServerMetadataSchema.optional(),
34
+ resourceMetadata: OAuthProtectedResourceMetadataSchema.optional(),
35
+ resourceMetadataUrl: external_exports.string().optional()
36
+ }).passthrough();
37
+
38
+ // src/shared/mcp/claude-code-credentials.ts
39
+ var execFile = promisify(execFileCb);
40
+ var ClaudeCodeOAuthEntrySchema = external_exports.object({
41
+ serverName: external_exports.string().nullable().optional(),
42
+ serverUrl: external_exports.string().nullable().optional(),
43
+ clientId: external_exports.string().nullable().optional(),
44
+ clientSecret: external_exports.string().nullable().optional(),
45
+ accessToken: external_exports.string().nullable().optional(),
46
+ refreshToken: external_exports.string().nullable().optional(),
47
+ expiresAt: external_exports.number().nullable().optional(),
48
+ discoveryState: DiscoveryStateSchema.nullable().optional()
49
+ }).passthrough();
50
+ var CredentialsFileSchema = external_exports.object({
51
+ mcpOAuth: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
52
+ }).passthrough();
53
+ var CREDENTIALS_PATH = join(homedir(), ".claude", ".credentials.json");
54
+ var KEYCHAIN_SERVICE = "Claude Code-credentials";
55
+ var PluginMcpJsonSchema = external_exports.record(
56
+ external_exports.string(),
57
+ external_exports.object({
58
+ url: external_exports.string().optional(),
59
+ oauth: external_exports.object({ clientId: external_exports.string(), callbackPort: external_exports.number().optional() }).optional()
60
+ }).passthrough()
61
+ );
62
+ async function extractClientIdsFromPlugin(installPath, result) {
63
+ const raw = await readFile(join(installPath, ".mcp.json"), "utf-8");
64
+ const json = JSON.parse(raw);
65
+ const outer = external_exports.record(external_exports.string(), external_exports.unknown()).safeParse(json);
66
+ if (!outer.success) return;
67
+ const servers = outer.data.mcpServers ?? outer.data;
68
+ const validated = PluginMcpJsonSchema.safeParse(servers);
69
+ if (!validated.success) return;
70
+ for (const config of Object.values(validated.data)) {
71
+ if (config.url && config.oauth?.clientId) {
72
+ result.set(config.url, config.oauth.clientId);
73
+ }
74
+ }
75
+ }
76
+ async function readPluginClientIds() {
77
+ const result = /* @__PURE__ */ new Map();
78
+ try {
79
+ const installedPath = join(homedir(), ".claude", "plugins", "installed_plugins.json");
80
+ const InstalledPluginsSchema = external_exports.object({
81
+ plugins: external_exports.record(external_exports.string(), external_exports.array(external_exports.object({ installPath: external_exports.string().optional() }))).optional()
82
+ });
83
+ const parsed = InstalledPluginsSchema.safeParse(
84
+ JSON.parse(await readFile(installedPath, "utf-8"))
85
+ );
86
+ if (!parsed.success || !parsed.data.plugins) return result;
87
+ for (const installs of Object.values(parsed.data.plugins)) {
88
+ const installPath = installs?.[0]?.installPath;
89
+ if (!installPath) continue;
90
+ await extractClientIdsFromPlugin(installPath, result).catch(() => {
91
+ });
92
+ }
93
+ } catch {
94
+ }
95
+ return result;
96
+ }
97
+ async function readKeychainCredentials() {
98
+ if (process.platform !== "darwin") return null;
99
+ try {
100
+ const { stdout } = await execFile(
101
+ "security",
102
+ ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-w"],
103
+ { timeout: 5e3 }
104
+ );
105
+ const result = external_exports.record(external_exports.string(), external_exports.unknown()).safeParse(JSON.parse(stdout.trim()));
106
+ return result.success ? result.data : null;
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+ function scanEntries(mcpOAuth, serverUrl) {
112
+ let bestWithToken = null;
113
+ let clientId;
114
+ let clientSecret;
115
+ let discoveryState;
116
+ for (const value of Object.values(mcpOAuth)) {
117
+ const entryResult = ClaudeCodeOAuthEntrySchema.safeParse(value);
118
+ if (!entryResult.success) continue;
119
+ const entry = entryResult.data;
120
+ if (entry.serverUrl !== serverUrl) continue;
121
+ if (entry.clientId && !clientId) {
122
+ clientId = entry.clientId;
123
+ clientSecret = entry.clientSecret || void 0;
124
+ }
125
+ if (entry.discoveryState && !discoveryState) {
126
+ discoveryState = entry.discoveryState;
127
+ }
128
+ if (entry.accessToken && !bestWithToken) {
129
+ bestWithToken = entry;
130
+ }
131
+ }
132
+ return { bestWithToken, clientId, clientSecret, discoveryState };
133
+ }
134
+ function findMatchingEntry(credentialsData, serverName, serverUrl) {
135
+ const fileResult = CredentialsFileSchema.safeParse(credentialsData);
136
+ if (!fileResult.success) return null;
137
+ const mcpOAuth = fileResult.data.mcpOAuth;
138
+ if (!mcpOAuth) return null;
139
+ const { bestWithToken, clientId, clientSecret, discoveryState } = scanEntries(
140
+ mcpOAuth,
141
+ serverUrl
142
+ );
143
+ if (!bestWithToken && !clientId) return null;
144
+ return {
145
+ serverName: bestWithToken?.serverName ?? serverName,
146
+ serverUrl,
147
+ clientId: bestWithToken?.clientId || clientId,
148
+ clientSecret: bestWithToken?.clientSecret || clientSecret || void 0,
149
+ accessToken: bestWithToken?.accessToken || void 0,
150
+ refreshToken: bestWithToken?.refreshToken || void 0,
151
+ expiresAt: bestWithToken?.expiresAt ?? void 0,
152
+ discoveryState: bestWithToken?.discoveryState ?? discoveryState ?? void 0
153
+ };
154
+ }
155
+ async function readClaudeCodeCredentials(serverName, serverUrl) {
156
+ if (!serverUrl) return null;
157
+ try {
158
+ const keychainData = await readKeychainCredentials();
159
+ if (keychainData) {
160
+ const entry = findMatchingEntry(keychainData, serverName, serverUrl);
161
+ if (entry) {
162
+ logger.debug({ serverUrl }, "Found Claude Code credentials in macOS Keychain");
163
+ return entry;
164
+ }
165
+ }
166
+ } catch (err) {
167
+ logger.debug({ err }, "Failed to read Claude Code credentials from Keychain");
168
+ }
169
+ try {
170
+ const raw = await readFile(CREDENTIALS_PATH, "utf-8");
171
+ const fileResult = CredentialsFileSchema.safeParse(JSON.parse(raw));
172
+ if (fileResult.success) {
173
+ const entry = findMatchingEntry(fileResult.data, serverName, serverUrl);
174
+ if (entry) {
175
+ logger.debug({ serverUrl }, "Found Claude Code credentials in legacy file");
176
+ return entry;
177
+ }
178
+ }
179
+ return null;
180
+ } catch (err) {
181
+ logger.debug({ err }, "Failed to read Claude Code credentials from file");
182
+ return null;
183
+ }
184
+ }
185
+
186
+ // src/shared/mcp/resolve-servers.ts
187
+ var ENV_VAR_PATTERN = /\$\{([^}]+)\}/g;
188
+ var VAULT_REF_PATTERN = /^\$\{vault:([^}]+)\}$/;
189
+ function interpolateEnvVars(headers) {
190
+ const result = {};
191
+ for (const [key, value] of Object.entries(headers)) {
192
+ result[key] = value.replace(
193
+ ENV_VAR_PATTERN,
194
+ (_, varName) => process.env[varName] ?? ""
195
+ );
196
+ }
197
+ return result;
198
+ }
199
+ function resolveHeaders(server) {
200
+ if (!server.headers) return void 0;
201
+ return interpolateEnvVars(server.headers);
202
+ }
203
+ function resolveStdioEnv(env, store, log) {
204
+ if (!env) return void 0;
205
+ if (!store) return env;
206
+ const result = {};
207
+ let changed = false;
208
+ for (const [key, value] of Object.entries(env)) {
209
+ const match = VAULT_REF_PATTERN.exec(value);
210
+ if (match) {
211
+ const vaultKey = match[1] ?? "";
212
+ const token = store.getTokenSync(vaultKey);
213
+ if (token && !store.isExpired(token)) {
214
+ result[key] = token.accessToken;
215
+ changed = true;
216
+ log?.({
217
+ event: "vault_ref_resolved",
218
+ envKey: key,
219
+ vaultKey,
220
+ tokenType: token.tokenType,
221
+ hasExpiry: token.expiresAt !== void 0
222
+ });
223
+ continue;
224
+ }
225
+ log?.({
226
+ event: "vault_ref_unresolved",
227
+ envKey: key,
228
+ vaultKey,
229
+ tokenFound: token !== null,
230
+ tokenExpired: token ? store.isExpired(token) : false
231
+ });
232
+ }
233
+ result[key] = value;
234
+ }
235
+ return changed ? result : env;
236
+ }
237
+ function resolveEnabledMcpServers(overrides, available, mcpTokenStore) {
238
+ if (!overrides || !available) return void 0;
239
+ const enabledNames = new Set(overrides.filter((o) => o.enabled).map((o) => o.name));
240
+ if (enabledNames.size === 0) return {};
241
+ const result = {};
242
+ for (const server of available) {
243
+ if (!enabledNames.has(server.name)) continue;
244
+ if (server.type === "http" && server.url) {
245
+ result[server.name] = {
246
+ type: "http",
247
+ url: server.url,
248
+ headers: resolveHeaders(server)
249
+ };
250
+ } else if (server.type === "sse" && server.url) {
251
+ result[server.name] = {
252
+ type: "sse",
253
+ url: server.url,
254
+ headers: resolveHeaders(server)
255
+ };
256
+ } else if (server.command) {
257
+ result[server.name] = {
258
+ command: server.command,
259
+ args: server.args ?? void 0,
260
+ env: resolveStdioEnv(server.env, mcpTokenStore)
261
+ };
262
+ }
263
+ }
264
+ return result;
265
+ }
266
+
267
+ // src/shared/capabilities/account-integrations.ts
268
+ import { execFile as execFileCb2 } from "child_process";
269
+ import { promisify as promisify2 } from "util";
270
+ var execFile2 = promisify2(execFileCb2);
271
+ async function readAnthropicOAuthToken() {
272
+ if (process.platform !== "darwin") return null;
273
+ try {
274
+ const { stdout } = await execFile2(
275
+ "security",
276
+ ["find-generic-password", "-s", "Claude Code", "-w"],
277
+ { timeout: 5e3 }
278
+ );
279
+ const token = stdout.trim();
280
+ if (!token || token.startsWith("sk-")) return null;
281
+ return token;
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function parseClaudeAiServers(data) {
287
+ if (typeof data !== "object" || data === null) return [];
288
+ const obj = data;
289
+ if (!Array.isArray(obj.data)) return [];
290
+ const results = [];
291
+ for (const entry of obj.data) {
292
+ if (typeof entry !== "object" || entry === null) continue;
293
+ const rec = entry;
294
+ if (typeof rec.name === "string" && typeof rec.url === "string") {
295
+ results.push({ name: rec.name, url: rec.url });
296
+ }
297
+ }
298
+ return results;
299
+ }
300
+ async function fetchClaudeAiIntegrations(log) {
301
+ try {
302
+ const token = await readAnthropicOAuthToken();
303
+ if (!token) return [];
304
+ const controller = new AbortController();
305
+ const timeout = setTimeout(() => controller.abort(), 1e4);
306
+ try {
307
+ const response = await fetch("https://api.anthropic.com/v1/mcp_servers?limit=1000", {
308
+ headers: {
309
+ Authorization: `Bearer ${token}`
310
+ },
311
+ signal: controller.signal
312
+ });
313
+ if (!response.ok) {
314
+ const debugLog = log ?? ((entry) => logger.debug(entry, entry.event));
315
+ debugLog({
316
+ event: "claudeai_integrations_fetch_failed",
317
+ status: response.status
318
+ });
319
+ return [];
320
+ }
321
+ const body = await response.json();
322
+ const servers = parseClaudeAiServers(body);
323
+ return servers.map((s) => ({
324
+ name: s.name,
325
+ type: "http",
326
+ url: s.url,
327
+ enabled: true,
328
+ source: "claudeai",
329
+ authStatus: "unknown"
330
+ }));
331
+ } finally {
332
+ clearTimeout(timeout);
333
+ }
334
+ } catch (err) {
335
+ const debugLog = log ?? ((entry) => logger.debug(entry, entry.event));
336
+ debugLog({
337
+ event: "claudeai_integrations_fetch_error",
338
+ error: err instanceof Error ? err.message : String(err)
339
+ });
340
+ return [];
341
+ }
342
+ }
343
+
344
+ // src/shared/capabilities/mcp-servers.ts
345
+ var MCPStdioEntrySchema = external_exports.object({
346
+ type: external_exports.literal("stdio").optional(),
347
+ command: external_exports.string(),
348
+ args: external_exports.array(external_exports.string()).optional(),
349
+ env: external_exports.record(external_exports.string(), external_exports.string()).optional()
350
+ }).passthrough();
351
+ var MCPOAuthConfigSchema = external_exports.object({
352
+ clientId: external_exports.string().optional(),
353
+ callbackPort: external_exports.number().optional()
354
+ }).passthrough();
355
+ var MCPHttpEntrySchema = external_exports.object({
356
+ type: external_exports.literal("http"),
357
+ url: external_exports.string(),
358
+ headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
359
+ oauth: MCPOAuthConfigSchema.optional()
360
+ }).passthrough();
361
+ var MCPSSEEntrySchema = external_exports.object({
362
+ type: external_exports.literal("sse"),
363
+ url: external_exports.string(),
364
+ headers: external_exports.record(external_exports.string(), external_exports.string()).optional()
365
+ }).passthrough();
366
+ var MCPServerEntrySchema = external_exports.union([MCPStdioEntrySchema, MCPHttpEntrySchema, MCPSSEEntrySchema]);
367
+ var SECRET_PATTERNS = /^(sk-|ghp_|gho_|glpat-|xoxb-|xoxp-|Bearer\s|token\s)/i;
368
+ var SECRET_FLAGS = /* @__PURE__ */ new Set(["--api-key", "--token", "--secret", "--password", "--key", "-k"]);
369
+ function redactArgs(args) {
370
+ return args.map((arg, i) => {
371
+ if (SECRET_PATTERNS.test(arg)) return "***";
372
+ const prevArg = i > 0 ? args[i - 1] : void 0;
373
+ if (prevArg && SECRET_FLAGS.has(prevArg)) return "***";
374
+ const eqIdx = arg.indexOf("=");
375
+ if (eqIdx > 0 && SECRET_FLAGS.has(arg.slice(0, eqIdx))) {
376
+ return `${arg.slice(0, eqIdx + 1)}***`;
377
+ }
378
+ return arg;
379
+ });
380
+ }
381
+ function redactEnv(env) {
382
+ const result = {};
383
+ for (const key of Object.keys(env)) {
384
+ result[key] = "***";
385
+ }
386
+ return result;
387
+ }
388
+ function isHttpEntry(data) {
389
+ return "type" in data && data.type === "http";
390
+ }
391
+ function isSSEEntry(data) {
392
+ return "type" in data && data.type === "sse";
393
+ }
394
+ function entryToServerInfo(name, data, source) {
395
+ if (isHttpEntry(data)) {
396
+ return {
397
+ name,
398
+ type: "http",
399
+ url: data.url,
400
+ headers: data.headers,
401
+ oauth: data.oauth,
402
+ enabled: true,
403
+ source,
404
+ authStatus: "unknown"
405
+ };
406
+ }
407
+ if (isSSEEntry(data)) {
408
+ return {
409
+ name,
410
+ type: "sse",
411
+ url: data.url,
412
+ headers: data.headers,
413
+ enabled: true,
414
+ source,
415
+ authStatus: "unknown"
416
+ };
417
+ }
418
+ return {
419
+ name,
420
+ type: "stdio",
421
+ command: data.command,
422
+ args: data.args,
423
+ env: data.env,
424
+ enabled: true,
425
+ source,
426
+ authStatus: "unknown"
427
+ };
428
+ }
429
+ async function readMCPConfig(filePath, source, log) {
430
+ try {
431
+ const raw = await readFile2(filePath, "utf-8");
432
+ const json = JSON.parse(raw);
433
+ const entries = typeof json.mcpServers === "object" && json.mcpServers !== null ? json.mcpServers : json;
434
+ const servers = [];
435
+ for (const [name, value] of Object.entries(entries)) {
436
+ if (name === "mcpServers") continue;
437
+ const result = MCPServerEntrySchema.safeParse(value);
438
+ if (!result.success) {
439
+ const entry = {
440
+ event: "mcp_config_entry_invalid",
441
+ name,
442
+ source,
443
+ filePath,
444
+ error: result.error.message
445
+ };
446
+ if (log) log(entry);
447
+ else console.warn("[mcp-servers]", entry.event, entry.name, entry.error);
448
+ continue;
449
+ }
450
+ servers.push(entryToServerInfo(name, result.data, source));
451
+ }
452
+ return servers;
453
+ } catch {
454
+ return [];
455
+ }
456
+ }
457
+ async function readPluginMCPServers() {
458
+ const servers = [];
459
+ try {
460
+ const settingsPath = join2(homedir2(), ".claude", "settings.json");
461
+ const settingsRaw = await readFile2(settingsPath, "utf-8");
462
+ const settings = JSON.parse(settingsRaw);
463
+ if (!settings.enabledPlugins) return [];
464
+ const installedPath = join2(homedir2(), ".claude", "plugins", "installed_plugins.json");
465
+ const installedRaw = await readFile2(installedPath, "utf-8");
466
+ const installed = JSON.parse(installedRaw);
467
+ if (!installed.plugins) return [];
468
+ for (const [pluginId, enabled] of Object.entries(settings.enabledPlugins)) {
469
+ if (!enabled) continue;
470
+ const installs = installed.plugins[pluginId];
471
+ if (!installs || installs.length === 0) continue;
472
+ const installPath = installs[0]?.installPath;
473
+ if (!installPath) continue;
474
+ const mcpJsonPath = join2(installPath, ".mcp.json");
475
+ const pluginServers = await readMCPConfig(mcpJsonPath, "plugin");
476
+ servers.push(...pluginServers);
477
+ }
478
+ } catch {
479
+ }
480
+ return servers;
481
+ }
482
+ var SOURCE_PRIORITY = [
483
+ "claudeai",
484
+ "plugin",
485
+ "user",
486
+ "project",
487
+ "local",
488
+ "mcp-json"
489
+ ];
490
+ async function resolveClaudeCodeAuthStatuses(servers) {
491
+ for (const server of servers.values()) {
492
+ if (server.authStatus !== "unknown") continue;
493
+ if (server.type === "stdio") continue;
494
+ const ccCreds = await readClaudeCodeCredentials(server.name, server.url ?? "");
495
+ if (ccCreds?.accessToken && ccCreds.expiresAt && ccCreds.expiresAt > Date.now()) {
496
+ server.authStatus = "authenticated";
497
+ }
498
+ }
499
+ }
500
+ function resolveStdioAuthStatus(server, tokens, tokenStore) {
501
+ if (!server.env) return;
502
+ for (const value of Object.values(server.env)) {
503
+ const match = VAULT_REF_PATTERN.exec(value);
504
+ if (!match) continue;
505
+ const vaultKey = match[1] ?? "";
506
+ const token = tokens[vaultKey];
507
+ if (token) {
508
+ server.authStatus = tokenStore.isExpired(token) ? "unauthenticated" : "authenticated";
509
+ return;
510
+ }
511
+ }
512
+ }
513
+ async function resolveAuthStatuses(servers, tokenStore) {
514
+ const tokens = await tokenStore.getAllTokens();
515
+ for (const server of servers.values()) {
516
+ if (server.type === "stdio") {
517
+ resolveStdioAuthStatus(server, tokens, tokenStore);
518
+ continue;
519
+ }
520
+ const token = tokens[server.name];
521
+ if (token) {
522
+ server.authStatus = tokenStore.isExpired(token) ? "unauthenticated" : "authenticated";
523
+ }
524
+ }
525
+ await resolveClaudeCodeAuthStatuses(servers);
526
+ }
527
+ async function detectMCPServers(environments, tokenStore, log) {
528
+ const allServers = [];
529
+ const userSettingsPath = join2(homedir2(), ".claude", "settings.json");
530
+ const userLocalSettingsPath = join2(homedir2(), ".claude", "settings.local.json");
531
+ const userMcpJsonPath = join2(homedir2(), ".mcp.json");
532
+ const userClaudeJsonPath = join2(homedir2(), ".claude.json");
533
+ const [
534
+ userServers,
535
+ userLocalServers,
536
+ userMcpJsonServers,
537
+ userClaudeJsonServers,
538
+ pluginServers,
539
+ claudeAiServers
540
+ ] = await Promise.all([
541
+ readMCPConfig(userSettingsPath, "user", log),
542
+ readMCPConfig(userLocalSettingsPath, "user", log),
543
+ readMCPConfig(userMcpJsonPath, "user", log),
544
+ readMCPConfig(userClaudeJsonPath, "user", log),
545
+ readPluginMCPServers(),
546
+ fetchClaudeAiIntegrations(log)
547
+ ]);
548
+ allServers.push(
549
+ ...claudeAiServers,
550
+ ...pluginServers,
551
+ ...userServers,
552
+ ...userLocalServers,
553
+ ...userMcpJsonServers,
554
+ ...userClaudeJsonServers
555
+ );
556
+ for (const env of environments) {
557
+ const projectPath = join2(env.path, ".claude", "settings.json");
558
+ const localPath = join2(env.path, ".claude", "settings.local.json");
559
+ const mcpJsonPath = join2(env.path, ".mcp.json");
560
+ const [projectServers, localServers, mcpJsonServers] = await Promise.all([
561
+ readMCPConfig(projectPath, "project", log),
562
+ readMCPConfig(localPath, "local", log),
563
+ readMCPConfig(mcpJsonPath, "mcp-json", log)
564
+ ]);
565
+ allServers.push(...projectServers, ...localServers, ...mcpJsonServers);
566
+ }
567
+ const deduped = /* @__PURE__ */ new Map();
568
+ for (const server of allServers) {
569
+ const existing = deduped.get(server.name);
570
+ if (!existing || SOURCE_PRIORITY.indexOf(server.source) >= SOURCE_PRIORITY.indexOf(existing.source)) {
571
+ deduped.set(server.name, server);
572
+ }
573
+ }
574
+ if (tokenStore) {
575
+ await resolveAuthStatuses(deduped, tokenStore);
576
+ }
577
+ return [...deduped.values()];
578
+ }
579
+
580
+ export {
581
+ DiscoveryStateSchema,
582
+ readPluginClientIds,
583
+ readClaudeCodeCredentials,
584
+ resolveStdioEnv,
585
+ resolveEnabledMcpServers,
586
+ readAnthropicOAuthToken,
587
+ redactArgs,
588
+ redactEnv,
589
+ detectMCPServers
590
+ };
591
+ //# sourceMappingURL=chunk-BYFLMOF7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/capabilities/mcp-servers.ts","../src/shared/mcp/claude-code-credentials.ts","../src/shared/mcp/schemas.ts","../src/shared/mcp/resolve-servers.ts","../src/shared/capabilities/account-integrations.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { GitRepoInfo, MCPServerInfo, MCPServerSource } from '@shipyard/session';\nimport { z } from 'zod';\nimport { readClaudeCodeCredentials } from '../mcp/claude-code-credentials.js';\nimport { VAULT_REF_PATTERN } from '../mcp/resolve-servers.js';\nimport { fetchClaudeAiIntegrations } from './account-integrations.js';\n\nconst MCPStdioEntrySchema = z\n .object({\n type: z.literal('stdio').optional(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPOAuthConfigSchema = z\n .object({\n clientId: z.string().optional(),\n callbackPort: z.number().optional(),\n })\n .passthrough();\n\nconst MCPHttpEntrySchema = z\n .object({\n type: z.literal('http'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n oauth: MCPOAuthConfigSchema.optional(),\n })\n .passthrough();\n\nconst MCPSSEEntrySchema = z\n .object({\n type: z.literal('sse'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPServerEntrySchema = z.union([MCPStdioEntrySchema, MCPHttpEntrySchema, MCPSSEEntrySchema]);\n\nconst SECRET_PATTERNS = /^(sk-|ghp_|gho_|glpat-|xoxb-|xoxp-|Bearer\\s|token\\s)/i;\nconst SECRET_FLAGS = new Set(['--api-key', '--token', '--secret', '--password', '--key', '-k']);\n\nexport function redactArgs(args: string[]): string[] {\n return args.map((arg, i) => {\n if (SECRET_PATTERNS.test(arg)) return '***';\n const prevArg = i > 0 ? args[i - 1] : undefined;\n if (prevArg && SECRET_FLAGS.has(prevArg)) return '***';\n const eqIdx = arg.indexOf('=');\n if (eqIdx > 0 && SECRET_FLAGS.has(arg.slice(0, eqIdx))) {\n return `${arg.slice(0, eqIdx + 1)}***`;\n }\n return arg;\n });\n}\n\n/**\n * Redact all environment variable values. Env vars passed to MCP servers\n * frequently contain secrets (API keys, tokens) and should never be\n * exposed to the browser.\n */\nexport function redactEnv(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const key of Object.keys(env)) {\n result[key] = '***';\n }\n return result;\n}\n\ntype MCPStdioEntry = z.infer<typeof MCPStdioEntrySchema>;\ntype MCPHttpEntry = z.infer<typeof MCPHttpEntrySchema>;\ntype MCPSSEEntry = z.infer<typeof MCPSSEEntrySchema>;\n\nfunction isHttpEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPHttpEntry {\n return 'type' in data && data.type === 'http';\n}\n\nfunction isSSEEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPSSEEntry {\n return 'type' in data && data.type === 'sse';\n}\n\nfunction entryToServerInfo(\n name: string,\n data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry,\n source: MCPServerSource\n): MCPServerInfo {\n if (isHttpEntry(data)) {\n return {\n name,\n type: 'http',\n url: data.url,\n headers: data.headers,\n oauth: data.oauth,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n if (isSSEEntry(data)) {\n return {\n name,\n type: 'sse',\n url: data.url,\n headers: data.headers,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n return {\n name,\n type: 'stdio',\n command: data.command,\n args: data.args,\n env: data.env,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n}\n\nasync function readMCPConfig(\n filePath: string,\n source: MCPServerSource,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Promise<MCPServerInfo[]> {\n try {\n const raw = await readFile(filePath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; validated per-entry below\n const json = JSON.parse(raw) as Record<string, unknown>;\n\n const entries =\n typeof json.mcpServers === 'object' && json.mcpServers !== null ? json.mcpServers : json;\n\n const servers: MCPServerInfo[] = [];\n for (const [name, value] of Object.entries(entries)) {\n if (name === 'mcpServers') continue;\n const result = MCPServerEntrySchema.safeParse(value);\n if (!result.success) {\n const entry = {\n event: 'mcp_config_entry_invalid',\n name,\n source,\n filePath,\n error: result.error.message,\n };\n if (log) log(entry);\n // biome-ignore lint/suspicious/noConsole: diagnostic fallback when no structured logger is injected\n else console.warn('[mcp-servers]', entry.event, entry.name, entry.error);\n continue;\n }\n servers.push(entryToServerInfo(name, result.data, source));\n }\n return servers;\n } catch {\n return [];\n }\n}\n\nasync function readPluginMCPServers(): Promise<MCPServerInfo[]> {\n const servers: MCPServerInfo[] = [];\n try {\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n const settingsRaw = await readFile(settingsPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking enabledPlugins field only\n const settings = JSON.parse(settingsRaw) as { enabledPlugins?: Record<string, boolean> };\n if (!settings.enabledPlugins) return [];\n\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const installedRaw = await readFile(installedPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking plugins field only\n const installed = JSON.parse(installedRaw) as {\n plugins?: Record<string, Array<{ installPath?: string }>>;\n };\n if (!installed.plugins) return [];\n\n for (const [pluginId, enabled] of Object.entries(settings.enabledPlugins)) {\n if (!enabled) continue;\n const installs = installed.plugins[pluginId];\n if (!installs || installs.length === 0) continue;\n const installPath = installs[0]?.installPath;\n if (!installPath) continue;\n\n const mcpJsonPath = join(installPath, '.mcp.json');\n const pluginServers = await readMCPConfig(mcpJsonPath, 'plugin');\n servers.push(...pluginServers);\n }\n } catch {}\n return servers;\n}\n\n/** Priority order for source-based deduplication (higher index wins). */\nconst SOURCE_PRIORITY: readonly MCPServerSource[] = [\n 'claudeai',\n 'plugin',\n 'user',\n 'project',\n 'local',\n 'mcp-json',\n];\n\n/**\n * Check Claude Code's credential store for servers that still have\n * `authStatus === 'unknown'` after checking Shipyard's own token store.\n * If Claude Code has a valid (non-expired) token, mark the server as authenticated.\n */\nasync function resolveClaudeCodeAuthStatuses(servers: Map<string, MCPServerInfo>): Promise<void> {\n for (const server of servers.values()) {\n if (server.authStatus !== 'unknown') continue;\n if (server.type === 'stdio') continue;\n const ccCreds = await readClaudeCodeCredentials(server.name, server.url ?? '');\n if (ccCreds?.accessToken && ccCreds.expiresAt && ccCreds.expiresAt > Date.now()) {\n server.authStatus = 'authenticated';\n }\n }\n}\n\nfunction resolveStdioAuthStatus(\n server: MCPServerInfo,\n tokens: Record<string, import('../mcp/token-store.js').MCPOAuthToken>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): void {\n if (!server.env) return;\n for (const value of Object.values(server.env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (!match) continue;\n const vaultKey = match[1] ?? '';\n const token = tokens[vaultKey];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n return;\n }\n }\n}\n\nasync function resolveAuthStatuses(\n servers: Map<string, MCPServerInfo>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): Promise<void> {\n const tokens = await tokenStore.getAllTokens();\n for (const server of servers.values()) {\n if (server.type === 'stdio') {\n resolveStdioAuthStatus(server, tokens, tokenStore);\n continue;\n }\n const token = tokens[server.name];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n }\n }\n\n await resolveClaudeCodeAuthStatuses(servers);\n}\n\nexport async function detectMCPServers(\n environments: GitRepoInfo[],\n tokenStore?: import('../mcp/token-store.js').MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Promise<MCPServerInfo[]> {\n const allServers: MCPServerInfo[] = [];\n\n const userSettingsPath = join(homedir(), '.claude', 'settings.json');\n const userLocalSettingsPath = join(homedir(), '.claude', 'settings.local.json');\n const userMcpJsonPath = join(homedir(), '.mcp.json');\n const userClaudeJsonPath = join(homedir(), '.claude.json');\n\n const [\n userServers,\n userLocalServers,\n userMcpJsonServers,\n userClaudeJsonServers,\n pluginServers,\n claudeAiServers,\n ] = await Promise.all([\n readMCPConfig(userSettingsPath, 'user', log),\n readMCPConfig(userLocalSettingsPath, 'user', log),\n readMCPConfig(userMcpJsonPath, 'user', log),\n readMCPConfig(userClaudeJsonPath, 'user', log),\n readPluginMCPServers(),\n fetchClaudeAiIntegrations(log),\n ]);\n allServers.push(\n ...claudeAiServers,\n ...pluginServers,\n ...userServers,\n ...userLocalServers,\n ...userMcpJsonServers,\n ...userClaudeJsonServers\n );\n\n for (const env of environments) {\n const projectPath = join(env.path, '.claude', 'settings.json');\n const localPath = join(env.path, '.claude', 'settings.local.json');\n const mcpJsonPath = join(env.path, '.mcp.json');\n\n const [projectServers, localServers, mcpJsonServers] = await Promise.all([\n readMCPConfig(projectPath, 'project', log),\n readMCPConfig(localPath, 'local', log),\n readMCPConfig(mcpJsonPath, 'mcp-json', log),\n ]);\n\n allServers.push(...projectServers, ...localServers, ...mcpJsonServers);\n }\n\n const deduped = new Map<string, MCPServerInfo>();\n for (const server of allServers) {\n const existing = deduped.get(server.name);\n if (\n !existing ||\n SOURCE_PRIORITY.indexOf(server.source) >= SOURCE_PRIORITY.indexOf(existing.source)\n ) {\n deduped.set(server.name, server);\n }\n }\n\n if (tokenStore) {\n await resolveAuthStatuses(deduped, tokenStore);\n }\n\n return [...deduped.values()];\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';\nimport { z } from 'zod';\n\nimport { logger } from '../logger.js';\nimport { DiscoveryStateSchema } from './schemas.js';\n\nconst execFile = promisify(execFileCb);\n\n/**\n * Represents a single OAuth entry from Claude Code's credential store.\n *\n * Claude Code v2.1.81+ stores credentials in macOS Keychain under\n * service name \"Claude Code-credentials\". Older versions used\n * `~/.claude/.credentials.json`.\n *\n * The format is owned by Claude Code and may change without notice.\n * All fields are optional to tolerate schema drift.\n */\nexport interface ClaudeCodeOAuthEntry {\n serverName: string;\n serverUrl: string;\n clientId?: string;\n clientSecret?: string;\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: number;\n discoveryState?: OAuthDiscoveryState;\n}\n\n/**\n * Schema for a single entry in `mcpOAuth`. We use `.passthrough()` so\n * unknown future fields are preserved.\n *\n * CC stores `null` for cleared fields (e.g., `accessToken: null` after\n * logout). Zod's `.optional()` only accepts `undefined`, not `null`,\n * so we must use `.nullable().optional()` to tolerate both.\n */\nconst ClaudeCodeOAuthEntrySchema = z\n .object({\n serverName: z.string().nullable().optional(),\n serverUrl: z.string().nullable().optional(),\n clientId: z.string().nullable().optional(),\n clientSecret: z.string().nullable().optional(),\n accessToken: z.string().nullable().optional(),\n refreshToken: z.string().nullable().optional(),\n expiresAt: z.number().nullable().optional(),\n discoveryState: DiscoveryStateSchema.nullable().optional(),\n })\n .passthrough();\n\nconst CredentialsFileSchema = z\n .object({\n mcpOAuth: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\n\nconst CREDENTIALS_PATH = join(homedir(), '.claude', '.credentials.json');\nconst KEYCHAIN_SERVICE = 'Claude Code-credentials';\n\nconst PluginMcpJsonSchema = z.record(\n z.string(),\n z\n .object({\n url: z.string().optional(),\n oauth: z.object({ clientId: z.string(), callbackPort: z.number().optional() }).optional(),\n })\n .passthrough()\n);\n\n/**\n * Read OAuth clientIds from CC plugin `.mcp.json` files.\n * Returns a Map from server URL to clientId.\n */\nasync function extractClientIdsFromPlugin(\n installPath: string,\n result: Map<string, string>\n): Promise<void> {\n const raw = await readFile(join(installPath, '.mcp.json'), 'utf-8');\n const json: unknown = JSON.parse(raw);\n const outer = z.record(z.string(), z.unknown()).safeParse(json);\n if (!outer.success) return;\n const servers = outer.data.mcpServers ?? outer.data;\n const validated = PluginMcpJsonSchema.safeParse(servers);\n if (!validated.success) return;\n\n for (const config of Object.values(validated.data)) {\n if (config.url && config.oauth?.clientId) {\n result.set(config.url, config.oauth.clientId);\n }\n }\n}\n\nexport async function readPluginClientIds(): Promise<Map<string, string>> {\n const result = new Map<string, string>();\n try {\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const InstalledPluginsSchema = z.object({\n plugins: z\n .record(z.string(), z.array(z.object({ installPath: z.string().optional() })))\n .optional(),\n });\n const parsed = InstalledPluginsSchema.safeParse(\n JSON.parse(await readFile(installedPath, 'utf-8'))\n );\n if (!parsed.success || !parsed.data.plugins) return result;\n\n for (const installs of Object.values(parsed.data.plugins)) {\n const installPath = installs?.[0]?.installPath;\n if (!installPath) continue;\n await extractClientIdsFromPlugin(installPath, result).catch(() => {});\n }\n } catch {}\n return result;\n}\n\n/**\n * Attempt to read Claude Code's credential blob from macOS Keychain.\n * Returns the parsed JSON object or null if unavailable.\n */\nasync function readKeychainCredentials(): Promise<Record<string, unknown> | null> {\n if (process.platform !== 'darwin') return null;\n\n try {\n const { stdout } = await execFile(\n 'security',\n ['find-generic-password', '-s', KEYCHAIN_SERVICE, '-w'],\n { timeout: 5_000 }\n );\n const result = z.record(z.string(), z.unknown()).safeParse(JSON.parse(stdout.trim()));\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Search a parsed credentials object for the best MCP OAuth entry\n * matching the given `serverUrl`. Prefers entries with an access token;\n * separately gathers clientId from any matching entry.\n *\n * CC stores entries under both bare (`slack`) and plugin-prefixed\n * (`plugin:slack:slack`) keys. The prefixed entries typically have\n * tokens while the bare entries may only have clientId. We merge\n * across all matching entries to get the most complete picture.\n */\n/** Scan a flat mcpOAuth record for entries matching serverUrl. */\nfunction scanEntries(\n mcpOAuth: Record<string, unknown>,\n serverUrl: string\n): {\n bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null;\n clientId?: string;\n clientSecret?: string;\n discoveryState?: OAuthDiscoveryState;\n} {\n let bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null = null;\n let clientId: string | undefined;\n let clientSecret: string | undefined;\n let discoveryState: OAuthDiscoveryState | undefined;\n\n for (const value of Object.values(mcpOAuth)) {\n const entryResult = ClaudeCodeOAuthEntrySchema.safeParse(value);\n if (!entryResult.success) continue;\n\n const entry = entryResult.data;\n if (entry.serverUrl !== serverUrl) continue;\n\n if (entry.clientId && !clientId) {\n clientId = entry.clientId;\n clientSecret = entry.clientSecret || undefined;\n }\n if (entry.discoveryState && !discoveryState) {\n discoveryState = entry.discoveryState;\n }\n if (entry.accessToken && !bestWithToken) {\n bestWithToken = entry;\n }\n }\n\n return { bestWithToken, clientId, clientSecret, discoveryState };\n}\n\nfunction findMatchingEntry(\n credentialsData: Record<string, unknown>,\n serverName: string,\n serverUrl: string\n): ClaudeCodeOAuthEntry | null {\n const fileResult = CredentialsFileSchema.safeParse(credentialsData);\n if (!fileResult.success) return null;\n\n const mcpOAuth = fileResult.data.mcpOAuth;\n if (!mcpOAuth) return null;\n\n const { bestWithToken, clientId, clientSecret, discoveryState } = scanEntries(\n mcpOAuth,\n serverUrl\n );\n if (!bestWithToken && !clientId) return null;\n\n return {\n serverName: bestWithToken?.serverName ?? serverName,\n serverUrl,\n clientId: bestWithToken?.clientId || clientId,\n clientSecret: bestWithToken?.clientSecret || clientSecret || undefined,\n accessToken: bestWithToken?.accessToken || undefined,\n refreshToken: bestWithToken?.refreshToken || undefined,\n expiresAt: bestWithToken?.expiresAt ?? undefined,\n discoveryState: bestWithToken?.discoveryState ?? discoveryState ?? undefined,\n };\n}\n\n/**\n * Search Claude Code's credential store for an MCP OAuth entry\n * matching the given `serverUrl`.\n *\n * Tries macOS Keychain first (Claude Code v2.1.81+), then falls back\n * to the legacy file at `~/.claude/.credentials.json`.\n *\n * Keys in the store use hashes we cannot reproduce, so we match\n * by `serverUrl` instead. Returns the first entry whose `serverUrl`\n * matches and that has a non-empty `clientId`.\n */\nexport async function readClaudeCodeCredentials(\n serverName: string,\n serverUrl: string\n): Promise<ClaudeCodeOAuthEntry | null> {\n if (!serverUrl) return null;\n\n try {\n const keychainData = await readKeychainCredentials();\n if (keychainData) {\n const entry = findMatchingEntry(keychainData, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in macOS Keychain');\n return entry;\n }\n }\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from Keychain');\n }\n\n try {\n const raw = await readFile(CREDENTIALS_PATH, 'utf-8');\n const fileResult = CredentialsFileSchema.safeParse(JSON.parse(raw));\n if (fileResult.success) {\n const entry = findMatchingEntry(fileResult.data, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in legacy file');\n return entry;\n }\n }\n\n return null;\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from file');\n return null;\n }\n}\n","import {\n OAuthMetadataSchema,\n OAuthProtectedResourceMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n} from '@modelcontextprotocol/sdk/shared/auth.js';\nimport { z } from 'zod';\n\nexport const AuthorizationServerMetadataSchema = z.union([\n OAuthMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n]);\n\nexport const DiscoveryStateSchema = z\n .object({\n authorizationServerUrl: z.string(),\n authorizationServerMetadata: AuthorizationServerMetadataSchema.optional(),\n resourceMetadata: OAuthProtectedResourceMetadataSchema.optional(),\n resourceMetadataUrl: z.string().optional(),\n })\n .passthrough();\n","import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';\nimport type { MCPServerInfo } from '@shipyard/session';\n\nimport type { MCPTokenStore } from './token-store.js';\n\nexport const ENV_VAR_PATTERN = /\\$\\{([^}]+)\\}/g;\nexport const VAULT_REF_PATTERN = /^\\$\\{vault:([^}]+)\\}$/;\n\nexport function interpolateEnvVars(headers: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n result[key] = value.replace(\n ENV_VAR_PATTERN,\n (_, varName: string) => process.env[varName] ?? ''\n );\n }\n return result;\n}\n\n/**\n * Pass through user-configured static headers only. OAuth tokens for HTTP\n * servers are managed by the SDK subprocess via CC's Keychain — injecting\n * Shipyard's own tokens here would conflict with the SDK's OAuth transport.\n * Token store injection is only used for stdio env vars (resolveStdioEnv).\n */\nexport function resolveHeaders(server: MCPServerInfo): Record<string, string> | undefined {\n if (!server.headers) return undefined;\n return interpolateEnvVars(server.headers);\n}\n\nexport function resolveStdioEnv(\n env: Record<string, string> | undefined,\n store?: MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Record<string, string> | undefined {\n if (!env) return undefined;\n if (!store) return env;\n\n const result: Record<string, string> = {};\n let changed = false;\n\n for (const [key, value] of Object.entries(env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (match) {\n const vaultKey = match[1] ?? '';\n const token = store.getTokenSync(vaultKey);\n if (token && !store.isExpired(token)) {\n result[key] = token.accessToken;\n changed = true;\n log?.({\n event: 'vault_ref_resolved',\n envKey: key,\n vaultKey,\n tokenType: token.tokenType,\n hasExpiry: token.expiresAt !== undefined,\n });\n continue;\n }\n log?.({\n event: 'vault_ref_unresolved',\n envKey: key,\n vaultKey,\n tokenFound: token !== null,\n tokenExpired: token ? store.isExpired(token) : false,\n });\n }\n result[key] = value;\n }\n\n return changed ? result : env;\n}\n\nexport function resolveEnabledMcpServers(\n overrides: Array<{ name: string; enabled: boolean }> | undefined,\n available: MCPServerInfo[] | undefined,\n mcpTokenStore?: MCPTokenStore\n): Record<string, McpServerConfig> | undefined {\n if (!overrides || !available) return undefined;\n const enabledNames = new Set(overrides.filter((o) => o.enabled).map((o) => o.name));\n if (enabledNames.size === 0) return {};\n const result: Record<string, McpServerConfig> = {};\n for (const server of available) {\n if (!enabledNames.has(server.name)) continue;\n if (server.type === 'http' && server.url) {\n result[server.name] = {\n type: 'http',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.type === 'sse' && server.url) {\n result[server.name] = {\n type: 'sse',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.command) {\n result[server.name] = {\n command: server.command,\n args: server.args ?? undefined,\n env: resolveStdioEnv(server.env, mcpTokenStore),\n };\n }\n }\n return result;\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { MCPServerInfo } from '@shipyard/session';\n\nimport { logger } from '../logger.js';\n\nconst execFile = promisify(execFileCb);\n\n/**\n * Read the Anthropic OAuth token from macOS Keychain (\"Claude Code\" service).\n * Returns null if the platform is not macOS, the entry does not exist,\n * or the stored value is an API key (starts with `sk-`) rather than an OAuth token.\n */\nexport async function readAnthropicOAuthToken(): Promise<string | null> {\n if (process.platform !== 'darwin') return null;\n\n try {\n const { stdout } = await execFile(\n 'security',\n ['find-generic-password', '-s', 'Claude Code', '-w'],\n { timeout: 5_000 }\n );\n const token = stdout.trim();\n if (!token || token.startsWith('sk-')) return null;\n return token;\n } catch {\n return null;\n }\n}\n\n/**\n * Minimal shape we expect from the Anthropic MCP servers API response.\n * Hand-rolled instead of Zod to keep best-effort parsing lightweight.\n */\nfunction parseClaudeAiServers(data: unknown): Array<{ name: string; url: string }> {\n if (typeof data !== 'object' || data === null) return [];\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; manually validated below\n const obj = data as Record<string, unknown>;\n if (!Array.isArray(obj.data)) return [];\n const results: Array<{ name: string; url: string }> = [];\n for (const entry of obj.data) {\n if (typeof entry !== 'object' || entry === null) continue;\n // eslint-disable-next-line no-restricted-syntax -- narrowing unknown entry fields\n const rec = entry as Record<string, unknown>;\n if (typeof rec.name === 'string' && typeof rec.url === 'string') {\n results.push({ name: rec.name, url: rec.url });\n }\n }\n return results;\n}\n\n/**\n * Fetch MCP server integrations from the user's Claude.ai account.\n * Best-effort: returns [] on any failure.\n */\nexport async function fetchClaudeAiIntegrations(\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Promise<MCPServerInfo[]> {\n try {\n const token = await readAnthropicOAuthToken();\n if (!token) return [];\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10_000);\n\n try {\n const response = await fetch('https://api.anthropic.com/v1/mcp_servers?limit=1000', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const debugLog = log ?? ((entry: { event: string }) => logger.debug(entry, entry.event));\n debugLog({\n event: 'claudeai_integrations_fetch_failed',\n status: response.status,\n });\n return [];\n }\n\n const body: unknown = await response.json();\n const servers = parseClaudeAiServers(body);\n\n return servers.map((s) => ({\n name: s.name,\n type: 'http' as const,\n url: s.url,\n enabled: true,\n source: 'claudeai' as const,\n authStatus: 'unknown' as const,\n }));\n } finally {\n clearTimeout(timeout);\n }\n } catch (err: unknown) {\n const debugLog = log ?? ((entry: { event: string }) => logger.debug(entry, entry.event));\n debugLog({\n event: 'claudeai_integrations_fetch_error',\n error: err instanceof Error ? err.message : String(err),\n });\n return [];\n }\n}\n"],"mappings":"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","names":["readFile","homedir","join","execFileCb","promisify","execFile","promisify","execFileCb","readFile","join","homedir"]}