@ai-ecoverse/slicc-agent 3.52.1 → 4.1.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 (94) hide show
  1. package/README.md +17 -13
  2. package/dist/client.js +3 -1
  3. package/dist/client.js.map +2 -2
  4. package/dist/control.js +4 -1
  5. package/dist/control.js.map +2 -2
  6. package/dist/freezer/index.js +27 -68
  7. package/dist/freezer/index.js.map +2 -2
  8. package/dist/host.js +2 -0
  9. package/dist/host.js.map +2 -2
  10. package/dist/index.js +0 -3
  11. package/dist/index.js.map +2 -2
  12. package/dist/kernel/agent-users.js +3 -0
  13. package/dist/kernel/agent-users.js.map +2 -2
  14. package/dist/main.js +111 -26
  15. package/dist/main.js.map +2 -2
  16. package/dist/mcp/accounts.js +18 -14
  17. package/dist/mcp/accounts.js.map +2 -2
  18. package/dist/mcp/connection.js +0 -2
  19. package/dist/mcp/connection.js.map +2 -2
  20. package/dist/mcp/credentials.js +6 -15
  21. package/dist/mcp/credentials.js.map +2 -2
  22. package/dist/mcp/index.js +0 -1
  23. package/dist/mcp/index.js.map +2 -2
  24. package/dist/mcp/oauth.js +15 -20
  25. package/dist/mcp/oauth.js.map +2 -2
  26. package/dist/mcp/runtime.js +36 -14
  27. package/dist/mcp/runtime.js.map +2 -2
  28. package/dist/mcp/tools.js +4 -6
  29. package/dist/mcp/tools.js.map +2 -2
  30. package/dist/models/index.js +7 -4
  31. package/dist/models/index.js.map +2 -2
  32. package/dist/monitor/index.js +5 -10
  33. package/dist/monitor/index.js.map +2 -2
  34. package/dist/monitor/redact.js +4 -5
  35. package/dist/monitor/redact.js.map +2 -2
  36. package/dist/secrets/credentials.js +32 -0
  37. package/dist/secrets/credentials.js.map +7 -0
  38. package/dist/secrets/endpoints.js +111 -0
  39. package/dist/secrets/endpoints.js.map +7 -0
  40. package/dist/secrets/extension.js +6 -3
  41. package/dist/secrets/extension.js.map +2 -2
  42. package/dist/secrets/fetch.js +160 -0
  43. package/dist/secrets/fetch.js.map +7 -0
  44. package/dist/secrets/records.js +35 -0
  45. package/dist/secrets/records.js.map +7 -0
  46. package/dist/secrets/store.js +50 -0
  47. package/dist/secrets/store.js.map +7 -0
  48. package/dist/secrets/vault.js +58 -13
  49. package/dist/secrets/vault.js.map +2 -2
  50. package/dist/services.js.map +2 -2
  51. package/dist/settings.js +85 -23
  52. package/dist/settings.js.map +2 -2
  53. package/dist/skills/index.js +20 -3
  54. package/dist/skills/index.js.map +3 -3
  55. package/dist/spectrum/agent-port.js +29 -1
  56. package/dist/spectrum/agent-port.js.map +2 -2
  57. package/dist/spectrum/settings-port.js +24 -6
  58. package/dist/spectrum/settings-port.js.map +2 -2
  59. package/dist/titles/extension.js +110 -0
  60. package/dist/titles/extension.js.map +7 -0
  61. package/dist/{freezer/title.js → titles/generate.js} +21 -2
  62. package/dist/titles/generate.js.map +7 -0
  63. package/dist/types/client.d.ts +2 -1
  64. package/dist/types/control.d.ts +1 -1
  65. package/dist/types/freezer/index.d.ts +2 -5
  66. package/dist/types/host.d.ts +1 -1
  67. package/dist/types/index.d.ts +0 -1
  68. package/dist/types/main.d.ts +7 -2
  69. package/dist/types/mcp/accounts.d.ts +10 -4
  70. package/dist/types/mcp/connection.d.ts +0 -2
  71. package/dist/types/mcp/credentials.d.ts +6 -4
  72. package/dist/types/mcp/oauth.d.ts +3 -2
  73. package/dist/types/mcp/runtime.d.ts +18 -4
  74. package/dist/types/models/index.d.ts +2 -0
  75. package/dist/types/monitor/index.d.ts +0 -1
  76. package/dist/types/monitor/redact.d.ts +2 -2
  77. package/dist/types/secrets/credentials.d.ts +5 -0
  78. package/dist/types/secrets/endpoints.d.ts +27 -0
  79. package/dist/types/secrets/fetch.d.ts +13 -0
  80. package/dist/types/secrets/records.d.ts +13 -0
  81. package/dist/types/secrets/store.d.ts +7 -0
  82. package/dist/types/secrets/vault.d.ts +31 -1
  83. package/dist/types/services.d.ts +13 -1
  84. package/dist/types/settings.d.ts +17 -2
  85. package/dist/types/skills/index.d.ts +4 -1
  86. package/dist/types/spectrum/agent-port.d.ts +1 -1
  87. package/dist/types/spectrum/settings-port.d.ts +6 -4
  88. package/dist/types/titles/extension.d.ts +19 -0
  89. package/dist/types/{freezer/title.d.ts → titles/generate.d.ts} +4 -0
  90. package/package.json +6 -4
  91. package/dist/credentials.js +0 -100
  92. package/dist/credentials.js.map +0 -7
  93. package/dist/freezer/title.js.map +0 -7
  94. package/dist/types/credentials.d.ts +0 -12
@@ -13,33 +13,24 @@ function secretNames(config) {
13
13
  for (const name of placeholders(value)) names.add(name);
14
14
  return [...names];
15
15
  }
16
- async function resolveHeaders(server, config, credentials) {
16
+ function resolveHeaders(server, config, records) {
17
17
  const names = secretNames(config);
18
- if (!names.length) return { ok: true, headers: { ...config.headers }, secrets: [] };
19
- const stored = await credentials.read(await credentialKey(server, config.url));
20
- const env = stored?.type === "api_key" ? stored.env ?? {} : {};
21
- const key = stored?.type === "api_key" ? stored.key : void 0;
22
- const value = (name) => env[name] ?? (names.length === 1 ? key : void 0);
23
- const missing = names.filter((name) => !value(name));
18
+ if (!names.length) return { ok: true, headers: { ...config.headers } };
19
+ const placeholder = (name) => (records.mcp(server, name) ?? (names.length === 1 ? records.mcp(server) : void 0))?.placeholder;
20
+ const missing = names.filter((name) => !placeholder(name));
24
21
  if (missing.length) return { ok: false, missing };
25
22
  const headers = {};
26
23
  for (const [header, text] of Object.entries(config.headers ?? {}))
27
24
  headers[header] = text.replace(
28
25
  /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
29
- (_, name) => String(value(name))
26
+ (_, name) => String(placeholder(name))
30
27
  );
31
- return { ok: true, headers, secrets: names.map((name) => String(value(name))) };
32
- }
33
- function scrub(text, secrets) {
34
- let out = text;
35
- for (const secret of secrets) if (secret.length >= 4) out = out.split(secret).join("[redacted]");
36
- return out;
28
+ return { ok: true, headers };
37
29
  }
38
30
  export {
39
31
  MCP_CREDENTIAL_PREFIX,
40
32
  credentialKey,
41
33
  resolveHeaders,
42
- scrub,
43
34
  secretNames
44
35
  };
45
36
  //# sourceMappingURL=credentials.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/credentials.ts"],
4
- "sourcesContent": ["import type { CredentialStore } from '@earendil-works/pi-ai';\nimport { type McpServerConfig, placeholders } from './config.ts';\n\nexport const MCP_CREDENTIAL_PREFIX = 'mcp:';\n\nasync function digest(text: string): Promise<string> {\n const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(bytes).slice(0, 12)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\nexport async function credentialKey(server: string, url: string): Promise<string> {\n return `${MCP_CREDENTIAL_PREFIX}${server}|${await digest(new URL(url).href)}`;\n}\n\nexport function secretNames(config: McpServerConfig): string[] {\n const names = new Set<string>();\n for (const value of Object.values(config.headers ?? {}))\n for (const name of placeholders(value)) names.add(name);\n return [...names];\n}\n\nexport type Resolved =\n | { ok: true; headers: Record<string, string>; secrets: string[] }\n | { ok: false; missing: string[] };\n\nexport async function resolveHeaders(\n server: string,\n config: McpServerConfig,\n credentials: CredentialStore\n): Promise<Resolved> {\n const names = secretNames(config);\n if (!names.length) return { ok: true, headers: { ...config.headers }, secrets: [] };\n const stored = await credentials.read(await credentialKey(server, config.url));\n const env = stored?.type === 'api_key' ? (stored.env ?? {}) : {};\n const key = stored?.type === 'api_key' ? stored.key : undefined;\n const value = (name: string) => env[name] ?? (names.length === 1 ? key : undefined);\n const missing = names.filter((name) => !value(name));\n if (missing.length) return { ok: false, missing };\n const headers: Record<string, string> = {};\n for (const [header, text] of Object.entries(config.headers ?? {}))\n headers[header] = text.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, name: string) =>\n String(value(name))\n );\n return { ok: true, headers, secrets: names.map((name) => String(value(name))) };\n}\n\nexport function scrub(text: string, secrets: readonly string[]): string {\n let out = text;\n for (const secret of secrets) if (secret.length >= 4) out = out.split(secret).join('[redacted]');\n return out;\n}\n"],
5
- "mappings": "AACA,SAA+B,oBAAoB;AAE5C,MAAM,wBAAwB;AAErC,eAAe,OAAO,MAA+B;AACnD,QAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAClF,SAAO,CAAC,GAAG,IAAI,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,EAC1C,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAChD,KAAK,EAAE;AACZ;AAEA,eAAsB,cAAc,QAAgB,KAA8B;AAChF,SAAO,GAAG,qBAAqB,GAAG,MAAM,IAAI,MAAM,OAAO,IAAI,IAAI,GAAG,EAAE,IAAI,CAAC;AAC7E;AAEO,SAAS,YAAY,QAAmC;AAC7D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC;AACpD,eAAW,QAAQ,aAAa,KAAK,EAAG,OAAM,IAAI,IAAI;AACxD,SAAO,CAAC,GAAG,KAAK;AAClB;AAMA,eAAsB,eACpB,QACA,QACA,aACmB;AACnB,QAAM,QAAQ,YAAY,MAAM;AAChC,MAAI,CAAC,MAAM,OAAQ,QAAO,EAAE,IAAI,MAAM,SAAS,EAAE,GAAG,OAAO,QAAQ,GAAG,SAAS,CAAC,EAAE;AAClF,QAAM,SAAS,MAAM,YAAY,KAAK,MAAM,cAAc,QAAQ,OAAO,GAAG,CAAC;AAC7E,QAAM,MAAM,QAAQ,SAAS,YAAa,OAAO,OAAO,CAAC,IAAK,CAAC;AAC/D,QAAM,MAAM,QAAQ,SAAS,YAAY,OAAO,MAAM;AACtD,QAAM,QAAQ,CAAC,SAAiB,IAAI,IAAI,MAAM,MAAM,WAAW,IAAI,MAAM;AACzE,QAAM,UAAU,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC;AACnD,MAAI,QAAQ,OAAQ,QAAO,EAAE,IAAI,OAAO,QAAQ;AAChD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC;AAC9D,YAAQ,MAAM,IAAI,KAAK;AAAA,MAAQ;AAAA,MAAmC,CAAC,GAAG,SACpE,OAAO,MAAM,IAAI,CAAC;AAAA,IACpB;AACF,SAAO,EAAE,IAAI,MAAM,SAAS,SAAS,MAAM,IAAI,CAAC,SAAS,OAAO,MAAM,IAAI,CAAC,CAAC,EAAE;AAChF;AAEO,SAAS,MAAM,MAAc,SAAoC;AACtE,MAAI,MAAM;AACV,aAAW,UAAU,QAAS,KAAI,OAAO,UAAU,EAAG,OAAM,IAAI,MAAM,MAAM,EAAE,KAAK,YAAY;AAC/F,SAAO;AACT;",
4
+ "sourcesContent": ["import { type McpServerConfig, placeholders } from './config.ts';\n\nexport const MCP_CREDENTIAL_PREFIX = 'mcp:';\n\nexport interface McpRecords {\n mcp(server: string, name?: string): { placeholder: string } | undefined;\n}\n\nasync function digest(text: string): Promise<string> {\n const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));\n return [...new Uint8Array(bytes).slice(0, 12)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\nexport async function credentialKey(server: string, url: string): Promise<string> {\n return `${MCP_CREDENTIAL_PREFIX}${server}|${await digest(new URL(url).href)}`;\n}\n\nexport function secretNames(config: McpServerConfig): string[] {\n const names = new Set<string>();\n for (const value of Object.values(config.headers ?? {}))\n for (const name of placeholders(value)) names.add(name);\n return [...names];\n}\n\nexport type Resolved =\n | { ok: true; headers: Record<string, string> }\n | { ok: false; missing: string[] };\n\nexport function resolveHeaders(\n server: string,\n config: McpServerConfig,\n records: McpRecords\n): Resolved {\n const names = secretNames(config);\n if (!names.length) return { ok: true, headers: { ...config.headers } };\n const placeholder = (name: string) =>\n (records.mcp(server, name) ?? (names.length === 1 ? records.mcp(server) : undefined))\n ?.placeholder;\n const missing = names.filter((name) => !placeholder(name));\n if (missing.length) return { ok: false, missing };\n const headers: Record<string, string> = {};\n for (const [header, text] of Object.entries(config.headers ?? {}))\n headers[header] = text.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, name: string) =>\n String(placeholder(name))\n );\n return { ok: true, headers };\n}\n"],
5
+ "mappings": "AAAA,SAA+B,oBAAoB;AAE5C,MAAM,wBAAwB;AAMrC,eAAe,OAAO,MAA+B;AACnD,QAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAClF,SAAO,CAAC,GAAG,IAAI,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,EAC1C,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAChD,KAAK,EAAE;AACZ;AAEA,eAAsB,cAAc,QAAgB,KAA8B;AAChF,SAAO,GAAG,qBAAqB,GAAG,MAAM,IAAI,MAAM,OAAO,IAAI,IAAI,GAAG,EAAE,IAAI,CAAC;AAC7E;AAEO,SAAS,YAAY,QAAmC;AAC7D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,SAAS,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC;AACpD,eAAW,QAAQ,aAAa,KAAK,EAAG,OAAM,IAAI,IAAI;AACxD,SAAO,CAAC,GAAG,KAAK;AAClB;AAMO,SAAS,eACd,QACA,QACA,SACU;AACV,QAAM,QAAQ,YAAY,MAAM;AAChC,MAAI,CAAC,MAAM,OAAQ,QAAO,EAAE,IAAI,MAAM,SAAS,EAAE,GAAG,OAAO,QAAQ,EAAE;AACrE,QAAM,cAAc,CAAC,UAClB,QAAQ,IAAI,QAAQ,IAAI,MAAM,MAAM,WAAW,IAAI,QAAQ,IAAI,MAAM,IAAI,UACtE;AACN,QAAM,UAAU,MAAM,OAAO,CAAC,SAAS,CAAC,YAAY,IAAI,CAAC;AACzD,MAAI,QAAQ,OAAQ,QAAO,EAAE,IAAI,OAAO,QAAQ;AAChD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC;AAC9D,YAAQ,MAAM,IAAI,KAAK;AAAA,MAAQ;AAAA,MAAmC,CAAC,GAAG,SACpE,OAAO,YAAY,IAAI,CAAC;AAAA,IAC1B;AACF,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;",
6
6
  "names": []
7
7
  }
package/dist/mcp/index.js CHANGED
@@ -13,7 +13,6 @@ const idle = {
13
13
  disconnect: async () => void 0,
14
14
  onChange: () => () => void 0,
15
15
  status: () => [],
16
- secrets: () => [],
17
16
  reload: async () => void 0,
18
17
  close: async () => void 0
19
18
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/index.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type {\n Agent,\n Registry,\n ToolExecutionApi,\n ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { CodemodeExtra } from './allow.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport { parseMcpConfig } from './config.ts';\nimport { configPath, shown } from './paths.ts';\nimport type { McpAttach, McpRuntime } from './runtime.ts';\n\nexport { allows, type CodemodeExtra, McpAllowDoc, mcpEntries, serverAllowed } from './allow.ts';\nexport type { McpAccounts, McpAttach, McpRuntime, ServerStatus } from './runtime.ts';\n\nexport type McpLoader = () => Promise<typeof import('./runtime.ts')>;\n\nexport const loadRuntime: McpLoader = () => import('./runtime.ts');\n\nexport interface McpSetup {\n catalog(): McpCatalog | undefined;\n direct(names: readonly string[]): ToolRegistration[];\n codemode(api: ToolExecutionApi, agent: Agent, context: Context): Promise<CodemodeExtra>;\n attach(options: McpAttach, context: Context): Promise<McpRuntime>;\n}\n\nconst idle: McpRuntime = {\n accounts: async () => [],\n signIn: async (id) => {\n throw new Error(`${id} is not an MCP server that signs in`);\n },\n connect: async (id) => {\n throw new Error(`${id} is not an MCP server that takes a token`);\n },\n disconnect: async () => undefined,\n onChange: () => () => undefined,\n status: () => [],\n secrets: () => [],\n reload: async () => undefined,\n close: async () => undefined,\n};\n\nasync function unavailable(options: McpAttach, error: unknown, context: Context) {\n const file = configPath(options.home, options.agentDir);\n const named = shown(file, options.home);\n const read = await options.env.readTextFile(file, context);\n if (!parseMcpConfig(read.ok ? read.value : undefined).servers.length) return;\n await options.licks\n .deliver(\n {\n channel: 'fswatch',\n source: `mcp:${named}`,\n title: \"MCP servers can't start in this SLICC\",\n text: error instanceof Error ? error.message : String(error),\n body: `The MCP client didn't load, so the servers in ${named} stay off until SLICC is updated.`,\n target: 'cone',\n severity: 'warn',\n eventId: 'mcp\\nunavailable',\n },\n context\n )\n .catch(() => undefined);\n}\n\nexport function setupMcp(registry: Registry, load: McpLoader = loadRuntime): McpSetup {\n let catalog: McpCatalog | undefined;\n return {\n catalog: () => catalog,\n direct: (names) => catalog?.direct(names) ?? [],\n codemode: async (api, agent, context) =>\n catalog ? catalog.codemode(api, agent, context) : { tools: [], globals: [] },\n async attach(options, context) {\n let started: Awaited<ReturnType<Awaited<ReturnType<McpLoader>>['startMcp']>>;\n try {\n started = await (await load()).startMcp(registry, options, context);\n } catch (error) {\n await unavailable(options, error, context);\n return idle;\n }\n catalog = started.catalog;\n return started.runtime;\n },\n };\n}\n"],
5
- "mappings": "AASA,SAAS,sBAAsB;AAC/B,SAAS,YAAY,aAAa;AAGlC,SAAS,QAA4B,aAAa,YAAY,qBAAqB;AAK5E,MAAM,cAAyB,MAAM,OAAO,cAAc;AASjE,MAAM,OAAmB;AAAA,EACvB,UAAU,YAAY,CAAC;AAAA,EACvB,QAAQ,OAAO,OAAO;AACpB,UAAM,IAAI,MAAM,GAAG,EAAE,qCAAqC;AAAA,EAC5D;AAAA,EACA,SAAS,OAAO,OAAO;AACrB,UAAM,IAAI,MAAM,GAAG,EAAE,0CAA0C;AAAA,EACjE;AAAA,EACA,YAAY,YAAY;AAAA,EACxB,UAAU,MAAM,MAAM;AAAA,EACtB,QAAQ,MAAM,CAAC;AAAA,EACf,SAAS,MAAM,CAAC;AAAA,EAChB,QAAQ,YAAY;AAAA,EACpB,OAAO,YAAY;AACrB;AAEA,eAAe,YAAY,SAAoB,OAAgB,SAAkB;AAC/E,QAAM,OAAO,WAAW,QAAQ,MAAM,QAAQ,QAAQ;AACtD,QAAM,QAAQ,MAAM,MAAM,QAAQ,IAAI;AACtC,QAAM,OAAO,MAAM,QAAQ,IAAI,aAAa,MAAM,OAAO;AACzD,MAAI,CAAC,eAAe,KAAK,KAAK,KAAK,QAAQ,MAAS,EAAE,QAAQ,OAAQ;AACtE,QAAM,QAAQ,MACX;AAAA,IACC;AAAA,MACE,SAAS;AAAA,MACT,QAAQ,OAAO,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC3D,MAAM,iDAAiD,KAAK;AAAA,MAC5D,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,IACA;AAAA,EACF,EACC,MAAM,MAAM,MAAS;AAC1B;AAEO,SAAS,SAAS,UAAoB,OAAkB,aAAuB;AACpF,MAAI;AACJ,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,QAAQ,CAAC,UAAU,SAAS,OAAO,KAAK,KAAK,CAAC;AAAA,IAC9C,UAAU,OAAO,KAAK,OAAO,YAC3B,UAAU,QAAQ,SAAS,KAAK,OAAO,OAAO,IAAI,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAC7E,MAAM,OAAO,SAAS,SAAS;AAC7B,UAAI;AACJ,UAAI;AACF,kBAAU,OAAO,MAAM,KAAK,GAAG,SAAS,UAAU,SAAS,OAAO;AAAA,MACpE,SAAS,OAAO;AACd,cAAM,YAAY,SAAS,OAAO,OAAO;AACzC,eAAO;AAAA,MACT;AACA,gBAAU,QAAQ;AAClB,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type {\n Agent,\n Registry,\n ToolExecutionApi,\n ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport type { CodemodeExtra } from './allow.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport { parseMcpConfig } from './config.ts';\nimport { configPath, shown } from './paths.ts';\nimport type { McpAttach, McpRuntime } from './runtime.ts';\n\nexport { allows, type CodemodeExtra, McpAllowDoc, mcpEntries, serverAllowed } from './allow.ts';\nexport type { McpAccounts, McpAttach, McpRuntime, ServerStatus } from './runtime.ts';\n\nexport type McpLoader = () => Promise<typeof import('./runtime.ts')>;\n\nexport const loadRuntime: McpLoader = () => import('./runtime.ts');\n\nexport interface McpSetup {\n catalog(): McpCatalog | undefined;\n direct(names: readonly string[]): ToolRegistration[];\n codemode(api: ToolExecutionApi, agent: Agent, context: Context): Promise<CodemodeExtra>;\n attach(options: McpAttach, context: Context): Promise<McpRuntime>;\n}\n\nconst idle: McpRuntime = {\n accounts: async () => [],\n signIn: async (id) => {\n throw new Error(`${id} is not an MCP server that signs in`);\n },\n connect: async (id) => {\n throw new Error(`${id} is not an MCP server that takes a token`);\n },\n disconnect: async () => undefined,\n onChange: () => () => undefined,\n status: () => [],\n reload: async () => undefined,\n close: async () => undefined,\n};\n\nasync function unavailable(options: McpAttach, error: unknown, context: Context) {\n const file = configPath(options.home, options.agentDir);\n const named = shown(file, options.home);\n const read = await options.env.readTextFile(file, context);\n if (!parseMcpConfig(read.ok ? read.value : undefined).servers.length) return;\n await options.licks\n .deliver(\n {\n channel: 'fswatch',\n source: `mcp:${named}`,\n title: \"MCP servers can't start in this SLICC\",\n text: error instanceof Error ? error.message : String(error),\n body: `The MCP client didn't load, so the servers in ${named} stay off until SLICC is updated.`,\n target: 'cone',\n severity: 'warn',\n eventId: 'mcp\\nunavailable',\n },\n context\n )\n .catch(() => undefined);\n}\n\nexport function setupMcp(registry: Registry, load: McpLoader = loadRuntime): McpSetup {\n let catalog: McpCatalog | undefined;\n return {\n catalog: () => catalog,\n direct: (names) => catalog?.direct(names) ?? [],\n codemode: async (api, agent, context) =>\n catalog ? catalog.codemode(api, agent, context) : { tools: [], globals: [] },\n async attach(options, context) {\n let started: Awaited<ReturnType<Awaited<ReturnType<McpLoader>>['startMcp']>>;\n try {\n started = await (await load()).startMcp(registry, options, context);\n } catch (error) {\n await unavailable(options, error, context);\n return idle;\n }\n catalog = started.catalog;\n return started.runtime;\n },\n };\n}\n"],
5
+ "mappings": "AASA,SAAS,sBAAsB;AAC/B,SAAS,YAAY,aAAa;AAGlC,SAAS,QAA4B,aAAa,YAAY,qBAAqB;AAK5E,MAAM,cAAyB,MAAM,OAAO,cAAc;AASjE,MAAM,OAAmB;AAAA,EACvB,UAAU,YAAY,CAAC;AAAA,EACvB,QAAQ,OAAO,OAAO;AACpB,UAAM,IAAI,MAAM,GAAG,EAAE,qCAAqC;AAAA,EAC5D;AAAA,EACA,SAAS,OAAO,OAAO;AACrB,UAAM,IAAI,MAAM,GAAG,EAAE,0CAA0C;AAAA,EACjE;AAAA,EACA,YAAY,YAAY;AAAA,EACxB,UAAU,MAAM,MAAM;AAAA,EACtB,QAAQ,MAAM,CAAC;AAAA,EACf,QAAQ,YAAY;AAAA,EACpB,OAAO,YAAY;AACrB;AAEA,eAAe,YAAY,SAAoB,OAAgB,SAAkB;AAC/E,QAAM,OAAO,WAAW,QAAQ,MAAM,QAAQ,QAAQ;AACtD,QAAM,QAAQ,MAAM,MAAM,QAAQ,IAAI;AACtC,QAAM,OAAO,MAAM,QAAQ,IAAI,aAAa,MAAM,OAAO;AACzD,MAAI,CAAC,eAAe,KAAK,KAAK,KAAK,QAAQ,MAAS,EAAE,QAAQ,OAAQ;AACtE,QAAM,QAAQ,MACX;AAAA,IACC;AAAA,MACE,SAAS;AAAA,MACT,QAAQ,OAAO,KAAK;AAAA,MACpB,OAAO;AAAA,MACP,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC3D,MAAM,iDAAiD,KAAK;AAAA,MAC5D,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,IACA;AAAA,EACF,EACC,MAAM,MAAM,MAAS;AAC1B;AAEO,SAAS,SAAS,UAAoB,OAAkB,aAAuB;AACpF,MAAI;AACJ,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,QAAQ,CAAC,UAAU,SAAS,OAAO,KAAK,KAAK,CAAC;AAAA,IAC9C,UAAU,OAAO,KAAK,OAAO,YAC3B,UAAU,QAAQ,SAAS,KAAK,OAAO,OAAO,IAAI,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAC7E,MAAM,OAAO,SAAS,SAAS;AAC7B,UAAI;AACJ,UAAI;AACF,kBAAU,OAAO,MAAM,KAAK,GAAG,SAAS,UAAU,SAAS,OAAO;AAAA,MACpE,SAAS,OAAO;AACd,cAAM,YAAY,SAAS,OAAO,OAAO;AACzC,eAAO;AAAA,MACT;AACA,gBAAU,QAAQ;AAClB,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/dist/mcp/oauth.js CHANGED
@@ -32,7 +32,8 @@ function authProvider(provider, server) {
32
32
  await authorizeMcp(provider, {
33
33
  serverUrl: context.serverUrl,
34
34
  ...challenge.resourceMetadataUrl ? { resourceMetadataUrl: challenge.resourceMetadataUrl } : {},
35
- ...step ? { scope: stepUpScope(granted?.scope, challenge.scope), skipRefresh: true } : challenge.scope ? { scope: challenge.scope } : {},
35
+ skipRefresh: true,
36
+ ...step ? { scope: stepUpScope(granted?.scope, challenge.scope) } : challenge.scope ? { scope: challenge.scope } : {},
36
37
  ...configured ? { authorizationServerMetadataUrl: new URL(configured) } : {},
37
38
  fetch: context.fetch
38
39
  });
@@ -69,12 +70,12 @@ function randomState() {
69
70
  class StoredOAuth {
70
71
  redirectUrl;
71
72
  clientMetadata;
72
- #credentials;
73
+ #store;
73
74
  #server;
74
75
  #writes = Promise.resolve();
75
- constructor(server, credentials, redirectUrl) {
76
+ constructor(server, store, redirectUrl) {
76
77
  this.#server = server;
77
- this.#credentials = credentials;
78
+ this.#store = store;
78
79
  this.redirectUrl = redirectUrl;
79
80
  this.clientMetadata = {
80
81
  client_name: server.config.oauth?.clientName ?? CLIENT_NAME,
@@ -93,26 +94,17 @@ class StoredOAuth {
93
94
  }
94
95
  async load() {
95
96
  await this.#writes;
96
- const stored = await this.#credentials.read(await this.#key());
97
- const state = stored?.type === "oauth" ? stored.state : void 0;
97
+ const state = await this.#store.get(await this.#key());
98
98
  return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };
99
99
  }
100
100
  async update(change) {
101
101
  const write = this.#writes.then(async () => {
102
102
  const key = await this.#key();
103
- await this.#credentials.modify(key, async (current) => {
104
- const before = current?.type === "oauth" ? current.state : void 0;
105
- const next = change(
106
- before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl }
107
- );
108
- return {
109
- type: "oauth",
110
- access: next.tokens?.access_token ?? "",
111
- refresh: next.tokens?.refresh_token ?? "",
112
- expires: next.tokensExpireAt ?? 0,
113
- state: next
114
- };
115
- });
103
+ const before = await this.#store.get(key);
104
+ await this.#store.set(
105
+ key,
106
+ change(before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl })
107
+ );
116
108
  });
117
109
  this.#writes = write.catch(() => void 0);
118
110
  await write;
@@ -176,7 +168,7 @@ class StoredOAuth {
176
168
  }
177
169
  async forget() {
178
170
  await this.#writes;
179
- await this.#credentials.delete(await this.#key());
171
+ await this.#store.set(await this.#key(), void 0);
180
172
  }
181
173
  }
182
174
  async function beginSignIn(provider, server, options) {
@@ -203,6 +195,9 @@ async function beginSignIn(provider, server, options) {
203
195
  });
204
196
  await provider.saveClientInformation(client);
205
197
  }
198
+ await options.register(
199
+ new URL(metadata?.token_endpoint ?? new URL("/token", info.authorizationServerUrl)).href
200
+ );
206
201
  const { verifier, challenge } = await pkce();
207
202
  const state = randomState();
208
203
  await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/oauth.ts"],
4
- "sourcesContent": ["import type { CredentialStore } from '@earendil-works/pi-ai';\nimport type { AuthProvider, McpFetch, UnauthorizedContext } from '@earendil-works/pi-mcp';\nimport {\n authorizeMcp,\n discoverOAuthServerInfo,\n McpOAuthAuthorizationRequiredError,\n type McpOAuthState,\n type OAuthChallenge,\n parseWwwAuthenticate,\n registerClient,\n selectResource,\n stepUpScope,\n} from '@earendil-works/pi-mcp/oauth';\nimport type { McpSignIn } from '../services.ts';\nimport type { McpServerConfig } from './config.ts';\nimport { credentialKey } from './credentials.ts';\n\ntype Provider = Parameters<typeof authorizeMcp>[0];\ntype Tokens = Awaited<ReturnType<Provider['tokens']>>;\ntype Client = Awaited<ReturnType<Provider['clientInformation']>>;\ntype Discovery = NonNullable<McpOAuthState['discovery']>;\n\nexport const CLIENT_NAME = 'SLICC';\n\nexport function sentence(text: string): string {\n const trimmed = text.trim();\n const lowered = /^[A-Z][a-z]/.test(trimmed)\n ? `${trimmed[0]?.toLowerCase()}${trimmed.slice(1)}`\n : trimmed;\n return /[.!?]$/.test(lowered) ? lowered : `${lowered}.`;\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nexport function secureUrl(url: URL): boolean {\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n}\n\nexport function signInRequired(cause?: unknown): McpOAuthAuthorizationRequiredError {\n const error = new McpOAuthAuthorizationRequiredError();\n if (cause !== undefined) error.cause = cause;\n return error;\n}\n\nexport function authProvider(provider: StoredOAuth, server: OAuthServer): AuthProvider {\n const configured = server.config.oauth?.authServerMetadataUrl;\n let inFlight: Promise<void> | undefined;\n const authorize = async (\n context: UnauthorizedContext,\n step: boolean,\n challenge: OAuthChallenge\n ) => {\n const granted = step ? await provider.tokens() : undefined;\n try {\n await authorizeMcp(provider, {\n serverUrl: context.serverUrl,\n ...(challenge.resourceMetadataUrl\n ? { resourceMetadataUrl: challenge.resourceMetadataUrl }\n : {}),\n ...(step\n ? { scope: stepUpScope(granted?.scope, challenge.scope), skipRefresh: true }\n : challenge.scope\n ? { scope: challenge.scope }\n : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch: context.fetch,\n });\n } catch (error) {\n throw error instanceof McpOAuthAuthorizationRequiredError ? error : signInRequired(error);\n }\n };\n return {\n token: async () => (await provider.tokens())?.access_token,\n async onUnauthorized(context) {\n const challenge = parseWwwAuthenticate(context.response.headers.get('www-authenticate'));\n const step = challenge.error === 'insufficient_scope';\n if (!step && !inFlight && context.token !== undefined) {\n const current = (await provider.tokens())?.access_token;\n if (current !== undefined && current !== context.token) return;\n }\n inFlight ??= authorize(context, step, challenge).finally(() => {\n inFlight = undefined;\n });\n await inFlight;\n },\n };\n}\n\nconst bytesToBase64Url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nconst random = (length: number) => crypto.getRandomValues(new Uint8Array(length));\n\nexport async function pkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = bytesToBase64Url(random(32));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return { verifier, challenge: bytesToBase64Url(new Uint8Array(digest)) };\n}\n\nexport function randomState(): string {\n return [...random(32)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport interface OAuthServer {\n name: string;\n config: McpServerConfig;\n}\n\nexport class StoredOAuth implements Provider {\n readonly redirectUrl: string;\n readonly clientMetadata: Provider['clientMetadata'];\n readonly #credentials: CredentialStore;\n readonly #server: OAuthServer;\n #writes: Promise<unknown> = Promise.resolve();\n\n constructor(server: OAuthServer, credentials: CredentialStore, redirectUrl: string) {\n this.#server = server;\n this.#credentials = credentials;\n this.redirectUrl = redirectUrl;\n this.clientMetadata = {\n client_name: server.config.oauth?.clientName ?? CLIENT_NAME,\n redirect_uris: [redirectUrl],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n };\n }\n\n get serverUrl(): string {\n return String(new URL(this.#server.config.url));\n }\n\n async #key(): Promise<string> {\n return credentialKey(this.#server.name, this.#server.config.url);\n }\n\n async load(): Promise<McpOAuthState> {\n await this.#writes;\n const stored = await this.#credentials.read(await this.#key());\n const state =\n stored?.type === 'oauth' ? (stored.state as McpOAuthState | undefined) : undefined;\n return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };\n }\n\n async update(change: (state: McpOAuthState) => McpOAuthState): Promise<void> {\n const write = this.#writes.then(async () => {\n const key = await this.#key();\n await this.#credentials.modify(key, async (current) => {\n const before =\n current?.type === 'oauth' ? (current.state as McpOAuthState | undefined) : undefined;\n const next = change(\n before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl }\n );\n return {\n type: 'oauth',\n access: next.tokens?.access_token ?? '',\n refresh: next.tokens?.refresh_token ?? '',\n expires: next.tokensExpireAt ?? 0,\n state: next,\n };\n });\n });\n this.#writes = write.catch(() => undefined);\n await write;\n }\n\n async signedIn(): Promise<boolean> {\n return (await this.load()).tokens !== undefined;\n }\n\n state(): string {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async clientInformation(): Promise<Client> {\n const configured = this.#server.config.oauth?.clientId;\n return configured ? { client_id: configured } : (await this.load()).clientInformation;\n }\n\n async saveClientInformation(information: NonNullable<Client>): Promise<void> {\n if (this.#server.config.oauth?.clientId) return;\n await this.update((state) => ({ ...state, clientInformation: information }));\n }\n\n async tokens(): Promise<Tokens> {\n return (await this.load()).tokens;\n }\n\n async saveTokens(tokens: NonNullable<Tokens>): Promise<void> {\n const expires =\n tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;\n await this.update((state) => {\n const next: McpOAuthState = { ...state, tokens };\n if (expires === undefined) delete next.tokensExpireAt;\n else next.tokensExpireAt = expires;\n return next;\n });\n }\n\n redirectToAuthorization(): void {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.update((state) => ({ ...state, codeVerifier: verifier }));\n }\n\n async codeVerifier(): Promise<string> {\n const verifier = (await this.load()).codeVerifier;\n if (!verifier) throw new Error('No OAuth PKCE code verifier is stored');\n return verifier;\n }\n\n async invalidateCredentials(\n kind: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'\n ): Promise<void> {\n await this.update((state) => {\n const next = { ...state };\n if (kind === 'all' || kind === 'client') delete next.clientInformation;\n if (kind === 'all' || kind === 'tokens') {\n delete next.tokens;\n delete next.tokensExpireAt;\n }\n if (kind === 'all' || kind === 'verifier') delete next.codeVerifier;\n if (kind === 'all' || kind === 'discovery') delete next.discovery;\n if (kind === 'all') delete next.oauthState;\n return next;\n });\n }\n\n async saveDiscoveryState(discovery: Discovery): Promise<void> {\n await this.update((state) => ({ ...state, discovery }));\n }\n\n async discoveryState(): Promise<Discovery | undefined> {\n return (await this.load()).discovery;\n }\n\n async forget(): Promise<void> {\n await this.#writes;\n await this.#credentials.delete(await this.#key());\n }\n}\n\nexport type { McpSignIn } from '../services.ts';\n\nexport async function beginSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n options: { fetch: McpFetch; resourceMetadataUrl?: URL }\n): Promise<McpSignIn> {\n const configured = server.config.oauth?.authServerMetadataUrl;\n const info = await discoverOAuthServerInfo(server.config.url, {\n fetch: options.fetch,\n ...(options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n });\n if (!configured) await provider.saveDiscoveryState(info);\n const metadata = info.authorizationServerMetadata;\n if (metadata && !metadata.response_types_supported.includes('code'))\n throw new Error(\"The authorization server doesn't support authorization codes.\");\n if (\n metadata?.code_challenge_methods_supported &&\n !metadata.code_challenge_methods_supported.includes('S256')\n )\n throw new Error(\"The authorization server doesn't support PKCE S256.\");\n const scope =\n server.config.oauth?.scope || info.resourceMetadata?.scopes_supported?.join(' ') || undefined;\n let client = await provider.clientInformation();\n if (!client) {\n client = await registerClient(info.authorizationServerUrl, {\n ...(metadata ? { metadata } : {}),\n clientMetadata: provider.clientMetadata,\n ...(scope ? { scope } : {}),\n fetch: options.fetch,\n });\n await provider.saveClientInformation(client);\n }\n const { verifier, challenge } = await pkce();\n const state = randomState();\n await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));\n const url = new URL(\n metadata?.authorization_endpoint ?? new URL('/authorize', info.authorizationServerUrl)\n );\n if (!secureUrl(url)) throw new Error(\"The authorization server's sign-in address isn't https.\");\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', client.client_id);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('redirect_uri', provider.redirectUrl);\n url.searchParams.set('state', state);\n if (scope) url.searchParams.set('scope', scope);\n if (scope?.split(/\\s+/).includes('offline_access')) url.searchParams.set('prompt', 'consent');\n const resource = selectResource(server.config.url, info.resourceMetadata);\n if (resource) url.searchParams.set('resource', resource);\n return { url: url.href, state };\n}\n\nexport interface Callback {\n code?: string;\n state?: string;\n iss?: string;\n error?: string;\n error_description?: string;\n}\n\nexport async function finishSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n answer: string,\n fetch: McpFetch\n): Promise<void> {\n let callback: Callback;\n try {\n callback = JSON.parse(answer) as Callback;\n } catch {\n throw new Error(\"The sign-in answer isn't a callback.\");\n }\n const expected = (await provider.load()).oauthState;\n if (!expected || callback.state !== expected)\n throw new Error(\"The sign-in answer doesn't match this sign-in. Try again.\");\n if (callback.error)\n throw new Error(\n `Sign-in failed: ${sentence(callback.error_description?.trim() || callback.error)}`\n );\n if (!callback.code) throw new Error('The sign-in answer has no authorization code.');\n const configured = server.config.oauth?.authServerMetadataUrl;\n await authorizeMcp(provider, {\n serverUrl: server.config.url,\n authorizationCode: callback.code,\n ...(callback.iss ? { iss: callback.iss } : {}),\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch,\n });\n await provider.update((state) => {\n const next = { ...state };\n delete next.oauthState;\n delete next.codeVerifier;\n return next;\n });\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { AuthProvider, McpFetch, UnauthorizedContext } from '@earendil-works/pi-mcp';\nimport {\n authorizeMcp,\n discoverOAuthServerInfo,\n McpOAuthAuthorizationRequiredError,\n type McpOAuthState,\n type OAuthChallenge,\n parseWwwAuthenticate,\n registerClient,\n selectResource,\n stepUpScope,\n} from '@earendil-works/pi-mcp/oauth';\nimport type { PlainStore } from '../secrets/store.ts';\nimport type { McpSignIn } from '../services.ts';\nimport type { McpServerConfig } from './config.ts';\nimport { credentialKey } from './credentials.ts';\n\ntype Provider = Parameters<typeof authorizeMcp>[0];\ntype Tokens = Awaited<ReturnType<Provider['tokens']>>;\ntype Client = Awaited<ReturnType<Provider['clientInformation']>>;\ntype Discovery = NonNullable<McpOAuthState['discovery']>;\n\nexport const CLIENT_NAME = 'SLICC';\n\nexport function sentence(text: string): string {\n const trimmed = text.trim();\n const lowered = /^[A-Z][a-z]/.test(trimmed)\n ? `${trimmed[0]?.toLowerCase()}${trimmed.slice(1)}`\n : trimmed;\n return /[.!?]$/.test(lowered) ? lowered : `${lowered}.`;\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\nexport function secureUrl(url: URL): boolean {\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n}\n\nexport function signInRequired(cause?: unknown): McpOAuthAuthorizationRequiredError {\n const error = new McpOAuthAuthorizationRequiredError();\n if (cause !== undefined) error.cause = cause;\n return error;\n}\n\nexport function authProvider(provider: StoredOAuth, server: OAuthServer): AuthProvider {\n const configured = server.config.oauth?.authServerMetadataUrl;\n let inFlight: Promise<void> | undefined;\n const authorize = async (\n context: UnauthorizedContext,\n step: boolean,\n challenge: OAuthChallenge\n ) => {\n const granted = step ? await provider.tokens() : undefined;\n try {\n await authorizeMcp(provider, {\n serverUrl: context.serverUrl,\n ...(challenge.resourceMetadataUrl\n ? { resourceMetadataUrl: challenge.resourceMetadataUrl }\n : {}),\n skipRefresh: true,\n ...(step\n ? { scope: stepUpScope(granted?.scope, challenge.scope) }\n : challenge.scope\n ? { scope: challenge.scope }\n : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch: context.fetch,\n });\n } catch (error) {\n throw error instanceof McpOAuthAuthorizationRequiredError ? error : signInRequired(error);\n }\n };\n return {\n token: async () => (await provider.tokens())?.access_token,\n async onUnauthorized(context) {\n const challenge = parseWwwAuthenticate(context.response.headers.get('www-authenticate'));\n const step = challenge.error === 'insufficient_scope';\n if (!step && !inFlight && context.token !== undefined) {\n const current = (await provider.tokens())?.access_token;\n if (current !== undefined && current !== context.token) return;\n }\n inFlight ??= authorize(context, step, challenge).finally(() => {\n inFlight = undefined;\n });\n await inFlight;\n },\n };\n}\n\nconst bytesToBase64Url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nconst random = (length: number) => crypto.getRandomValues(new Uint8Array(length));\n\nexport async function pkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = bytesToBase64Url(random(32));\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return { verifier, challenge: bytesToBase64Url(new Uint8Array(digest)) };\n}\n\nexport function randomState(): string {\n return [...random(32)].map((byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport interface OAuthServer {\n name: string;\n config: McpServerConfig;\n}\n\nexport class StoredOAuth implements Provider {\n readonly redirectUrl: string;\n readonly clientMetadata: Provider['clientMetadata'];\n readonly #store: PlainStore;\n readonly #server: OAuthServer;\n #writes: Promise<unknown> = Promise.resolve();\n\n constructor(server: OAuthServer, store: PlainStore, redirectUrl: string) {\n this.#server = server;\n this.#store = store;\n this.redirectUrl = redirectUrl;\n this.clientMetadata = {\n client_name: server.config.oauth?.clientName ?? CLIENT_NAME,\n redirect_uris: [redirectUrl],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n };\n }\n\n get serverUrl(): string {\n return String(new URL(this.#server.config.url));\n }\n\n async #key(): Promise<string> {\n return credentialKey(this.#server.name, this.#server.config.url);\n }\n\n async load(): Promise<McpOAuthState> {\n await this.#writes;\n const state = (await this.#store.get(await this.#key())) as McpOAuthState | undefined;\n return state?.serverUrl === this.serverUrl ? state : { serverUrl: this.serverUrl };\n }\n\n async update(change: (state: McpOAuthState) => McpOAuthState): Promise<void> {\n const write = this.#writes.then(async () => {\n const key = await this.#key();\n const before = (await this.#store.get(key)) as McpOAuthState | undefined;\n await this.#store.set(\n key,\n change(before?.serverUrl === this.serverUrl ? before : { serverUrl: this.serverUrl })\n );\n });\n this.#writes = write.catch(() => undefined);\n await write;\n }\n\n async signedIn(): Promise<boolean> {\n return (await this.load()).tokens !== undefined;\n }\n\n state(): string {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async clientInformation(): Promise<Client> {\n const configured = this.#server.config.oauth?.clientId;\n return configured ? { client_id: configured } : (await this.load()).clientInformation;\n }\n\n async saveClientInformation(information: NonNullable<Client>): Promise<void> {\n if (this.#server.config.oauth?.clientId) return;\n await this.update((state) => ({ ...state, clientInformation: information }));\n }\n\n async tokens(): Promise<Tokens> {\n return (await this.load()).tokens;\n }\n\n async saveTokens(tokens: NonNullable<Tokens>): Promise<void> {\n const expires =\n tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;\n await this.update((state) => {\n const next: McpOAuthState = { ...state, tokens };\n if (expires === undefined) delete next.tokensExpireAt;\n else next.tokensExpireAt = expires;\n return next;\n });\n }\n\n redirectToAuthorization(): void {\n throw new McpOAuthAuthorizationRequiredError();\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.update((state) => ({ ...state, codeVerifier: verifier }));\n }\n\n async codeVerifier(): Promise<string> {\n const verifier = (await this.load()).codeVerifier;\n if (!verifier) throw new Error('No OAuth PKCE code verifier is stored');\n return verifier;\n }\n\n async invalidateCredentials(\n kind: 'all' | 'client' | 'tokens' | 'verifier' | 'discovery'\n ): Promise<void> {\n await this.update((state) => {\n const next = { ...state };\n if (kind === 'all' || kind === 'client') delete next.clientInformation;\n if (kind === 'all' || kind === 'tokens') {\n delete next.tokens;\n delete next.tokensExpireAt;\n }\n if (kind === 'all' || kind === 'verifier') delete next.codeVerifier;\n if (kind === 'all' || kind === 'discovery') delete next.discovery;\n if (kind === 'all') delete next.oauthState;\n return next;\n });\n }\n\n async saveDiscoveryState(discovery: Discovery): Promise<void> {\n await this.update((state) => ({ ...state, discovery }));\n }\n\n async discoveryState(): Promise<Discovery | undefined> {\n return (await this.load()).discovery;\n }\n\n async forget(): Promise<void> {\n await this.#writes;\n await this.#store.set(await this.#key(), undefined);\n }\n}\n\nexport type { McpSignIn } from '../services.ts';\n\nexport async function beginSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n options: { fetch: McpFetch; resourceMetadataUrl?: URL; register(tokenUrl: string): Promise<void> }\n): Promise<McpSignIn> {\n const configured = server.config.oauth?.authServerMetadataUrl;\n const info = await discoverOAuthServerInfo(server.config.url, {\n fetch: options.fetch,\n ...(options.resourceMetadataUrl ? { resourceMetadataUrl: options.resourceMetadataUrl } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n });\n if (!configured) await provider.saveDiscoveryState(info);\n const metadata = info.authorizationServerMetadata;\n if (metadata && !metadata.response_types_supported.includes('code'))\n throw new Error(\"The authorization server doesn't support authorization codes.\");\n if (\n metadata?.code_challenge_methods_supported &&\n !metadata.code_challenge_methods_supported.includes('S256')\n )\n throw new Error(\"The authorization server doesn't support PKCE S256.\");\n const scope =\n server.config.oauth?.scope || info.resourceMetadata?.scopes_supported?.join(' ') || undefined;\n let client = await provider.clientInformation();\n if (!client) {\n client = await registerClient(info.authorizationServerUrl, {\n ...(metadata ? { metadata } : {}),\n clientMetadata: provider.clientMetadata,\n ...(scope ? { scope } : {}),\n fetch: options.fetch,\n });\n await provider.saveClientInformation(client);\n }\n await options.register(\n new URL(metadata?.token_endpoint ?? new URL('/token', info.authorizationServerUrl)).href\n );\n const { verifier, challenge } = await pkce();\n const state = randomState();\n await provider.update((current) => ({ ...current, codeVerifier: verifier, oauthState: state }));\n const url = new URL(\n metadata?.authorization_endpoint ?? new URL('/authorize', info.authorizationServerUrl)\n );\n if (!secureUrl(url)) throw new Error(\"The authorization server's sign-in address isn't https.\");\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', client.client_id);\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('redirect_uri', provider.redirectUrl);\n url.searchParams.set('state', state);\n if (scope) url.searchParams.set('scope', scope);\n if (scope?.split(/\\s+/).includes('offline_access')) url.searchParams.set('prompt', 'consent');\n const resource = selectResource(server.config.url, info.resourceMetadata);\n if (resource) url.searchParams.set('resource', resource);\n return { url: url.href, state };\n}\n\nexport interface Callback {\n code?: string;\n state?: string;\n iss?: string;\n error?: string;\n error_description?: string;\n}\n\nexport async function finishSignIn(\n provider: StoredOAuth,\n server: OAuthServer,\n answer: string,\n fetch: McpFetch\n): Promise<void> {\n let callback: Callback;\n try {\n callback = JSON.parse(answer) as Callback;\n } catch {\n throw new Error(\"The sign-in answer isn't a callback.\");\n }\n const expected = (await provider.load()).oauthState;\n if (!expected || callback.state !== expected)\n throw new Error(\"The sign-in answer doesn't match this sign-in. Try again.\");\n if (callback.error)\n throw new Error(\n `Sign-in failed: ${sentence(callback.error_description?.trim() || callback.error)}`\n );\n if (!callback.code) throw new Error('The sign-in answer has no authorization code.');\n const configured = server.config.oauth?.authServerMetadataUrl;\n await authorizeMcp(provider, {\n serverUrl: server.config.url,\n authorizationCode: callback.code,\n ...(callback.iss ? { iss: callback.iss } : {}),\n ...(server.config.oauth?.scope ? { scope: server.config.oauth.scope } : {}),\n ...(configured ? { authorizationServerMetadataUrl: new URL(configured) } : {}),\n fetch,\n });\n await provider.update((state) => {\n const next = { ...state };\n delete next.oauthState;\n delete next.codeVerifier;\n return next;\n });\n}\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,qBAAqB;AAOvB,MAAM,cAAc;AAEpB,SAAS,SAAS,MAAsB;AAC7C,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,UAAU,cAAc,KAAK,OAAO,IACtC,GAAG,QAAQ,CAAC,GAAG,YAAY,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC,KAC/C;AACJ,SAAO,SAAS,KAAK,OAAO,IAAI,UAAU,GAAG,OAAO;AACtD;AAEA,MAAM,WAAW,oBAAI,IAAI,CAAC,aAAa,aAAa,OAAO,CAAC;AAErD,SAAS,UAAU,KAAmB;AAC3C,SAAO,IAAI,aAAa,YAAa,IAAI,aAAa,WAAW,SAAS,IAAI,IAAI,QAAQ;AAC5F;AAEO,SAAS,eAAe,OAAqD;AAClF,QAAM,QAAQ,IAAI,mCAAmC;AACrD,MAAI,UAAU,OAAW,OAAM,QAAQ;AACvC,SAAO;AACT;AAEO,SAAS,aAAa,UAAuB,QAAmC;AACrF,QAAM,aAAa,OAAO,OAAO,OAAO;AACxC,MAAI;AACJ,QAAM,YAAY,OAChB,SACA,MACA,cACG;AACH,UAAM,UAAU,OAAO,MAAM,SAAS,OAAO,IAAI;AACjD,QAAI;AACF,YAAM,aAAa,UAAU;AAAA,QAC3B,WAAW,QAAQ;AAAA,QACnB,GAAI,UAAU,sBACV,EAAE,qBAAqB,UAAU,oBAAoB,IACrD,CAAC;AAAA,QACL,aAAa;AAAA,QACb,GAAI,OACA,EAAE,OAAO,YAAY,SAAS,OAAO,UAAU,KAAK,EAAE,IACtD,UAAU,QACR,EAAE,OAAO,UAAU,MAAM,IACzB,CAAC;AAAA,QACP,GAAI,aAAa,EAAE,gCAAgC,IAAI,IAAI,UAAU,EAAE,IAAI,CAAC;AAAA,QAC5E,OAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,iBAAiB,qCAAqC,QAAQ,eAAe,KAAK;AAAA,IAC1F;AAAA,EACF;AACA,SAAO;AAAA,IACL,OAAO,aAAa,MAAM,SAAS,OAAO,IAAI;AAAA,IAC9C,MAAM,eAAe,SAAS;AAC5B,YAAM,YAAY,qBAAqB,QAAQ,SAAS,QAAQ,IAAI,kBAAkB,CAAC;AACvF,YAAM,OAAO,UAAU,UAAU;AACjC,UAAI,CAAC,QAAQ,CAAC,YAAY,QAAQ,UAAU,QAAW;AACrD,cAAM,WAAW,MAAM,SAAS,OAAO,IAAI;AAC3C,YAAI,YAAY,UAAa,YAAY,QAAQ,MAAO;AAAA,MAC1D;AACA,mBAAa,UAAU,SAAS,MAAM,SAAS,EAAE,QAAQ,MAAM;AAC7D,mBAAW;AAAA,MACb,CAAC;AACD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,MAAM,mBAAmB,CAAC,UACxB,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC,EAC/B,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAEtB,MAAM,SAAS,CAAC,WAAmB,OAAO,gBAAgB,IAAI,WAAW,MAAM,CAAC;AAEhF,eAAsB,OAAyD;AAC7E,QAAM,WAAW,iBAAiB,OAAO,EAAE,CAAC;AAC5C,QAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,QAAQ,CAAC;AACvF,SAAO,EAAE,UAAU,WAAW,iBAAiB,IAAI,WAAW,MAAM,CAAC,EAAE;AACzE;AAEO,SAAS,cAAsB;AACpC,SAAO,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAClF;AAOO,MAAM,YAAgC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,UAA4B,QAAQ,QAAQ;AAAA,EAE5C,YAAY,QAAqB,OAAmB,aAAqB;AACvE,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,SAAK,iBAAiB;AAAA,MACpB,aAAa,OAAO,OAAO,OAAO,cAAc;AAAA,MAChD,eAAe,CAAC,WAAW;AAAA,MAC3B,aAAa,CAAC,sBAAsB,eAAe;AAAA,MACnD,gBAAgB,CAAC,MAAM;AAAA,MACvB,4BAA4B;AAAA,MAC5B,GAAI,OAAO,OAAO,OAAO,QAAQ,EAAE,OAAO,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC3E;AAAA,EACF;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,OAAO,IAAI,IAAI,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,EAChD;AAAA,EAEA,MAAM,OAAwB;AAC5B,WAAO,cAAc,KAAK,QAAQ,MAAM,KAAK,QAAQ,OAAO,GAAG;AAAA,EACjE;AAAA,EAEA,MAAM,OAA+B;AACnC,UAAM,KAAK;AACX,UAAM,QAAS,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,CAAC;AACtD,WAAO,OAAO,cAAc,KAAK,YAAY,QAAQ,EAAE,WAAW,KAAK,UAAU;AAAA,EACnF;AAAA,EAEA,MAAM,OAAO,QAAgE;AAC3E,UAAM,QAAQ,KAAK,QAAQ,KAAK,YAAY;AAC1C,YAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,YAAM,SAAU,MAAM,KAAK,OAAO,IAAI,GAAG;AACzC,YAAM,KAAK,OAAO;AAAA,QAChB;AAAA,QACA,OAAO,QAAQ,cAAc,KAAK,YAAY,SAAS,EAAE,WAAW,KAAK,UAAU,CAAC;AAAA,MACtF;AAAA,IACF,CAAC;AACD,SAAK,UAAU,MAAM,MAAM,MAAM,MAAS;AAC1C,UAAM;AAAA,EACR;AAAA,EAEA,MAAM,WAA6B;AACjC,YAAQ,MAAM,KAAK,KAAK,GAAG,WAAW;AAAA,EACxC;AAAA,EAEA,QAAgB;AACd,UAAM,IAAI,mCAAmC;AAAA,EAC/C;AAAA,EAEA,MAAM,oBAAqC;AACzC,UAAM,aAAa,KAAK,QAAQ,OAAO,OAAO;AAC9C,WAAO,aAAa,EAAE,WAAW,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG;AAAA,EACtE;AAAA,EAEA,MAAM,sBAAsB,aAAiD;AAC3E,QAAI,KAAK,QAAQ,OAAO,OAAO,SAAU;AACzC,UAAM,KAAK,OAAO,CAAC,WAAW,EAAE,GAAG,OAAO,mBAAmB,YAAY,EAAE;AAAA,EAC7E;AAAA,EAEA,MAAM,SAA0B;AAC9B,YAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,WAAW,QAA4C;AAC3D,UAAM,UACJ,OAAO,eAAe,SAAY,SAAY,KAAK,IAAI,IAAI,OAAO,aAAa;AACjF,UAAM,KAAK,OAAO,CAAC,UAAU;AAC3B,YAAM,OAAsB,EAAE,GAAG,OAAO,OAAO;AAC/C,UAAI,YAAY,OAAW,QAAO,KAAK;AAAA,UAClC,MAAK,iBAAiB;AAC3B,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,0BAAgC;AAC9B,UAAM,IAAI,mCAAmC;AAAA,EAC/C;AAAA,EAEA,MAAM,iBAAiB,UAAiC;AACtD,UAAM,KAAK,OAAO,CAAC,WAAW,EAAE,GAAG,OAAO,cAAc,SAAS,EAAE;AAAA,EACrE;AAAA,EAEA,MAAM,eAAgC;AACpC,UAAM,YAAY,MAAM,KAAK,KAAK,GAAG;AACrC,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,uCAAuC;AACtE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,sBACJ,MACe;AACf,UAAM,KAAK,OAAO,CAAC,UAAU;AAC3B,YAAM,OAAO,EAAE,GAAG,MAAM;AACxB,UAAI,SAAS,SAAS,SAAS,SAAU,QAAO,KAAK;AACrD,UAAI,SAAS,SAAS,SAAS,UAAU;AACvC,eAAO,KAAK;AACZ,eAAO,KAAK;AAAA,MACd;AACA,UAAI,SAAS,SAAS,SAAS,WAAY,QAAO,KAAK;AACvD,UAAI,SAAS,SAAS,SAAS,YAAa,QAAO,KAAK;AACxD,UAAI,SAAS,MAAO,QAAO,KAAK;AAChC,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,mBAAmB,WAAqC;AAC5D,UAAM,KAAK,OAAO,CAAC,WAAW,EAAE,GAAG,OAAO,UAAU,EAAE;AAAA,EACxD;AAAA,EAEA,MAAM,iBAAiD;AACrD,YAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,KAAK;AACX,UAAM,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,GAAG,MAAS;AAAA,EACpD;AACF;AAIA,eAAsB,YACpB,UACA,QACA,SACoB;AACpB,QAAM,aAAa,OAAO,OAAO,OAAO;AACxC,QAAM,OAAO,MAAM,wBAAwB,OAAO,OAAO,KAAK;AAAA,IAC5D,OAAO,QAAQ;AAAA,IACf,GAAI,QAAQ,sBAAsB,EAAE,qBAAqB,QAAQ,oBAAoB,IAAI,CAAC;AAAA,IAC1F,GAAI,aAAa,EAAE,gCAAgC,IAAI,IAAI,UAAU,EAAE,IAAI,CAAC;AAAA,EAC9E,CAAC;AACD,MAAI,CAAC,WAAY,OAAM,SAAS,mBAAmB,IAAI;AACvD,QAAM,WAAW,KAAK;AACtB,MAAI,YAAY,CAAC,SAAS,yBAAyB,SAAS,MAAM;AAChE,UAAM,IAAI,MAAM,+DAA+D;AACjF,MACE,UAAU,oCACV,CAAC,SAAS,iCAAiC,SAAS,MAAM;AAE1D,UAAM,IAAI,MAAM,qDAAqD;AACvE,QAAM,QACJ,OAAO,OAAO,OAAO,SAAS,KAAK,kBAAkB,kBAAkB,KAAK,GAAG,KAAK;AACtF,MAAI,SAAS,MAAM,SAAS,kBAAkB;AAC9C,MAAI,CAAC,QAAQ;AACX,aAAS,MAAM,eAAe,KAAK,wBAAwB;AAAA,MACzD,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MAC/B,gBAAgB,SAAS;AAAA,MACzB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,MACzB,OAAO,QAAQ;AAAA,IACjB,CAAC;AACD,UAAM,SAAS,sBAAsB,MAAM;AAAA,EAC7C;AACA,QAAM,QAAQ;AAAA,IACZ,IAAI,IAAI,UAAU,kBAAkB,IAAI,IAAI,UAAU,KAAK,sBAAsB,CAAC,EAAE;AAAA,EACtF;AACA,QAAM,EAAE,UAAU,UAAU,IAAI,MAAM,KAAK;AAC3C,QAAM,QAAQ,YAAY;AAC1B,QAAM,SAAS,OAAO,CAAC,aAAa,EAAE,GAAG,SAAS,cAAc,UAAU,YAAY,MAAM,EAAE;AAC9F,QAAM,MAAM,IAAI;AAAA,IACd,UAAU,0BAA0B,IAAI,IAAI,cAAc,KAAK,sBAAsB;AAAA,EACvF;AACA,MAAI,CAAC,UAAU,GAAG,EAAG,OAAM,IAAI,MAAM,yDAAyD;AAC9F,MAAI,aAAa,IAAI,iBAAiB,MAAM;AAC5C,MAAI,aAAa,IAAI,aAAa,OAAO,SAAS;AAClD,MAAI,aAAa,IAAI,kBAAkB,SAAS;AAChD,MAAI,aAAa,IAAI,yBAAyB,MAAM;AACpD,MAAI,aAAa,IAAI,gBAAgB,SAAS,WAAW;AACzD,MAAI,aAAa,IAAI,SAAS,KAAK;AACnC,MAAI,MAAO,KAAI,aAAa,IAAI,SAAS,KAAK;AAC9C,MAAI,OAAO,MAAM,KAAK,EAAE,SAAS,gBAAgB,EAAG,KAAI,aAAa,IAAI,UAAU,SAAS;AAC5F,QAAM,WAAW,eAAe,OAAO,OAAO,KAAK,KAAK,gBAAgB;AACxE,MAAI,SAAU,KAAI,aAAa,IAAI,YAAY,QAAQ;AACvD,SAAO,EAAE,KAAK,IAAI,MAAM,MAAM;AAChC;AAUA,eAAsB,aACpB,UACA,QACA,QACA,OACe;AACf,MAAI;AACJ,MAAI;AACF,eAAW,KAAK,MAAM,MAAM;AAAA,EAC9B,QAAQ;AACN,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,QAAM,YAAY,MAAM,SAAS,KAAK,GAAG;AACzC,MAAI,CAAC,YAAY,SAAS,UAAU;AAClC,UAAM,IAAI,MAAM,2DAA2D;AAC7E,MAAI,SAAS;AACX,UAAM,IAAI;AAAA,MACR,mBAAmB,SAAS,SAAS,mBAAmB,KAAK,KAAK,SAAS,KAAK,CAAC;AAAA,IACnF;AACF,MAAI,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,+CAA+C;AACnF,QAAM,aAAa,OAAO,OAAO,OAAO;AACxC,QAAM,aAAa,UAAU;AAAA,IAC3B,WAAW,OAAO,OAAO;AAAA,IACzB,mBAAmB,SAAS;AAAA,IAC5B,GAAI,SAAS,MAAM,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC;AAAA,IAC5C,GAAI,OAAO,OAAO,OAAO,QAAQ,EAAE,OAAO,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IACzE,GAAI,aAAa,EAAE,gCAAgC,IAAI,IAAI,UAAU,EAAE,IAAI,CAAC;AAAA,IAC5E;AAAA,EACF,CAAC;AACD,QAAM,SAAS,OAAO,CAAC,UAAU;AAC/B,UAAM,OAAO,EAAE,GAAG,MAAM;AACxB,WAAO,KAAK;AACZ,WAAO,KAAK;AACZ,WAAO;AAAA,EACT,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { StreamableHttpTransport } from "@earendil-works/pi-mcp";
2
+ import { scrubText } from "../secrets/scrub.js";
2
3
  import { mcpAccounts } from "./accounts.js";
3
4
  import { McpCatalog } from "./catalog.js";
4
5
  import {
@@ -7,7 +8,7 @@ import {
7
8
  usesOAuth
8
9
  } from "./config.js";
9
10
  import { McpServerConnection } from "./connection.js";
10
- import { resolveHeaders, scrub, secretNames } from "./credentials.js";
11
+ import { resolveHeaders, secretNames } from "./credentials.js";
11
12
  import { authProvider as oauthAuth, StoredOAuth } from "./oauth.js";
12
13
  import { configPath, logPath, shown } from "./paths.js";
13
14
  import { patchSse } from "./sse.js";
@@ -54,7 +55,7 @@ ${connection.missing.join(",")}`
54
55
  auth`
55
56
  };
56
57
  if (connection.state !== "failed") return void 0;
57
- const error = scrub(connection.error ?? "unknown error", connection.secrets);
58
+ const error = connection.error ?? "unknown error";
58
59
  const blocked = cors && CORS_ERROR.test(error);
59
60
  return {
60
61
  ...base,
@@ -79,9 +80,9 @@ connected
79
80
  ${connection.entry.config.url}`
80
81
  };
81
82
  }
82
- function logLine(server, params, secrets, at) {
83
+ function logLine(server, params, at) {
83
84
  const { level, logger, data } = params ?? {};
84
- const text = scrub(typeof data === "string" ? data : JSON.stringify(data) ?? "", secrets);
85
+ const text = typeof data === "string" ? data : JSON.stringify(data) ?? "";
85
86
  return `${at.toISOString()} [${server}] ${level ?? "info"}${logger ? ` ${logger}` : ""}: ${text}
86
87
  `;
87
88
  }
@@ -91,8 +92,9 @@ function serverLog(options, context) {
91
92
  const { env } = options;
92
93
  return {
93
94
  write(connection, params) {
94
- const line = logLine(connection.name, params, connection.secrets, /* @__PURE__ */ new Date());
95
+ const text = logLine(connection.name, params, /* @__PURE__ */ new Date());
95
96
  logging = logging.then(async () => {
97
+ const line = await scrubText(text);
96
98
  const info = await env.fileInfo(path, context);
97
99
  if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)
98
100
  await env.renameFile(path, `${path}.1`, context);
@@ -118,22 +120,30 @@ async function attachMcp(catalog, options, context) {
118
120
  let initial = /* @__PURE__ */ new Set();
119
121
  let first = true;
120
122
  const log = serverLog(options, context);
121
- const fetcher = (input, init) => options.fetch(input, init);
122
- const oauth = (entry) => new StoredOAuth(entry, options.credentials, options.redirect);
123
+ const { vault } = options;
124
+ const base = (input, init) => options.fetch(input, init);
125
+ const fetcher = (server) => vault.fetch(server, base);
126
+ const oauth = (entry) => new StoredOAuth(entry, options.store, options.redirect);
123
127
  const transport = async (entry) => {
124
- const resolved = await resolveHeaders(entry.name, entry.config, options.credentials);
128
+ const resolved = resolveHeaders(entry.name, entry.config, vault.records);
125
129
  if (!resolved.ok) return { missing: resolved.missing };
126
130
  const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : void 0;
127
131
  return {
128
132
  transport: new StreamableHttpTransport({
129
133
  url: entry.config.url,
130
134
  headers: resolved.headers,
131
- fetch: fetcher,
135
+ fetch: fetcher(entry.name),
132
136
  ...authProvider ? { authProvider } : {}
133
- }),
134
- secrets: resolved.secrets
137
+ })
135
138
  };
136
139
  };
140
+ const register = (entries) => vault.servers(
141
+ [...entries].map((entry) => ({
142
+ server: entry.name,
143
+ url: entry.config.url,
144
+ names: secretNames(entry.config)
145
+ }))
146
+ ).catch(() => void 0);
137
147
  const open = (entry) => {
138
148
  const connection = new McpServerConnection({
139
149
  entry,
@@ -170,6 +180,7 @@ async function attachMcp(catalog, options, context) {
170
180
  }
171
181
  if (first) initial = new Set(wanted.keys());
172
182
  first = false;
183
+ void register(wanted.values());
173
184
  for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);
174
185
  catalog.rebuild();
175
186
  notify();
@@ -192,10 +203,21 @@ async function attachMcp(catalog, options, context) {
192
203
  void connection.close();
193
204
  catalog.rebuild();
194
205
  };
206
+ const stopRecords = vault.records.state.subscribe(() => {
207
+ for (const connection of [...catalog.connections.values()])
208
+ if (connection.state === "needs-secret") restart(connection);
209
+ notify();
210
+ });
195
211
  return {
196
212
  ...mcpAccounts(
197
213
  catalog,
198
- { credentials: options.credentials, oauth, fetch: fetcher },
214
+ {
215
+ records: vault.records,
216
+ oauth,
217
+ fetch: fetcher,
218
+ grant: (server) => vault.grant(server, fetcher(server)),
219
+ register: (server, url) => vault.token(server, url)
220
+ },
199
221
  restart,
200
222
  listeners
201
223
  ),
@@ -203,12 +225,12 @@ async function attachMcp(catalog, options, context) {
203
225
  name: connection.name,
204
226
  state: connection.state,
205
227
  tools: connection.tools.length,
206
- ...connection.error ? { error: scrub(connection.error, connection.secrets) } : {}
228
+ ...connection.error ? { error: connection.error } : {}
207
229
  })),
208
- secrets: () => [...catalog.connections.values()].flatMap((connection) => connection.secrets),
209
230
  reload,
210
231
  async close(using) {
211
232
  clearTimeout(timer);
233
+ stopRecords();
212
234
  await watcher?.close(using);
213
235
  const all = [...catalog.connections.values()];
214
236
  catalog.connections.clear();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/runtime.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { CredentialStore } from '@earendil-works/pi-ai';\nimport type { Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { StreamableHttpTransport } from '@earendil-works/pi-mcp';\nimport type { LickEvent, Licks } from '../licks/licks.ts';\nimport { type McpAccounts, mcpAccounts } from './accounts.ts';\nimport { McpCatalog } from './catalog.ts';\nimport {\n type McpConfigProblem,\n type McpServerEntry,\n parseMcpConfig,\n sameConfig,\n usesOAuth,\n} from './config.ts';\nimport { McpServerConnection } from './connection.ts';\nimport { resolveHeaders, scrub, secretNames } from './credentials.ts';\nimport { authProvider as oauthAuth, StoredOAuth } from './oauth.ts';\nimport { configPath, logPath, shown } from './paths.ts';\nimport { patchSse } from './sse.ts';\n\nexport const MCP_LOG_MAX_BYTES = 5 * 1024 * 1024;\n\nexport { configPath, logPath, shown } from './paths.ts';\n\nexport interface McpAttach {\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n agentDir?: string;\n credentials: CredentialStore;\n fetch: typeof fetch;\n version: string;\n reloadMs: number;\n codemode: boolean;\n cors: boolean;\n redirect: string;\n}\n\nexport interface ServerStatus {\n name: string;\n state: string;\n tools: number;\n error?: string;\n}\n\nexport type { McpAccounts } from './accounts.ts';\n\nexport interface McpRuntime extends McpAccounts {\n status(): ServerStatus[];\n secrets(): string[];\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport function problemLick(problem: McpConfigProblem, home: string, dir?: string): LickEvent {\n const file = shown(configPath(home, dir), home);\n const where = problem.server ? `server \"${problem.server}\"` : file;\n const field = problem.field ? `, field ${problem.field}` : '';\n return {\n channel: 'fswatch',\n source: `mcp:${file}`,\n title: `MCP config problem: ${where}${field}`,\n text: problem.message,\n body: `${file}: the entry is skipped until it is fixed. The format is in the slicc-agent README.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${problem.server ?? ''}\\n${problem.field ?? ''}\\n${problem.message}`,\n };\n}\n\nconst CORS_ERROR = /failed to fetch|networkerror|load failed|cors/i;\n\nexport function stateLick(connection: McpServerConnection, cors: boolean): LickEvent | undefined {\n const name = connection.name;\n const base = { channel: 'fswatch' as const, source: `mcp:${name}`, target: 'cone' as const };\n if (connection.state === 'needs-secret')\n return {\n ...base,\n title: `MCP server \"${name}\" needs a token`,\n text: `Its headers use ${connection.missing.map((item) => `\\${${item}}`).join(', ')}. Add the value in Settings \u203A MCP servers.`,\n severity: 'warn',\n eventId: `${name}\\nsecret\\n${connection.missing.join(',')}`,\n };\n if (connection.state === 'needs-auth')\n return {\n ...base,\n title: secretNames(connection.entry.config).length\n ? `MCP server \"${name}\" rejected its token`\n : `MCP server \"${name}\" needs sign-in`,\n text: connection.signInMessage(),\n severity: 'warn',\n eventId: `${name}\\nauth`,\n };\n if (connection.state !== 'failed') return undefined;\n const error = scrub(connection.error ?? 'unknown error', connection.secrets);\n const blocked = cors && CORS_ERROR.test(error);\n return {\n ...base,\n title: `MCP server \"${name}\" can't connect`,\n text: blocked\n ? \"Seven reaches it with the page's own fetch, and the server sends no CORS headers. Link the `slicc` CLI or connect the SLICC extension; either fetches without CORS.\"\n : error,\n severity: 'warn',\n eventId: `${name}\\nfailed\\n${blocked ? 'cors' : error}`,\n };\n}\n\nexport function connectedLick(connection: McpServerConnection): LickEvent {\n const count = connection.tools.length;\n return {\n channel: 'fswatch',\n source: `mcp:${connection.name}`,\n title: `MCP server \"${connection.name}\" connected`,\n text: `${connection.entry.config.url} offers ${count} tool${count === 1 ? '' : 's'}.`,\n target: 'cone',\n eventId: `${connection.name}\\nconnected\\n${connection.entry.config.url}`,\n };\n}\n\nexport function logLine(\n server: string,\n params: unknown,\n secrets: readonly string[],\n at: Date\n): string {\n const { level, logger, data } = (params ?? {}) as {\n level?: string;\n logger?: string;\n data?: unknown;\n };\n const text = scrub(typeof data === 'string' ? data : (JSON.stringify(data) ?? ''), secrets);\n return `${at.toISOString()} [${server}] ${level ?? 'info'}${logger ? ` ${logger}` : ''}: ${text}\\n`;\n}\n\nfunction serverLog(options: McpAttach, context: Context) {\n let logging = Promise.resolve();\n const path = logPath(options.home, options.agentDir);\n const { env } = options;\n return {\n write(connection: McpServerConnection, params: unknown) {\n const line = logLine(connection.name, params, connection.secrets, new Date());\n logging = logging.then(async () => {\n const info = await env.fileInfo(path, context);\n if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)\n await env.renameFile(path, `${path}.1`, context);\n await env.appendFile(path, line, context);\n });\n },\n settled: () => logging,\n };\n}\n\nexport async function attachMcp(\n catalog: McpCatalog,\n options: McpAttach,\n context: Context\n): Promise<McpRuntime> {\n const { env, home } = options;\n catalog.codemodeOn = options.codemode;\n const reported = new Set<string>();\n const report = async (lick: LickEvent | undefined, using: Context) => {\n if (!lick?.eventId || reported.has(lick.eventId)) return;\n reported.add(lick.eventId);\n await options.licks.deliver(lick, using).catch(() => undefined);\n };\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n let initial: Set<string> = new Set();\n let first = true;\n const log = serverLog(options, context);\n const fetcher = (input: string | URL, init?: RequestInit) => options.fetch(input, init);\n const oauth = (entry: McpServerEntry) =>\n new StoredOAuth(entry, options.credentials, options.redirect);\n const transport = async (entry: McpServerEntry) => {\n const resolved = await resolveHeaders(entry.name, entry.config, options.credentials);\n if (!resolved.ok) return { missing: resolved.missing };\n const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : undefined;\n return {\n transport: new StreamableHttpTransport({\n url: entry.config.url,\n headers: resolved.headers,\n fetch: fetcher,\n ...(authProvider ? { authProvider } : {}),\n }),\n secrets: resolved.secrets,\n };\n };\n const open = (entry: McpServerEntry) => {\n const connection = new McpServerConnection({\n entry,\n version: options.version,\n transport,\n onTools: () => catalog.rebuild(),\n onLog: log.write,\n onChange: (changed) => {\n if (catalog.connections.get(changed.name) !== changed) return;\n if (changed.state === 'connected' && !initial.has(changed.name))\n void report(connectedLick(changed), context);\n void report(stateLick(changed, options.cors), context);\n if (changed.state !== 'connected') catalog.rebuild();\n notify();\n },\n });\n catalog.connections.set(entry.name, connection);\n void connection.getClient().catch(() => undefined);\n };\n const reload = async (using: Context) => {\n const read = await env.readTextFile(configPath(home, options.agentDir), using);\n const parsed = parseMcpConfig(read.ok ? read.value : undefined);\n for (const problem of parsed.problems)\n await report(problemLick(problem, home, options.agentDir), using);\n const wanted = new Map(\n parsed.servers\n .filter((entry) => entry.config.enabled !== false)\n .map((entry) => [entry.name, entry])\n );\n const closing: Promise<void>[] = [];\n for (const [name, connection] of [...catalog.connections]) {\n const next = wanted.get(name);\n if (next && sameConfig(next.config, connection.entry.config)) continue;\n catalog.connections.delete(name);\n closing.push(connection.close());\n }\n if (first) initial = new Set(wanted.keys());\n first = false;\n for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);\n catalog.rebuild();\n notify();\n await Promise.all(closing);\n };\n await reload(context);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const schedule = () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n };\n const watched = await env.watch(\n [{ path: configPath(home, options.agentDir) }],\n schedule,\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const restart = (connection: McpServerConnection) => {\n open(connection.entry);\n void connection.close();\n catalog.rebuild();\n };\n return {\n ...mcpAccounts(\n catalog,\n { credentials: options.credentials, oauth, fetch: fetcher },\n restart,\n listeners\n ),\n status: () =>\n [...catalog.connections.values()].map((connection) => ({\n name: connection.name,\n state: connection.state,\n tools: connection.tools.length,\n ...(connection.error ? { error: scrub(connection.error, connection.secrets) } : {}),\n })),\n secrets: () => [...catalog.connections.values()].flatMap((connection) => connection.secrets),\n reload,\n async close(using) {\n clearTimeout(timer);\n await watcher?.close(using);\n const all = [...catalog.connections.values()];\n catalog.connections.clear();\n await Promise.all(all.map((connection) => connection.close()));\n catalog.rebuild();\n await log.settled();\n },\n };\n}\n\nexport async function startMcp(\n registry: Registry,\n options: McpAttach,\n context: Context\n): Promise<{ catalog: McpCatalog; runtime: McpRuntime }> {\n patchSse();\n const catalog = new McpCatalog(registry);\n return { catalog, runtime: await attachMcp(catalog, options, context) };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Registry } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { StreamableHttpTransport } from '@earendil-works/pi-mcp';\nimport type { LickEvent, Licks } from '../licks/licks.ts';\nimport type { McpServerSource } from '../secrets/endpoints.ts';\nimport { scrubText } from '../secrets/scrub.ts';\nimport type { PlainStore } from '../secrets/store.ts';\nimport { type McpAccounts, mcpAccounts } from './accounts.ts';\nimport { McpCatalog } from './catalog.ts';\nimport {\n type McpConfigProblem,\n type McpServerEntry,\n parseMcpConfig,\n sameConfig,\n usesOAuth,\n} from './config.ts';\nimport { McpServerConnection } from './connection.ts';\nimport { type McpRecords, resolveHeaders, secretNames } from './credentials.ts';\nimport { authProvider as oauthAuth, StoredOAuth } from './oauth.ts';\nimport { configPath, logPath, shown } from './paths.ts';\nimport { patchSse } from './sse.ts';\n\nexport const MCP_LOG_MAX_BYTES = 5 * 1024 * 1024;\n\nexport { configPath, logPath, shown } from './paths.ts';\n\nexport interface McpVault {\n records: McpRecords & {\n refresh(context: Context): Promise<void>;\n state: { subscribe(listener: () => void): () => void };\n };\n fetch(server: string, base: typeof fetch): typeof fetch;\n grant(server: string, base: typeof fetch): typeof fetch;\n servers(list: readonly McpServerSource[]): Promise<void>;\n token(server: string, url: string): Promise<void>;\n}\n\nexport interface McpAttach {\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n agentDir?: string;\n store: PlainStore;\n vault: McpVault;\n fetch: typeof fetch;\n version: string;\n reloadMs: number;\n codemode: boolean;\n cors: boolean;\n redirect: string;\n}\n\nexport interface ServerStatus {\n name: string;\n state: string;\n tools: number;\n error?: string;\n}\n\nexport type { McpAccounts } from './accounts.ts';\n\nexport interface McpRuntime extends McpAccounts {\n status(): ServerStatus[];\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport function problemLick(problem: McpConfigProblem, home: string, dir?: string): LickEvent {\n const file = shown(configPath(home, dir), home);\n const where = problem.server ? `server \"${problem.server}\"` : file;\n const field = problem.field ? `, field ${problem.field}` : '';\n return {\n channel: 'fswatch',\n source: `mcp:${file}`,\n title: `MCP config problem: ${where}${field}`,\n text: problem.message,\n body: `${file}: the entry is skipped until it is fixed. The format is in the slicc-agent README.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${problem.server ?? ''}\\n${problem.field ?? ''}\\n${problem.message}`,\n };\n}\n\nconst CORS_ERROR = /failed to fetch|networkerror|load failed|cors/i;\n\nexport function stateLick(connection: McpServerConnection, cors: boolean): LickEvent | undefined {\n const name = connection.name;\n const base = { channel: 'fswatch' as const, source: `mcp:${name}`, target: 'cone' as const };\n if (connection.state === 'needs-secret')\n return {\n ...base,\n title: `MCP server \"${name}\" needs a token`,\n text: `Its headers use ${connection.missing.map((item) => `\\${${item}}`).join(', ')}. Add the value in Settings \u203A MCP servers.`,\n severity: 'warn',\n eventId: `${name}\\nsecret\\n${connection.missing.join(',')}`,\n };\n if (connection.state === 'needs-auth')\n return {\n ...base,\n title: secretNames(connection.entry.config).length\n ? `MCP server \"${name}\" rejected its token`\n : `MCP server \"${name}\" needs sign-in`,\n text: connection.signInMessage(),\n severity: 'warn',\n eventId: `${name}\\nauth`,\n };\n if (connection.state !== 'failed') return undefined;\n const error = connection.error ?? 'unknown error';\n const blocked = cors && CORS_ERROR.test(error);\n return {\n ...base,\n title: `MCP server \"${name}\" can't connect`,\n text: blocked\n ? \"Seven reaches it with the page's own fetch, and the server sends no CORS headers. Link the `slicc` CLI or connect the SLICC extension; either fetches without CORS.\"\n : error,\n severity: 'warn',\n eventId: `${name}\\nfailed\\n${blocked ? 'cors' : error}`,\n };\n}\n\nexport function connectedLick(connection: McpServerConnection): LickEvent {\n const count = connection.tools.length;\n return {\n channel: 'fswatch',\n source: `mcp:${connection.name}`,\n title: `MCP server \"${connection.name}\" connected`,\n text: `${connection.entry.config.url} offers ${count} tool${count === 1 ? '' : 's'}.`,\n target: 'cone',\n eventId: `${connection.name}\\nconnected\\n${connection.entry.config.url}`,\n };\n}\n\nexport function logLine(server: string, params: unknown, at: Date): string {\n const { level, logger, data } = (params ?? {}) as {\n level?: string;\n logger?: string;\n data?: unknown;\n };\n const text = typeof data === 'string' ? data : (JSON.stringify(data) ?? '');\n return `${at.toISOString()} [${server}] ${level ?? 'info'}${logger ? ` ${logger}` : ''}: ${text}\\n`;\n}\n\nfunction serverLog(options: McpAttach, context: Context) {\n let logging = Promise.resolve();\n const path = logPath(options.home, options.agentDir);\n const { env } = options;\n return {\n write(connection: McpServerConnection, params: unknown) {\n const text = logLine(connection.name, params, new Date());\n logging = logging.then(async () => {\n const line = await scrubText(text);\n const info = await env.fileInfo(path, context);\n if (info.ok && info.value.size > MCP_LOG_MAX_BYTES)\n await env.renameFile(path, `${path}.1`, context);\n await env.appendFile(path, line, context);\n });\n },\n settled: () => logging,\n };\n}\n\nexport async function attachMcp(\n catalog: McpCatalog,\n options: McpAttach,\n context: Context\n): Promise<McpRuntime> {\n const { env, home } = options;\n catalog.codemodeOn = options.codemode;\n const reported = new Set<string>();\n const report = async (lick: LickEvent | undefined, using: Context) => {\n if (!lick?.eventId || reported.has(lick.eventId)) return;\n reported.add(lick.eventId);\n await options.licks.deliver(lick, using).catch(() => undefined);\n };\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n let initial: Set<string> = new Set();\n let first = true;\n const log = serverLog(options, context);\n const { vault } = options;\n const base = (input: string | URL | Request, init?: RequestInit) => options.fetch(input, init);\n const fetcher = (server: string) => vault.fetch(server, base);\n const oauth = (entry: McpServerEntry) => new StoredOAuth(entry, options.store, options.redirect);\n const transport = async (entry: McpServerEntry) => {\n const resolved = resolveHeaders(entry.name, entry.config, vault.records);\n if (!resolved.ok) return { missing: resolved.missing };\n const authProvider = usesOAuth(entry.config) ? oauthAuth(oauth(entry), entry) : undefined;\n return {\n transport: new StreamableHttpTransport({\n url: entry.config.url,\n headers: resolved.headers,\n fetch: fetcher(entry.name),\n ...(authProvider ? { authProvider } : {}),\n }),\n };\n };\n const register = (entries: Iterable<McpServerEntry>) =>\n vault\n .servers(\n [...entries].map((entry) => ({\n server: entry.name,\n url: entry.config.url,\n names: secretNames(entry.config),\n }))\n )\n .catch(() => undefined);\n const open = (entry: McpServerEntry) => {\n const connection = new McpServerConnection({\n entry,\n version: options.version,\n transport,\n onTools: () => catalog.rebuild(),\n onLog: log.write,\n onChange: (changed) => {\n if (catalog.connections.get(changed.name) !== changed) return;\n if (changed.state === 'connected' && !initial.has(changed.name))\n void report(connectedLick(changed), context);\n void report(stateLick(changed, options.cors), context);\n if (changed.state !== 'connected') catalog.rebuild();\n notify();\n },\n });\n catalog.connections.set(entry.name, connection);\n void connection.getClient().catch(() => undefined);\n };\n const reload = async (using: Context) => {\n const read = await env.readTextFile(configPath(home, options.agentDir), using);\n const parsed = parseMcpConfig(read.ok ? read.value : undefined);\n for (const problem of parsed.problems)\n await report(problemLick(problem, home, options.agentDir), using);\n const wanted = new Map(\n parsed.servers\n .filter((entry) => entry.config.enabled !== false)\n .map((entry) => [entry.name, entry])\n );\n const closing: Promise<void>[] = [];\n for (const [name, connection] of [...catalog.connections]) {\n const next = wanted.get(name);\n if (next && sameConfig(next.config, connection.entry.config)) continue;\n catalog.connections.delete(name);\n closing.push(connection.close());\n }\n if (first) initial = new Set(wanted.keys());\n first = false;\n void register(wanted.values());\n for (const [name, entry] of wanted) if (!catalog.connections.has(name)) open(entry);\n catalog.rebuild();\n notify();\n await Promise.all(closing);\n };\n await reload(context);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const schedule = () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n };\n const watched = await env.watch(\n [{ path: configPath(home, options.agentDir) }],\n schedule,\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const restart = (connection: McpServerConnection) => {\n open(connection.entry);\n void connection.close();\n catalog.rebuild();\n };\n const stopRecords = vault.records.state.subscribe(() => {\n for (const connection of [...catalog.connections.values()])\n if (connection.state === 'needs-secret') restart(connection);\n notify();\n });\n return {\n ...mcpAccounts(\n catalog,\n {\n records: vault.records,\n oauth,\n fetch: fetcher,\n grant: (server) => vault.grant(server, fetcher(server)),\n register: (server, url) => vault.token(server, url),\n },\n restart,\n listeners\n ),\n status: () =>\n [...catalog.connections.values()].map((connection) => ({\n name: connection.name,\n state: connection.state,\n tools: connection.tools.length,\n ...(connection.error ? { error: connection.error } : {}),\n })),\n reload,\n async close(using) {\n clearTimeout(timer);\n stopRecords();\n await watcher?.close(using);\n const all = [...catalog.connections.values()];\n catalog.connections.clear();\n await Promise.all(all.map((connection) => connection.close()));\n catalog.rebuild();\n await log.settled();\n },\n };\n}\n\nexport async function startMcp(\n registry: Registry,\n options: McpAttach,\n context: Context\n): Promise<{ catalog: McpCatalog; runtime: McpRuntime }> {\n patchSse();\n const catalog = new McpCatalog(registry);\n return { catalog, runtime: await attachMcp(catalog, options, context) };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["configPath", "logPath", "shown"]
7
7
  }
package/dist/mcp/tools.js CHANGED
@@ -15,7 +15,6 @@ import {
15
15
  scriptResult,
16
16
  spiller
17
17
  } from "./content.js";
18
- import { scrub } from "./credentials.js";
19
18
  const LIST_MCP_RESOURCES = "list_mcp_resources";
20
19
  const LIST_MCP_RESOURCE_TEMPLATES = "list_mcp_resource_templates";
21
20
  function parametersOf(schema) {
@@ -56,9 +55,8 @@ function progressText(progress) {
56
55
  const total = progress.total === void 0 ? "" : `/${progress.total}`;
57
56
  return progress.message ?? `Progress ${progress.progress}${total}`;
58
57
  }
59
- function failure(entry, error) {
60
- const message = error instanceof Error ? error.message : String(error);
61
- throw new Error(scrub(message, entry.connection.secrets));
58
+ function failure(error) {
59
+ throw new Error(error instanceof Error ? error.message : String(error));
62
60
  }
63
61
  function directTool(entry, readable) {
64
62
  return defineTool({
@@ -75,7 +73,7 @@ function directTool(entry, readable) {
75
73
  };
76
74
  const signal = signalOf(context);
77
75
  if (signal) options.signal = signal;
78
- const result = await entry.connection.callTool(entry.tool.name, args, options).catch((error) => failure(entry, error));
76
+ const result = await entry.connection.callTool(entry.tool.name, args, options).catch(failure);
79
77
  const converted = await convertResult(
80
78
  entry.server,
81
79
  entry.tool.name,
@@ -109,7 +107,7 @@ function scriptTool(entry, signal) {
109
107
  const result = await entry.connection.callTool(entry.tool.name, args ?? {}, {
110
108
  signal: combined,
111
109
  timeoutMs: entry.connection.timeoutMs
112
- }).catch((error) => failure(entry, error));
110
+ }).catch(failure);
113
111
  return scriptResult(result);
114
112
  }
115
113
  };