@ai-ecoverse/slicc-agent 3.35.0 → 3.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -283,13 +283,15 @@ MCP servers reachable over Streamable HTTP become native agent tools. The client
283
283
  ```
284
284
 
285
285
  - **Fields.** `url`, `type` (`http` or `streamable-http`), `headers`, `exposure`, `toolExposure`, `enabled`, `timeout` (seconds, default 60; progress resets it) and `description`.
286
- - **Refused.** `command` (stdio), `type: "sse"`, `oauth`, `auth`, `!command` values, and literal secrets: a credential-like header (`Authorization`, `Cookie`, or a name with `token`, `key`, `secret`, `password` or `session`) or a URL with a password or a key in its query. The file is readable by every agent, so it holds only `${NAME}` placeholders.
286
+ - **Refused.** `command` (stdio), `type: "sse"`, `auth`, the `oauth` fields that need a loopback or a client secret, `!command` values, and literal secrets: a credential-like header (`Authorization`, `Cookie`, or a name with `token`, `key`, `secret`, `password` or `session`) or a URL with a password or a key in its query. The file is readable by every agent, so it holds only `${NAME}` placeholders.
287
287
  - **Problems.** Each refused or broken entry is skipped and becomes one `warn` lick naming the server and the field; the other servers still connect.
288
288
  - **Reconcile.** The worker watches the file. An unchanged server keeps its session, a changed one reconnects, and a removed one is closed and loses its tools. A server added later posts a "connected" lick naming its URL.
289
289
 
290
290
  **Per agent.** Today every cone reads the one file in `/home` and gets every enabled server. A scoop doesn't read a file of its own: it inherits its cone's servers, and a role's `tools:` or `agent --tools` list narrows them. A list with no `mcp__` entry gets no MCP servers. A list with `mcp__docs__search` or `mcp__docs__*` keeps only those, and `codemode` is added so codemode tools stay reachable. The resource tools must be listed by name, and they reach only the servers the list names. A nested scoop without a list inherits its calling scoop's list. A `beforeTool` hook blocks the model's calls outside the list, and codemode scripts only get the tools on it. When kernel users give each cone its own home, each cone reads `~/.pi/agent/mcp.json` in that home, and scoops keep inheriting from their cone.
291
291
 
292
- **Secrets.** `${NAME}` placeholders are filled from the agent's encrypted credential store, under `mcp:<server>|<hash of the URL>`, when the worker connects. A server whose secret is missing comes up as needing one, with a lick. Pointing a server at another URL leaves it without its secret. Header values never reach SQLite, the transcript, licks or `~/.pi/agent/mcp.log`, and stored secrets are scrubbed from error texts and server log lines. **Settings › MCP servers** lists each server whose headers use a placeholder, under its own heading below Accounts, with the URL host. Connect takes the token, which fills every placeholder of a server that uses one name; a server whose headers use several different names can't be filled from Settings yet. The server then reconnects. A server that answers 401 shows as **Rejected**, with Reconnect, and Disconnect deletes the token. Tool cards and remote summaries label MCP calls `server · tool`, unless the name is ambiguous (a server name that starts or ends with `_`, or a name with more than two `__` separators); then they show the raw name.
292
+ **Secrets.** `${NAME}` placeholders are filled from the agent's encrypted credential store, under `mcp:<server>|<hash of the URL>`, when the worker connects. A server whose secret is missing comes up as needing one, with a lick. Pointing a server at another URL leaves it without its secret. Header values never reach SQLite, the transcript, licks or `~/.pi/agent/mcp.log`, and stored secrets are scrubbed from error texts and server log lines. **OAuth.** A server without an `Authorization` header that answers 401 needs a sign-in, as in pi. Its row in Settings › MCP servers offers Connect, which opens a pop-up on the authorization server, found through the server's protected-resource metadata (RFC 9728) or `oauth.authServerMetadataUrl`. SLICC registers itself dynamically as a public client named `SLICC` (`oauth.clientName` changes it), or uses `oauth.clientId`, with PKCE S256 and the redirect URI `https://<origin>/oauth/mcp-callback`. slicc-bios serves that path from `dist/oauth/mcp-callback.html` in this package. The callback page hands the code back over the `slicc-mcp-oauth` BroadcastChannel and clears it from its address. The worker exchanges the code, checking `state` and `iss`, and keeps the tokens with the client and discovery state in the encrypted store under the server's name and URL. Tokens are refreshed when the server answers 401. When the refresh fails, the row asks for a sign-in again. Disconnect deletes the whole grant. `oauth.scope` sets the scopes, else the server's advertised ones are used. `clientSecret`, `callbackPort`, `callbackUrl` and `clientRegistration` are refused, because SLICC signs in as a public client through its own page.
293
+
294
+ **Settings › MCP servers** lists each server whose headers use a placeholder, under its own heading below Accounts, with the URL host. Connect takes the token, which fills every placeholder of a server that uses one name; a server whose headers use several different names can't be filled from Settings yet. The server then reconnects. A server that answers 401 shows as **Rejected**, with Reconnect, and Disconnect deletes the token. Tool cards and remote summaries label MCP calls `server · tool`, unless the name is ambiguous (a server name that starts or ends with `_`, or a name with more than two `__` separators); then they show the raw name.
293
295
 
294
296
  **Tools.** Tools are named `mcp__<server>__<tool>` (pi's names: everything but letters, digits and `_` becomes `_`, cut to 64 characters with a hash suffix on a collision). Their exposure follows pi:
295
297
  - `codemode` (the default): callable from codemode scripts as `tools.mcp__docs__search(args)`, which resolves to the whole `CallToolResult`, but not declared to the model. The `mcp_servers` section lists these servers with their description or the first line of the server's instructions. It also says that MCP results are data from the server, not instructions.
@@ -455,7 +457,7 @@ The built-in `sprinkles` skill teaches the files, the bridge and the command.
455
457
 
456
458
  ## The kernel
457
459
 
458
- `@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.47.1 <2`, the first kernel with users and the H1 wasm commands; until 3.31.0 the floor was 1.26.3): the agent only calls `attachKernel`, the client end of the page kernel's port, so it must match the kernel the page runs. Seven chooses that version: slicc-bios pins the certified kernel next to the agent in `src/packages/agent/package.json`, and pnpm fills the peer from that pin. The devDependency is the lowest certified kernel the range allows (1.47.1), and the unit and integration tests run on it with the H1 wasm commands as devDependencies. A second CI job reruns the integration tests on the newest certified kernel (`NEWEST_CERTIFIED_KERNEL` in `.github/workflows/ci.yml`), with the wasm commands certified for it (`NEWEST_CERTIFIED_WASM`). Today the floor is 1.47.1 and the newest certified kernel 1.54.9; the second job moves by hand to each newer certified kernel, so both ends of the range are tested. If the port protocol ever breaks within 1.x, a patch release narrows the upper bound. Renovate opens PRs for the kernel, `wasi-pnpm`, the `wasm-*` packages and `slicc-cdp` but never merges them, because they move only after the homescoop coordinator certifies them.
460
+ `@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.47.1 <2`, the first kernel with users and the H1 wasm commands; until 3.31.0 the floor was 1.26.3): the agent only calls `attachKernel`, the client end of the page kernel's port, so it must match the kernel the page runs. Seven chooses that version: slicc-bios pins the certified kernel next to the agent in `src/packages/agent/package.json`, and pnpm fills the peer from that pin. The devDependency is the lowest certified kernel the range allows (1.47.1), and the unit and integration tests run on it with the H1 wasm commands as devDependencies. A second CI job reruns the integration tests on the newest certified kernel (`NEWEST_CERTIFIED_KERNEL` in `.github/workflows/ci.yml`), with the wasm commands certified for it (`NEWEST_CERTIFIED_WASM`). Today the floor is 1.47.1 and the newest certified kernel 1.56.0; the second job moves by hand to each newer certified kernel, so both ends of the range are tested. If the port protocol ever breaks within 1.x, a patch release narrows the upper bound. Renovate opens PRs for the kernel, `wasi-pnpm`, the `wasm-*` packages and `slicc-cdp` but never merges them, because they move only after the homescoop coordinator certifies them.
459
461
 
460
462
  ## Patched dependencies
461
463
 
package/dist/main.js CHANGED
@@ -78,6 +78,7 @@ async function codemodeTools(client, options, native, extra) {
78
78
  codemodeTool({ declared: CodingTools.tools, sandbox, extra })
79
79
  ];
80
80
  }
81
+ const MCP_CALLBACK = "/oauth/mcp-callback";
81
82
  async function start(scope, ports, options) {
82
83
  const { client, users } = await attached(ports, options);
83
84
  const home = new SliccKernelEnv(client, { cwd: HOME });
@@ -148,7 +149,8 @@ async function start(scope, ports, options) {
148
149
  version: facts.version,
149
150
  reloadMs: RELOAD_MS,
150
151
  codemode: codemode.length > 0,
151
- cors: client.transport.traits?.crossOrigin === "cors"
152
+ cors: client.transport.traits?.crossOrigin === "cors",
153
+ redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href
152
154
  },
153
155
  BACKGROUND_CONTEXT
154
156
  );
@@ -246,6 +248,7 @@ function runAgentWorker(scope, options = {}) {
246
248
  return host;
247
249
  }
248
250
  export {
251
+ MCP_CALLBACK,
249
252
  STORE_DIR,
250
253
  alive,
251
254
  runAgentWorker,
package/dist/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, openAgent } from './agent.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n return { client, users: usersOf(admin, options.users, attach) };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra']\n) {\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const { client, users } = await attached(ports, options);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n browser: await browserConnected(client),\n };\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders();\n const models = createSliccModels(credentials, providers);\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n registry.install(\n imageReads(\n (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration\n )\n );\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n });\n const agent = await openAgent({\n models,\n model,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n opened = agent;\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n home: HOME,\n credentials,\n licks: licks.licks,\n fetch: scope.fetch,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: client.transport.traits?.crossOrigin === 'cors',\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n home: HOME,\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME)),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n sprinkle: sprinkles.command,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsCommand({ agents: agent.agents, settings, catalog: models }),\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const host = start(scope, kernelPorts(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, openAgent } from './agent.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n mcpRedirect?: string;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n return { client, users: usersOf(admin, options.users, attach) };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra']\n) {\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nexport const MCP_CALLBACK = '/oauth/mcp-callback';\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const { client, users } = await attached(ports, options);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n browser: await browserConnected(client),\n };\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders();\n const models = createSliccModels(credentials, providers);\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n registry.install(\n imageReads(\n (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration\n )\n );\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n });\n const agent = await openAgent({\n models,\n model,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n opened = agent;\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n home: HOME,\n credentials,\n licks: licks.licks,\n fetch: scope.fetch,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: client.transport.traits?.crossOrigin === 'cors',\n redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href,\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n home: HOME,\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME)),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n sprinkle: sprinkles.command,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsCommand({ agents: agent.agents, settings, catalog: models }),\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const host = start(scope, kernelPorts(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,79 @@
1
+ import { credentialKey, secretNames } from "./credentials.js";
2
+ import { MCP_ACCOUNT } from "./label.js";
3
+ import { beginSignIn, finishSignIn } from "./oauth.js";
4
+ function kindOf(connection) {
5
+ return secretNames(connection.entry.config).length ? "token" : "oauth";
6
+ }
7
+ function row(connection, kind, stored) {
8
+ const rejected = connection.state === "needs-auth";
9
+ return {
10
+ id: `${MCP_ACCOUNT}${connection.name}`,
11
+ provider: connection.name,
12
+ identity: new URL(connection.entry.config.url).host,
13
+ status: rejected && (kind === "token" || stored) ? "expired" : stored && connection.live ? "connected" : "disconnected",
14
+ auth: kind === "token" ? "api-key" : "oauth",
15
+ needs: null,
16
+ group: "mcp",
17
+ ...kind === "token" ? { secret: "token" } : {}
18
+ };
19
+ }
20
+ function mcpAccounts(catalog, sources, restart, listeners) {
21
+ const { credentials } = sources;
22
+ const find = (id, want) => {
23
+ const connection = id.startsWith(MCP_ACCOUNT) ? catalog.connections.get(id.slice(MCP_ACCOUNT.length)) : void 0;
24
+ const kind = connection && kindOf(connection);
25
+ if (!connection || want && kind !== want)
26
+ throw new Error(
27
+ `${id} is not an MCP server that ${want === "oauth" ? "signs in" : "takes a token or a sign-in"}`
28
+ );
29
+ return { connection, kind };
30
+ };
31
+ const keyOf = (connection) => credentialKey(connection.name, connection.entry.config.url);
32
+ return {
33
+ async accounts() {
34
+ const rows = [];
35
+ for (const connection of catalog.connections.values()) {
36
+ const kind = kindOf(connection);
37
+ const stored = kind === "token" ? await credentials.read(await keyOf(connection)) !== void 0 : await sources.oauth(connection.entry).signedIn();
38
+ if (kind === "oauth" && !stored && connection.state !== "needs-auth") continue;
39
+ rows.push(row(connection, kind, stored));
40
+ }
41
+ return rows;
42
+ },
43
+ async signIn(id) {
44
+ const { connection } = find(id, "oauth");
45
+ return beginSignIn(sources.oauth(connection.entry), connection.entry, {
46
+ fetch: sources.fetch
47
+ });
48
+ },
49
+ async connect(id, secret) {
50
+ const { connection, kind } = find(id);
51
+ if (kind === "oauth")
52
+ await finishSignIn(
53
+ sources.oauth(connection.entry),
54
+ connection.entry,
55
+ secret,
56
+ sources.fetch
57
+ );
58
+ else
59
+ await credentials.modify(await keyOf(connection), async () => ({
60
+ type: "api_key",
61
+ key: secret
62
+ }));
63
+ restart(connection);
64
+ },
65
+ async disconnect(id) {
66
+ const { connection } = find(id);
67
+ await credentials.delete(await keyOf(connection));
68
+ restart(connection);
69
+ },
70
+ onChange(listener) {
71
+ listeners.add(listener);
72
+ return () => listeners.delete(listener);
73
+ }
74
+ };
75
+ }
76
+ export {
77
+ mcpAccounts
78
+ };
79
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/mcp/accounts.ts"],
4
+ "sourcesContent": ["import type { CredentialStore } from '@earendil-works/pi-ai';\nimport type { McpFetch } from '@earendil-works/pi-mcp';\nimport type { AccountState } from '../services.ts';\nimport type { McpCatalog } from './catalog.ts';\nimport type { McpServerEntry } from './config.ts';\nimport type { McpServerConnection } from './connection.ts';\nimport { credentialKey, secretNames } from './credentials.ts';\nimport { MCP_ACCOUNT } from './label.ts';\nimport { beginSignIn, finishSignIn, type McpSignIn, type StoredOAuth } from './oauth.ts';\n\nexport interface McpAccounts {\n accounts(): Promise<AccountState[]>;\n signIn(id: string): Promise<McpSignIn>;\n connect(id: string, secret: string): Promise<void>;\n disconnect(id: string): Promise<void>;\n onChange(listener: () => void): () => void;\n}\n\nexport interface AccountSources {\n credentials: CredentialStore;\n oauth: (entry: McpServerEntry) => StoredOAuth;\n fetch: McpFetch;\n}\n\ntype Kind = 'token' | 'oauth';\n\nfunction kindOf(connection: McpServerConnection): Kind {\n return secretNames(connection.entry.config).length ? 'token' : 'oauth';\n}\n\nfunction row(connection: McpServerConnection, kind: Kind, stored: boolean): AccountState {\n const rejected = connection.state === 'needs-auth';\n return {\n id: `${MCP_ACCOUNT}${connection.name}`,\n provider: connection.name,\n identity: new URL(connection.entry.config.url).host,\n status:\n rejected && (kind === 'token' || stored)\n ? 'expired'\n : stored && connection.live\n ? 'connected'\n : 'disconnected',\n auth: kind === 'token' ? 'api-key' : 'oauth',\n needs: null,\n group: 'mcp',\n ...(kind === 'token' ? { secret: 'token' as const } : {}),\n };\n}\n\nexport function mcpAccounts(\n catalog: McpCatalog,\n sources: AccountSources,\n restart: (connection: McpServerConnection) => void,\n listeners: Set<() => void>\n): McpAccounts {\n const { credentials } = sources;\n const find = (id: string, want?: Kind) => {\n const connection = id.startsWith(MCP_ACCOUNT)\n ? catalog.connections.get(id.slice(MCP_ACCOUNT.length))\n : undefined;\n const kind = connection && kindOf(connection);\n if (!connection || (want && kind !== want))\n throw new Error(\n `${id} is not an MCP server that ${want === 'oauth' ? 'signs in' : 'takes a token or a sign-in'}`\n );\n return { connection, kind: kind as Kind };\n };\n const keyOf = (connection: McpServerConnection) =>\n credentialKey(connection.name, connection.entry.config.url);\n return {\n async accounts() {\n const rows: AccountState[] = [];\n for (const connection of catalog.connections.values()) {\n const kind = kindOf(connection);\n const stored =\n kind === 'token'\n ? (await credentials.read(await keyOf(connection))) !== undefined\n : await sources.oauth(connection.entry).signedIn();\n if (kind === 'oauth' && !stored && connection.state !== 'needs-auth') continue;\n rows.push(row(connection, kind, stored));\n }\n return rows;\n },\n async signIn(id) {\n const { connection } = find(id, 'oauth');\n return beginSignIn(sources.oauth(connection.entry), connection.entry, {\n fetch: sources.fetch,\n });\n },\n async connect(id, secret) {\n const { connection, kind } = find(id);\n if (kind === 'oauth')\n await finishSignIn(\n sources.oauth(connection.entry),\n connection.entry,\n secret,\n sources.fetch\n );\n else\n await credentials.modify(await keyOf(connection), async () => ({\n type: 'api_key',\n key: secret,\n }));\n restart(connection);\n },\n async disconnect(id) {\n const { connection } = find(id);\n await credentials.delete(await keyOf(connection));\n restart(connection);\n },\n onChange(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],
5
+ "mappings": "AAMA,SAAS,eAAe,mBAAmB;AAC3C,SAAS,mBAAmB;AAC5B,SAAS,aAAa,oBAAsD;AAkB5E,SAAS,OAAO,YAAuC;AACrD,SAAO,YAAY,WAAW,MAAM,MAAM,EAAE,SAAS,UAAU;AACjE;AAEA,SAAS,IAAI,YAAiC,MAAY,QAA+B;AACvF,QAAM,WAAW,WAAW,UAAU;AACtC,SAAO;AAAA,IACL,IAAI,GAAG,WAAW,GAAG,WAAW,IAAI;AAAA,IACpC,UAAU,WAAW;AAAA,IACrB,UAAU,IAAI,IAAI,WAAW,MAAM,OAAO,GAAG,EAAE;AAAA,IAC/C,QACE,aAAa,SAAS,WAAW,UAC7B,YACA,UAAU,WAAW,OACnB,cACA;AAAA,IACR,MAAM,SAAS,UAAU,YAAY;AAAA,IACrC,OAAO;AAAA,IACP,OAAO;AAAA,IACP,GAAI,SAAS,UAAU,EAAE,QAAQ,QAAiB,IAAI,CAAC;AAAA,EACzD;AACF;AAEO,SAAS,YACd,SACA,SACA,SACA,WACa;AACb,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,OAAO,CAAC,IAAY,SAAgB;AACxC,UAAM,aAAa,GAAG,WAAW,WAAW,IACxC,QAAQ,YAAY,IAAI,GAAG,MAAM,YAAY,MAAM,CAAC,IACpD;AACJ,UAAM,OAAO,cAAc,OAAO,UAAU;AAC5C,QAAI,CAAC,cAAe,QAAQ,SAAS;AACnC,YAAM,IAAI;AAAA,QACR,GAAG,EAAE,8BAA8B,SAAS,UAAU,aAAa,4BAA4B;AAAA,MACjG;AACF,WAAO,EAAE,YAAY,KAAmB;AAAA,EAC1C;AACA,QAAM,QAAQ,CAAC,eACb,cAAc,WAAW,MAAM,WAAW,MAAM,OAAO,GAAG;AAC5D,SAAO;AAAA,IACL,MAAM,WAAW;AACf,YAAM,OAAuB,CAAC;AAC9B,iBAAW,cAAc,QAAQ,YAAY,OAAO,GAAG;AACrD,cAAM,OAAO,OAAO,UAAU;AAC9B,cAAM,SACJ,SAAS,UACJ,MAAM,YAAY,KAAK,MAAM,MAAM,UAAU,CAAC,MAAO,SACtD,MAAM,QAAQ,MAAM,WAAW,KAAK,EAAE,SAAS;AACrD,YAAI,SAAS,WAAW,CAAC,UAAU,WAAW,UAAU,aAAc;AACtE,aAAK,KAAK,IAAI,YAAY,MAAM,MAAM,CAAC;AAAA,MACzC;AACA,aAAO;AAAA,IACT;AAAA,IACA,MAAM,OAAO,IAAI;AACf,YAAM,EAAE,WAAW,IAAI,KAAK,IAAI,OAAO;AACvC,aAAO,YAAY,QAAQ,MAAM,WAAW,KAAK,GAAG,WAAW,OAAO;AAAA,QACpE,OAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,QAAQ,IAAI,QAAQ;AACxB,YAAM,EAAE,YAAY,KAAK,IAAI,KAAK,EAAE;AACpC,UAAI,SAAS;AACX,cAAM;AAAA,UACJ,QAAQ,MAAM,WAAW,KAAK;AAAA,UAC9B,WAAW;AAAA,UACX;AAAA,UACA,QAAQ;AAAA,QACV;AAAA;AAEA,cAAM,YAAY,OAAO,MAAM,MAAM,UAAU,GAAG,aAAa;AAAA,UAC7D,MAAM;AAAA,UACN,KAAK;AAAA,QACP,EAAE;AACJ,cAAQ,UAAU;AAAA,IACpB;AAAA,IACA,MAAM,WAAW,IAAI;AACnB,YAAM,EAAE,WAAW,IAAI,KAAK,EAAE;AAC9B,YAAM,YAAY,OAAO,MAAM,MAAM,UAAU,CAAC;AAChD,cAAQ,UAAU;AAAA,IACpB;AAAA,IACA,SAAS,UAAU;AACjB,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM,UAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -10,8 +10,16 @@ const ACCEPTED = /* @__PURE__ */ new Set([
10
10
  "toolExposure",
11
11
  "enabled",
12
12
  "timeout",
13
- "description"
13
+ "description",
14
+ "oauth"
14
15
  ]);
16
+ const OAUTH_STRINGS = ["clientId", "clientName", "scope", "authServerMetadataUrl"];
17
+ const OAUTH_REFUSED = {
18
+ clientSecret: "a client secret would have to live in mcp.json; SLICC signs in as a public client",
19
+ callbackPort: "SLICC signs in through its own page, not a loopback port",
20
+ callbackUrl: "SLICC signs in through its own page, not a loopback URL",
21
+ clientRegistration: "SLICC registers dynamically or uses oauth.clientId"
22
+ };
15
23
  const SECRET_HEADER = /authorization|cookie|token|key|secret|password|session/i;
16
24
  const SECRET_PARAM = /token|key|secret|password|signature|sig|auth|code/i;
17
25
  const PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -62,6 +70,34 @@ function checkHeaders(name, headers) {
62
70
  }
63
71
  return void 0;
64
72
  }
73
+ function checkOAuth(name, value) {
74
+ const { oauth } = value;
75
+ if (oauth === void 0) return void 0;
76
+ if (!isRecord(oauth)) return fail(name, "oauth must be an object", "oauth");
77
+ if (Object.keys(value.headers ?? {}).some((header) => header.toLowerCase() === "authorization"))
78
+ return fail(name, "oauth and an Authorization header exclude each other", "oauth");
79
+ for (const [key, entry] of Object.entries(oauth)) {
80
+ const refused = OAUTH_REFUSED[key];
81
+ if (refused) return fail(name, `oauth.${key} is not supported: ${refused}`, `oauth.${key}`);
82
+ if (!OAUTH_STRINGS.includes(key))
83
+ return fail(name, `oauth.${key} is not a known field`, `oauth.${key}`);
84
+ if (typeof entry !== "string" || !entry.trim())
85
+ return fail(name, `oauth.${key} must be a non-empty string`, `oauth.${key}`);
86
+ }
87
+ const metadata = oauth.authServerMetadataUrl;
88
+ if (metadata !== void 0 && (!URL.canParse(metadata) || new URL(metadata).protocol !== "https:"))
89
+ return fail(
90
+ name,
91
+ "oauth.authServerMetadataUrl must be an https URL",
92
+ "oauth.authServerMetadataUrl"
93
+ );
94
+ return void 0;
95
+ }
96
+ function usesOAuth(config) {
97
+ return !Object.keys(config.headers ?? {}).some(
98
+ (header) => header.toLowerCase() === "authorization"
99
+ );
100
+ }
65
101
  function checkExposures(name, value) {
66
102
  const allowed = EXPOSURES.map((item) => `"${item}"`).join(", ");
67
103
  if (value.exposure !== void 0 && !isExposure(value.exposure))
@@ -116,10 +152,8 @@ function validateServer(name, raw) {
116
152
  value.toolExposure = Object.fromEntries(
117
153
  Object.entries(raw.toolExposure).map(([tool, exposure]) => [tool, alias(exposure)])
118
154
  );
119
- for (const key of Object.keys(value))
120
- if (key === "oauth" || key === "auth")
121
- return fail(name, `${key} is not supported in SLICC yet`, key);
122
- const problem = checkTransport(name, value) ?? checkHeaders(name, value.headers) ?? checkExposures(name, value) ?? checkFields(name, value);
155
+ if (value.auth !== void 0) return fail(name, "auth is not supported in SLICC yet", "auth");
156
+ const problem = checkTransport(name, value) ?? checkHeaders(name, value.headers) ?? checkOAuth(name, value) ?? checkExposures(name, value) ?? checkFields(name, value);
123
157
  if (problem) return problem;
124
158
  const config = {};
125
159
  for (const [key, entry] of Object.entries(value))
@@ -196,6 +230,7 @@ export {
196
230
  placeholders,
197
231
  sameConfig,
198
232
  toolExposure,
233
+ usesOAuth,
199
234
  validateServer
200
235
  };
201
236
  //# sourceMappingURL=config.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/config.ts"],
4
- "sourcesContent": ["import { hash } from '../licks/sources.ts';\n\nexport type McpExposure = 'codemode' | 'deferred' | 'direct' | 'hidden';\n\nexport interface McpServerConfig {\n url: string;\n headers?: Record<string, string>;\n exposure?: McpExposure;\n toolExposure?: Record<string, McpExposure>;\n enabled?: boolean;\n timeout?: number;\n description?: string;\n}\n\nexport interface McpServerEntry {\n name: string;\n config: McpServerConfig;\n}\n\nexport interface McpConfigProblem {\n server?: string;\n field?: string;\n message: string;\n}\n\nexport interface McpConfig {\n servers: McpServerEntry[];\n problems: McpConfigProblem[];\n}\n\nconst SERVER_NAME = /^[A-Za-z0-9_-]+$/;\nconst EXPOSURES: readonly string[] = ['codemode', 'deferred', 'direct', 'hidden'];\nconst ALIASES: Record<string, McpExposure> = { 'codemode-deferred': 'codemode' };\nconst ACCEPTED = new Set([\n 'type',\n 'url',\n 'headers',\n 'exposure',\n 'toolExposure',\n 'enabled',\n 'timeout',\n 'description',\n]);\nconst SECRET_HEADER = /authorization|cookie|token|key|secret|password|session/i;\nconst SECRET_PARAM = /token|key|secret|password|signature|sig|auth|code/i;\nconst PLACEHOLDER = /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\nconst SCHEME = /^\\s*(?:bearer|basic|token|apikey|api-key)\\s+/i;\nconst MAX_TOOL_NAME = 64;\nexport const DEFAULT_TIMEOUT_SECONDS = 60;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction alias(value: unknown): unknown {\n return typeof value === 'string' ? (ALIASES[value] ?? value) : value;\n}\n\nfunction isExposure(value: unknown): value is McpExposure {\n return typeof value === 'string' && EXPOSURES.includes(value);\n}\n\nexport function placeholders(value: string): string[] {\n return [...value.matchAll(PLACEHOLDER)].map((match) => match[1] as string);\n}\n\nexport function literalSecret(header: string, value: string): boolean {\n if (!SECRET_HEADER.test(header)) return false;\n return value.replace(PLACEHOLDER, '').replace(SCHEME, '').trim() !== '';\n}\n\nfunction urlSecret(url: URL): string | undefined {\n if (url.username || url.password) return 'url';\n for (const name of url.searchParams.keys()) if (SECRET_PARAM.test(name)) return `url ?${name}`;\n return undefined;\n}\n\ntype Checked = McpServerConfig | McpConfigProblem;\n\nfunction fail(server: string, message: string, field?: string): McpConfigProblem {\n return { server, ...(field ? { field } : {}), message };\n}\n\nfunction checkHeaders(name: string, headers: unknown): McpConfigProblem | undefined {\n if (headers === undefined) return undefined;\n if (!isRecord(headers) || !Object.values(headers).every((value) => typeof value === 'string'))\n return fail(name, 'headers must map names to strings', 'headers');\n for (const [header, value] of Object.entries(headers as Record<string, string>)) {\n if (value.trim().startsWith('!'))\n return fail(\n name,\n `headers.${header} runs a command, which SLICC can't do`,\n `headers.${header}`\n );\n if (literalSecret(header, value))\n return fail(\n name,\n `headers.${header} holds a literal secret. mcp.json is readable by every agent: write \\${NAME} instead, and add the value in Settings \u203A MCP servers`,\n `headers.${header}`\n );\n }\n return undefined;\n}\n\nfunction checkExposures(\n name: string,\n value: Record<string, unknown>\n): McpConfigProblem | undefined {\n const allowed = EXPOSURES.map((item) => `\"${item}\"`).join(', ');\n if (value.exposure !== undefined && !isExposure(value.exposure))\n return fail(name, `exposure must be one of ${allowed}`, 'exposure');\n if (value.toolExposure === undefined) return undefined;\n if (!isRecord(value.toolExposure))\n return fail(name, 'toolExposure must map tool names to exposures', 'toolExposure');\n for (const [tool, exposure] of Object.entries(value.toolExposure))\n if (!isExposure(exposure))\n return fail(name, `toolExposure \"${tool}\" must be one of ${allowed}`, `toolExposure.${tool}`);\n return undefined;\n}\n\nfunction checkFields(name: string, value: Record<string, unknown>): McpConfigProblem | undefined {\n if (value.enabled !== undefined && typeof value.enabled !== 'boolean')\n return fail(name, 'enabled must be a boolean', 'enabled');\n if (value.description !== undefined && typeof value.description !== 'string')\n return fail(name, 'description must be a string', 'description');\n if (value.timeout !== undefined && (typeof value.timeout !== 'number' || !(value.timeout > 0)))\n return fail(name, 'timeout must be a positive number of seconds', 'timeout');\n return undefined;\n}\n\nfunction checkTransport(\n name: string,\n value: Record<string, unknown>\n): McpConfigProblem | undefined {\n if (value.command !== undefined || value.type === 'stdio')\n return fail(name, 'stdio servers need slicc-kernel#68, so this one is skipped', 'command');\n if (value.type === 'sse')\n return fail(\n name,\n 'the legacy SSE transport is not supported; use the streamable HTTP URL',\n 'type'\n );\n if (value.type !== undefined && value.type !== 'http' && value.type !== 'streamable-http')\n return fail(name, 'type must be \"http\" or \"streamable-http\"', 'type');\n if (typeof value.url !== 'string') return fail(name, 'needs a \"url\" (streamable HTTP)', 'url');\n if (!URL.canParse(value.url) || !/^https?:$/.test(new URL(value.url).protocol))\n return fail(name, 'url must be an http or https URL', 'url');\n const secret = urlSecret(new URL(value.url));\n if (secret)\n return fail(\n name,\n `${secret} holds a literal secret. Put it in a header as \\${NAME} and add the value in Settings \u203A MCP servers`,\n 'url'\n );\n return undefined;\n}\n\nexport function validateServer(name: string, raw: unknown): Checked {\n if (!SERVER_NAME.test(name))\n return fail(name, `invalid server name \"${name}\" (use letters, digits, \"_\" and \"-\")`);\n if (!isRecord(raw)) return fail(name, 'must be an object');\n const value: Record<string, unknown> = { ...raw, exposure: alias(raw.exposure) };\n if (raw.exposure === undefined) delete value.exposure;\n if (isRecord(raw.toolExposure))\n value.toolExposure = Object.fromEntries(\n Object.entries(raw.toolExposure).map(([tool, exposure]) => [tool, alias(exposure)])\n );\n for (const key of Object.keys(value))\n if (key === 'oauth' || key === 'auth')\n return fail(name, `${key} is not supported in SLICC yet`, key);\n const problem =\n checkTransport(name, value) ??\n checkHeaders(name, value.headers) ??\n checkExposures(name, value) ??\n checkFields(name, value);\n if (problem) return problem;\n const config: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value))\n if (ACCEPTED.has(key) && key !== 'type') config[key] = entry;\n return config as unknown as McpServerConfig;\n}\n\nfunction canonical(name: string): string {\n return name.replace(/-/g, '_');\n}\n\nexport function parseMcpConfig(text: string | undefined): McpConfig {\n if (text === undefined || text.trim() === '') return { servers: [], problems: [] };\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n return { servers: [], problems: [{ message: `not valid JSON: ${(error as Error).message}` }] };\n }\n if (!isRecord(parsed) || (parsed.mcpServers !== undefined && !isRecord(parsed.mcpServers)))\n return { servers: [], problems: [{ message: 'mcpServers must be an object' }] };\n const servers: McpServerEntry[] = [];\n const problems: McpConfigProblem[] = [];\n const seen = new Set<string>();\n for (const [name, raw] of Object.entries((parsed.mcpServers ?? {}) as Record<string, unknown>)) {\n const checked = validateServer(name, raw);\n if ('message' in checked) {\n problems.push(checked);\n continue;\n }\n if (seen.has(canonical(name))) {\n problems.push(fail(name, 'names that differ only in \"-\" and \"_\" count as the same server'));\n continue;\n }\n seen.add(canonical(name));\n servers.push({ name, config: checked });\n }\n return { servers, problems };\n}\n\nfunction patternRegExp(pattern: string): RegExp {\n const source = pattern\n .split('*')\n .map((part) => part.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*');\n return new RegExp(`^${source}$`);\n}\n\nexport function nameMatcher(entries: readonly string[]): (name: string) => boolean {\n const names = new Set(entries.filter((entry) => !entry.includes('*')));\n const patterns = entries.filter((entry) => entry.includes('*')).map(patternRegExp);\n return (name) => names.has(name) || patterns.some((pattern) => pattern.test(name));\n}\n\nexport function toolExposure(config: McpServerConfig, tool: string): McpExposure {\n const overrides = config.toolExposure ?? {};\n const exact = overrides[tool];\n if (exact !== undefined) return exact;\n for (const [pattern, exposure] of Object.entries(overrides))\n if (pattern.includes('*') && patternRegExp(pattern).test(tool)) return exposure;\n return config.exposure ?? 'codemode';\n}\n\nexport function mcpNamespace(server: string): string {\n return `mcp__${canonical(server)}`;\n}\n\nexport function mcpToolName(\n server: string,\n tool: string,\n taken: (name: string) => boolean = () => false\n): string {\n const name = `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, '_');\n if (name.length <= MAX_TOOL_NAME && !taken(name)) return name;\n const suffix = hash(`${server}\\0${tool}`).padStart(7, '0').slice(0, 7);\n return `${name.slice(0, MAX_TOOL_NAME - suffix.length - 1)}_${suffix}`;\n}\n\nexport function sameConfig(a: McpServerConfig, b: McpServerConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { hash } from '../licks/sources.ts';\n\nexport type McpExposure = 'codemode' | 'deferred' | 'direct' | 'hidden';\n\nexport interface McpServerConfig {\n url: string;\n headers?: Record<string, string>;\n exposure?: McpExposure;\n toolExposure?: Record<string, McpExposure>;\n enabled?: boolean;\n timeout?: number;\n description?: string;\n oauth?: McpOAuthConfig;\n}\n\nexport interface McpOAuthConfig {\n clientId?: string;\n clientName?: string;\n scope?: string;\n authServerMetadataUrl?: string;\n}\n\nexport interface McpServerEntry {\n name: string;\n config: McpServerConfig;\n}\n\nexport interface McpConfigProblem {\n server?: string;\n field?: string;\n message: string;\n}\n\nexport interface McpConfig {\n servers: McpServerEntry[];\n problems: McpConfigProblem[];\n}\n\nconst SERVER_NAME = /^[A-Za-z0-9_-]+$/;\nconst EXPOSURES: readonly string[] = ['codemode', 'deferred', 'direct', 'hidden'];\nconst ALIASES: Record<string, McpExposure> = { 'codemode-deferred': 'codemode' };\nconst ACCEPTED = new Set([\n 'type',\n 'url',\n 'headers',\n 'exposure',\n 'toolExposure',\n 'enabled',\n 'timeout',\n 'description',\n 'oauth',\n]);\nconst OAUTH_STRINGS = ['clientId', 'clientName', 'scope', 'authServerMetadataUrl'];\nconst OAUTH_REFUSED: Record<string, string> = {\n clientSecret: 'a client secret would have to live in mcp.json; SLICC signs in as a public client',\n callbackPort: 'SLICC signs in through its own page, not a loopback port',\n callbackUrl: 'SLICC signs in through its own page, not a loopback URL',\n clientRegistration: 'SLICC registers dynamically or uses oauth.clientId',\n};\nconst SECRET_HEADER = /authorization|cookie|token|key|secret|password|session/i;\nconst SECRET_PARAM = /token|key|secret|password|signature|sig|auth|code/i;\nconst PLACEHOLDER = /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\nconst SCHEME = /^\\s*(?:bearer|basic|token|apikey|api-key)\\s+/i;\nconst MAX_TOOL_NAME = 64;\nexport const DEFAULT_TIMEOUT_SECONDS = 60;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction alias(value: unknown): unknown {\n return typeof value === 'string' ? (ALIASES[value] ?? value) : value;\n}\n\nfunction isExposure(value: unknown): value is McpExposure {\n return typeof value === 'string' && EXPOSURES.includes(value);\n}\n\nexport function placeholders(value: string): string[] {\n return [...value.matchAll(PLACEHOLDER)].map((match) => match[1] as string);\n}\n\nexport function literalSecret(header: string, value: string): boolean {\n if (!SECRET_HEADER.test(header)) return false;\n return value.replace(PLACEHOLDER, '').replace(SCHEME, '').trim() !== '';\n}\n\nfunction urlSecret(url: URL): string | undefined {\n if (url.username || url.password) return 'url';\n for (const name of url.searchParams.keys()) if (SECRET_PARAM.test(name)) return `url ?${name}`;\n return undefined;\n}\n\ntype Checked = McpServerConfig | McpConfigProblem;\n\nfunction fail(server: string, message: string, field?: string): McpConfigProblem {\n return { server, ...(field ? { field } : {}), message };\n}\n\nfunction checkHeaders(name: string, headers: unknown): McpConfigProblem | undefined {\n if (headers === undefined) return undefined;\n if (!isRecord(headers) || !Object.values(headers).every((value) => typeof value === 'string'))\n return fail(name, 'headers must map names to strings', 'headers');\n for (const [header, value] of Object.entries(headers as Record<string, string>)) {\n if (value.trim().startsWith('!'))\n return fail(\n name,\n `headers.${header} runs a command, which SLICC can't do`,\n `headers.${header}`\n );\n if (literalSecret(header, value))\n return fail(\n name,\n `headers.${header} holds a literal secret. mcp.json is readable by every agent: write \\${NAME} instead, and add the value in Settings \u203A MCP servers`,\n `headers.${header}`\n );\n }\n return undefined;\n}\n\nfunction checkOAuth(name: string, value: Record<string, unknown>): McpConfigProblem | undefined {\n const { oauth } = value;\n if (oauth === undefined) return undefined;\n if (!isRecord(oauth)) return fail(name, 'oauth must be an object', 'oauth');\n if (Object.keys(value.headers ?? {}).some((header) => header.toLowerCase() === 'authorization'))\n return fail(name, 'oauth and an Authorization header exclude each other', 'oauth');\n for (const [key, entry] of Object.entries(oauth)) {\n const refused = OAUTH_REFUSED[key];\n if (refused) return fail(name, `oauth.${key} is not supported: ${refused}`, `oauth.${key}`);\n if (!OAUTH_STRINGS.includes(key))\n return fail(name, `oauth.${key} is not a known field`, `oauth.${key}`);\n if (typeof entry !== 'string' || !entry.trim())\n return fail(name, `oauth.${key} must be a non-empty string`, `oauth.${key}`);\n }\n const metadata = oauth.authServerMetadataUrl as string | undefined;\n if (\n metadata !== undefined &&\n (!URL.canParse(metadata) || new URL(metadata).protocol !== 'https:')\n )\n return fail(\n name,\n 'oauth.authServerMetadataUrl must be an https URL',\n 'oauth.authServerMetadataUrl'\n );\n return undefined;\n}\n\nexport function usesOAuth(config: McpServerConfig): boolean {\n return !Object.keys(config.headers ?? {}).some(\n (header) => header.toLowerCase() === 'authorization'\n );\n}\n\nfunction checkExposures(\n name: string,\n value: Record<string, unknown>\n): McpConfigProblem | undefined {\n const allowed = EXPOSURES.map((item) => `\"${item}\"`).join(', ');\n if (value.exposure !== undefined && !isExposure(value.exposure))\n return fail(name, `exposure must be one of ${allowed}`, 'exposure');\n if (value.toolExposure === undefined) return undefined;\n if (!isRecord(value.toolExposure))\n return fail(name, 'toolExposure must map tool names to exposures', 'toolExposure');\n for (const [tool, exposure] of Object.entries(value.toolExposure))\n if (!isExposure(exposure))\n return fail(name, `toolExposure \"${tool}\" must be one of ${allowed}`, `toolExposure.${tool}`);\n return undefined;\n}\n\nfunction checkFields(name: string, value: Record<string, unknown>): McpConfigProblem | undefined {\n if (value.enabled !== undefined && typeof value.enabled !== 'boolean')\n return fail(name, 'enabled must be a boolean', 'enabled');\n if (value.description !== undefined && typeof value.description !== 'string')\n return fail(name, 'description must be a string', 'description');\n if (value.timeout !== undefined && (typeof value.timeout !== 'number' || !(value.timeout > 0)))\n return fail(name, 'timeout must be a positive number of seconds', 'timeout');\n return undefined;\n}\n\nfunction checkTransport(\n name: string,\n value: Record<string, unknown>\n): McpConfigProblem | undefined {\n if (value.command !== undefined || value.type === 'stdio')\n return fail(name, 'stdio servers need slicc-kernel#68, so this one is skipped', 'command');\n if (value.type === 'sse')\n return fail(\n name,\n 'the legacy SSE transport is not supported; use the streamable HTTP URL',\n 'type'\n );\n if (value.type !== undefined && value.type !== 'http' && value.type !== 'streamable-http')\n return fail(name, 'type must be \"http\" or \"streamable-http\"', 'type');\n if (typeof value.url !== 'string') return fail(name, 'needs a \"url\" (streamable HTTP)', 'url');\n if (!URL.canParse(value.url) || !/^https?:$/.test(new URL(value.url).protocol))\n return fail(name, 'url must be an http or https URL', 'url');\n const secret = urlSecret(new URL(value.url));\n if (secret)\n return fail(\n name,\n `${secret} holds a literal secret. Put it in a header as \\${NAME} and add the value in Settings \u203A MCP servers`,\n 'url'\n );\n return undefined;\n}\n\nexport function validateServer(name: string, raw: unknown): Checked {\n if (!SERVER_NAME.test(name))\n return fail(name, `invalid server name \"${name}\" (use letters, digits, \"_\" and \"-\")`);\n if (!isRecord(raw)) return fail(name, 'must be an object');\n const value: Record<string, unknown> = { ...raw, exposure: alias(raw.exposure) };\n if (raw.exposure === undefined) delete value.exposure;\n if (isRecord(raw.toolExposure))\n value.toolExposure = Object.fromEntries(\n Object.entries(raw.toolExposure).map(([tool, exposure]) => [tool, alias(exposure)])\n );\n if (value.auth !== undefined) return fail(name, 'auth is not supported in SLICC yet', 'auth');\n const problem =\n checkTransport(name, value) ??\n checkHeaders(name, value.headers) ??\n checkOAuth(name, value) ??\n checkExposures(name, value) ??\n checkFields(name, value);\n if (problem) return problem;\n const config: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value))\n if (ACCEPTED.has(key) && key !== 'type') config[key] = entry;\n return config as unknown as McpServerConfig;\n}\n\nfunction canonical(name: string): string {\n return name.replace(/-/g, '_');\n}\n\nexport function parseMcpConfig(text: string | undefined): McpConfig {\n if (text === undefined || text.trim() === '') return { servers: [], problems: [] };\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n return { servers: [], problems: [{ message: `not valid JSON: ${(error as Error).message}` }] };\n }\n if (!isRecord(parsed) || (parsed.mcpServers !== undefined && !isRecord(parsed.mcpServers)))\n return { servers: [], problems: [{ message: 'mcpServers must be an object' }] };\n const servers: McpServerEntry[] = [];\n const problems: McpConfigProblem[] = [];\n const seen = new Set<string>();\n for (const [name, raw] of Object.entries((parsed.mcpServers ?? {}) as Record<string, unknown>)) {\n const checked = validateServer(name, raw);\n if ('message' in checked) {\n problems.push(checked);\n continue;\n }\n if (seen.has(canonical(name))) {\n problems.push(fail(name, 'names that differ only in \"-\" and \"_\" count as the same server'));\n continue;\n }\n seen.add(canonical(name));\n servers.push({ name, config: checked });\n }\n return { servers, problems };\n}\n\nfunction patternRegExp(pattern: string): RegExp {\n const source = pattern\n .split('*')\n .map((part) => part.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*');\n return new RegExp(`^${source}$`);\n}\n\nexport function nameMatcher(entries: readonly string[]): (name: string) => boolean {\n const names = new Set(entries.filter((entry) => !entry.includes('*')));\n const patterns = entries.filter((entry) => entry.includes('*')).map(patternRegExp);\n return (name) => names.has(name) || patterns.some((pattern) => pattern.test(name));\n}\n\nexport function toolExposure(config: McpServerConfig, tool: string): McpExposure {\n const overrides = config.toolExposure ?? {};\n const exact = overrides[tool];\n if (exact !== undefined) return exact;\n for (const [pattern, exposure] of Object.entries(overrides))\n if (pattern.includes('*') && patternRegExp(pattern).test(tool)) return exposure;\n return config.exposure ?? 'codemode';\n}\n\nexport function mcpNamespace(server: string): string {\n return `mcp__${canonical(server)}`;\n}\n\nexport function mcpToolName(\n server: string,\n tool: string,\n taken: (name: string) => boolean = () => false\n): string {\n const name = `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, '_');\n if (name.length <= MAX_TOOL_NAME && !taken(name)) return name;\n const suffix = hash(`${server}\\0${tool}`).padStart(7, '0').slice(0, 7);\n return `${name.slice(0, MAX_TOOL_NAME - suffix.length - 1)}_${suffix}`;\n}\n\nexport function sameConfig(a: McpServerConfig, b: McpServerConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,9 +6,14 @@ import {
6
6
  McpHttpError,
7
7
  McpSessionExpiredError
8
8
  } from "@earendil-works/pi-mcp";
9
- import { DEFAULT_TIMEOUT_SECONDS } from "./config.js";
9
+ import { McpOAuthAuthorizationRequiredError } from "@earendil-works/pi-mcp/oauth";
10
+ import { DEFAULT_TIMEOUT_SECONDS, usesOAuth } from "./config.js";
11
+ import { secretNames } from "./credentials.js";
10
12
  const CONNECT_RETRY_DELAYS_MS = [250, 1e3];
11
13
  const CLIENT_NAME = "slicc";
14
+ function signInNeeded(error) {
15
+ return error instanceof McpAuthRequiredError || error instanceof McpOAuthAuthorizationRequiredError;
16
+ }
12
17
  function errorMessage(error) {
13
18
  return error instanceof Error ? error.message : String(error);
14
19
  }
@@ -127,7 +132,7 @@ class McpServerConnection {
127
132
  }
128
133
  continue;
129
134
  }
130
- if (!(error instanceof McpAuthRequiredError)) throw error;
135
+ if (!signInNeeded(error)) throw error;
131
136
  await this.#drop(client);
132
137
  this.#mark("needs-auth");
133
138
  throw new Error(this.signInMessage());
@@ -144,7 +149,7 @@ class McpServerConnection {
144
149
  void this.#drop(client);
145
150
  }
146
151
  signInMessage() {
147
- return `MCP server "${this.name}" rejected its token (401). Update it in Settings \u203A MCP servers.`;
152
+ return usesOAuth(this.entry.config) && !secretNames(this.entry.config).length ? `MCP server "${this.name}" needs you to sign in. Sign in under Settings \u203A MCP servers.` : `MCP server "${this.name}" rejected its token (401). Update it in Settings \u203A MCP servers.`;
148
153
  }
149
154
  #mark(state, error) {
150
155
  this.state = state;
@@ -240,7 +245,7 @@ class McpServerConnection {
240
245
  this.#mark("needs-secret");
241
246
  return new Error(error.message);
242
247
  }
243
- if (error instanceof McpAuthRequiredError && !this.closed) {
248
+ if (signInNeeded(error) && !this.closed) {
244
249
  this.#mark("needs-auth");
245
250
  return new Error(this.signInMessage());
246
251
  }
@@ -291,6 +296,7 @@ export {
291
296
  MissingSecret,
292
297
  errorMessage,
293
298
  isMcpApp,
294
- isTransient
299
+ isTransient,
300
+ signInNeeded
295
301
  };
296
302
  //# sourceMappingURL=connection.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/connection.ts"],
4
- "sourcesContent": ["import {\n type CallToolResult,\n JSON_RPC_ERROR_CODES,\n type ListResourcesResult,\n type ListResourceTemplatesResult,\n McpAuthRequiredError,\n McpClient,\n McpError,\n McpHttpError,\n type McpRequestOptions,\n McpSessionExpiredError,\n type Tool as McpTool,\n type McpTransport,\n type ReadResourceResult,\n type Resource,\n type ResourceTemplate,\n} from '@earendil-works/pi-mcp';\nimport { DEFAULT_TIMEOUT_SECONDS, type McpServerEntry } from './config.ts';\n\nexport type ServerState =\n | 'connecting'\n | 'connected'\n | 'needs-auth'\n | 'needs-secret'\n | 'failed'\n | 'closed';\n\nexport type TransportFactory = (\n entry: McpServerEntry\n) => Promise<{ transport: McpTransport; secrets: string[] } | { missing: string[] }>;\n\nexport const CONNECT_RETRY_DELAYS_MS = [250, 1_000];\nexport const CLIENT_NAME = 'slicc';\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function isTransient(error: unknown): boolean {\n if (error instanceof McpHttpError)\n return (\n error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501)\n );\n return error instanceof TypeError;\n}\n\nexport function isMcpApp(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n const uri = item.uri ?? item.uriTemplate ?? '';\n return uri.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? '');\n}\n\nasync function withoutTemplates<T>(list: () => Promise<T>, empty: T): Promise<T> {\n try {\n return await list();\n } catch (error) {\n if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)\n return empty;\n throw error;\n }\n}\n\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true }\n );\n });\n\nexport interface ConnectionOptions {\n entry: McpServerEntry;\n version: string;\n transport: TransportFactory;\n onTools: (connection: McpServerConnection) => void;\n onChange?: (connection: McpServerConnection) => void;\n onLog?: (connection: McpServerConnection, params: unknown) => void;\n}\n\nexport class McpServerConnection {\n readonly entry: McpServerEntry;\n state: ServerState = 'connecting';\n error: string | undefined;\n missing: string[] = [];\n tools: McpTool[] = [];\n hasResources = false;\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n instructions: string | undefined;\n secrets: string[] = [];\n #client: McpClient | undefined;\n #opening: Promise<McpClient> | undefined;\n readonly #shutdown = new AbortController();\n readonly #busy = new Map<McpClient, number>();\n #seen = false;\n readonly #expired = new Set<McpClient>();\n readonly #options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.entry = options.entry;\n this.#options = options;\n }\n\n get name(): string {\n return this.entry.name;\n }\n\n get timeoutMs(): number {\n return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n }\n\n get live(): boolean {\n return this.state === 'connected' || (this.state === 'connecting' && this.#seen);\n }\n\n get closed(): boolean {\n return this.#shutdown.signal.aborted;\n }\n\n getClient(): Promise<McpClient> {\n if (this.closed) return Promise.reject(new Error(`MCP server \"${this.name}\" is shut down`));\n if (this.#client?.connectionState === 'connected') return Promise.resolve(this.#client);\n this.#opening ??= this.#open().finally(() => {\n this.#opening = undefined;\n });\n return this.#opening;\n }\n\n callTool(\n name: string,\n args: Record<string, unknown>,\n options: McpRequestOptions\n ): Promise<CallToolResult> {\n return this.#with((client) => client.callTool(name, args, options));\n }\n\n readResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult> {\n return this.#with((client) => client.readResource(uri, options), true);\n }\n\n resourcesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourcesResult> {\n return this.#with((client) => client.listResourcesPage(cursor, options), true);\n }\n\n resourceTemplatesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourceTemplatesResult> {\n return this.#with(\n (client) =>\n withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), {\n resourceTemplates: [],\n }),\n true\n );\n }\n\n allResources(options: McpRequestOptions): Promise<Resource[]> {\n return this.#with((client) => client.listResources(options), true);\n }\n\n allResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]> {\n return this.#with(\n (client) => withoutTemplates(() => client.listResourceTemplates(options), []),\n true\n );\n }\n\n async #with<T>(run: (client: McpClient) => Promise<T>, readOnly = false): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n const client = await this.getClient();\n this.#busy.set(client, (this.#busy.get(client) ?? 0) + 1);\n try {\n return await run(client);\n } catch (error) {\n if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransient(error)) {\n await sleep(CONNECT_RETRY_DELAYS_MS[0] as number, this.#shutdown.signal);\n continue;\n }\n if (error instanceof McpSessionExpiredError && attempt === 1) {\n if (this.#client === client) {\n this.#client = undefined;\n this.#expired.add(client);\n }\n continue;\n }\n if (!(error instanceof McpAuthRequiredError)) throw error;\n await this.#drop(client);\n this.#mark('needs-auth');\n throw new Error(this.signInMessage());\n } finally {\n this.#release(client);\n }\n }\n }\n\n #release(client: McpClient): void {\n const left = (this.#busy.get(client) as number) - 1;\n this.#busy.set(client, left);\n if (left > 0 || !this.#expired.delete(client)) return;\n this.#busy.delete(client);\n void this.#drop(client);\n }\n\n signInMessage(): string {\n return `MCP server \"${this.name}\" rejected its token (401). Update it in Settings \u203A MCP servers.`;\n }\n\n #mark(state: ServerState, error?: string): void {\n this.state = state;\n this.error = error;\n this.#options.onChange?.(this);\n }\n\n async #drop(client: McpClient): Promise<void> {\n if (this.#client === client) this.#client = undefined;\n await client.close().catch(() => undefined);\n }\n\n async #open(): Promise<McpClient> {\n this.#mark('connecting');\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.#connectOnce();\n } catch (error) {\n const delay = CONNECT_RETRY_DELAYS_MS[attempt];\n if (this.closed || delay === undefined || !isTransient(error)) throw this.#failed(error);\n await sleep(delay, this.#shutdown.signal);\n if (this.closed) throw this.#failed(error);\n }\n }\n }\n\n async #connectOnce(): Promise<McpClient> {\n const made = await this.#options.transport(this.entry);\n if ('missing' in made) {\n this.missing = made.missing;\n throw new MissingSecret(made.missing);\n }\n this.secrets = made.secrets;\n const client = new McpClient({\n name: CLIENT_NAME,\n version: this.#options.version,\n requestTimeoutMs: this.timeoutMs,\n });\n const log = this.#options.onLog;\n if (log) client.onNotification('notifications/message', (params) => log(this, params));\n let closing: Promise<void> | undefined;\n const closeClient = () => {\n closing ??= client.close().catch(() => undefined);\n return closing;\n };\n this.#shutdown.signal.addEventListener('abort', closeClient, { once: true });\n try {\n await client.connect(made.transport);\n client.onNotification('notifications/tools/list_changed', () => {\n void this.#refreshTools(client);\n });\n client.onNotification('notifications/resources/list_changed', () => {\n void this.#refreshResources(client);\n });\n const hasResources = client.serverCapabilities?.resources !== undefined;\n const [tools, resources] = await Promise.all([\n client.serverCapabilities?.tools ? client.listTools() : [],\n hasResources ? this.#fetchResources(client) : { resources: [], resourceTemplates: [] },\n ]);\n if (this.closed) throw new Error('shut down while connecting');\n if (client.connectionState !== 'connected') throw new Error('connection closed during setup');\n this.#client = client;\n this.tools = tools;\n this.hasResources = hasResources;\n this.resources = resources.resources;\n this.resourceTemplates = resources.resourceTemplates;\n this.instructions = client.instructions?.trim() || undefined;\n this.missing = [];\n this.#seen = true;\n this.state = 'connected';\n this.error = undefined;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n return client;\n } catch (error) {\n await closeClient();\n throw error;\n } finally {\n this.#shutdown.signal.removeEventListener('abort', closeClient);\n }\n }\n\n async #fetchResources(client: McpClient) {\n const [resources, resourceTemplates] = await Promise.all([\n client.listResources().catch(() => [] as Resource[]),\n withoutTemplates(() => client.listResourceTemplates(), [] as ResourceTemplate[]).catch(\n () => [] as ResourceTemplate[]\n ),\n ]);\n return {\n resources: resources.filter((resource) => !isMcpApp(resource)),\n resourceTemplates: resourceTemplates.filter((template) => !isMcpApp(template)),\n };\n }\n\n #failed(error: unknown): Error {\n if (error instanceof MissingSecret) {\n this.#mark('needs-secret');\n return new Error(error.message);\n }\n if (error instanceof McpAuthRequiredError && !this.closed) {\n this.#mark('needs-auth');\n return new Error(this.signInMessage());\n }\n this.#mark(this.closed ? 'closed' : 'failed', errorMessage(error));\n return new Error(`MCP server \"${this.name}\" failed to connect: ${this.error}`);\n }\n\n async #refreshTools(client: McpClient): Promise<void> {\n try {\n const tools = await client.listTools();\n if (this.#client !== client || this.closed) return;\n this.tools = tools;\n this.#options.onTools(this);\n } catch (error) {\n this.error = `Failed to refresh tools: ${errorMessage(error)}`;\n }\n this.#options.onChange?.(this);\n }\n\n async #refreshResources(client: McpClient): Promise<void> {\n const { resources, resourceTemplates } = await this.#fetchResources(client);\n if (this.#client !== client || this.closed) return;\n this.resources = resources;\n this.resourceTemplates = resourceTemplates;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n }\n\n async close(): Promise<void> {\n this.#shutdown.abort();\n this.state = 'closed';\n this.#options.onChange?.(this);\n const client = this.#client;\n this.#client = undefined;\n await Promise.allSettled([client?.close(), this.#opening]);\n }\n}\n\nexport class MissingSecret extends Error {\n readonly names: string[];\n\n constructor(names: string[]) {\n super(\n `needs ${names.map((name) => `\\${${name}}`).join(', ')}: add the value in Settings \u203A MCP servers`\n );\n this.names = names;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n type CallToolResult,\n JSON_RPC_ERROR_CODES,\n type ListResourcesResult,\n type ListResourceTemplatesResult,\n McpAuthRequiredError,\n McpClient,\n McpError,\n McpHttpError,\n type McpRequestOptions,\n McpSessionExpiredError,\n type Tool as McpTool,\n type McpTransport,\n type ReadResourceResult,\n type Resource,\n type ResourceTemplate,\n} from '@earendil-works/pi-mcp';\nimport { McpOAuthAuthorizationRequiredError } from '@earendil-works/pi-mcp/oauth';\nimport { DEFAULT_TIMEOUT_SECONDS, type McpServerEntry, usesOAuth } from './config.ts';\nimport { secretNames } from './credentials.ts';\n\nexport type ServerState =\n | 'connecting'\n | 'connected'\n | 'needs-auth'\n | 'needs-secret'\n | 'failed'\n | 'closed';\n\nexport type TransportFactory = (\n entry: McpServerEntry\n) => Promise<{ transport: McpTransport; secrets: string[] } | { missing: string[] }>;\n\nexport const CONNECT_RETRY_DELAYS_MS = [250, 1_000];\nexport const CLIENT_NAME = 'slicc';\n\nexport function signInNeeded(error: unknown): boolean {\n return (\n error instanceof McpAuthRequiredError || error instanceof McpOAuthAuthorizationRequiredError\n );\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function isTransient(error: unknown): boolean {\n if (error instanceof McpHttpError)\n return (\n error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501)\n );\n return error instanceof TypeError;\n}\n\nexport function isMcpApp(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n const uri = item.uri ?? item.uriTemplate ?? '';\n return uri.startsWith('ui://') || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? '');\n}\n\nasync function withoutTemplates<T>(list: () => Promise<T>, empty: T): Promise<T> {\n try {\n return await list();\n } catch (error) {\n if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)\n return empty;\n throw error;\n }\n}\n\nconst sleep = (ms: number, signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true }\n );\n });\n\nexport interface ConnectionOptions {\n entry: McpServerEntry;\n version: string;\n transport: TransportFactory;\n onTools: (connection: McpServerConnection) => void;\n onChange?: (connection: McpServerConnection) => void;\n onLog?: (connection: McpServerConnection, params: unknown) => void;\n}\n\nexport class McpServerConnection {\n readonly entry: McpServerEntry;\n state: ServerState = 'connecting';\n error: string | undefined;\n missing: string[] = [];\n tools: McpTool[] = [];\n hasResources = false;\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n instructions: string | undefined;\n secrets: string[] = [];\n #client: McpClient | undefined;\n #opening: Promise<McpClient> | undefined;\n readonly #shutdown = new AbortController();\n readonly #busy = new Map<McpClient, number>();\n #seen = false;\n readonly #expired = new Set<McpClient>();\n readonly #options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.entry = options.entry;\n this.#options = options;\n }\n\n get name(): string {\n return this.entry.name;\n }\n\n get timeoutMs(): number {\n return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n }\n\n get live(): boolean {\n return this.state === 'connected' || (this.state === 'connecting' && this.#seen);\n }\n\n get closed(): boolean {\n return this.#shutdown.signal.aborted;\n }\n\n getClient(): Promise<McpClient> {\n if (this.closed) return Promise.reject(new Error(`MCP server \"${this.name}\" is shut down`));\n if (this.#client?.connectionState === 'connected') return Promise.resolve(this.#client);\n this.#opening ??= this.#open().finally(() => {\n this.#opening = undefined;\n });\n return this.#opening;\n }\n\n callTool(\n name: string,\n args: Record<string, unknown>,\n options: McpRequestOptions\n ): Promise<CallToolResult> {\n return this.#with((client) => client.callTool(name, args, options));\n }\n\n readResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult> {\n return this.#with((client) => client.readResource(uri, options), true);\n }\n\n resourcesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourcesResult> {\n return this.#with((client) => client.listResourcesPage(cursor, options), true);\n }\n\n resourceTemplatesPage(\n cursor: string | undefined,\n options: McpRequestOptions\n ): Promise<ListResourceTemplatesResult> {\n return this.#with(\n (client) =>\n withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), {\n resourceTemplates: [],\n }),\n true\n );\n }\n\n allResources(options: McpRequestOptions): Promise<Resource[]> {\n return this.#with((client) => client.listResources(options), true);\n }\n\n allResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]> {\n return this.#with(\n (client) => withoutTemplates(() => client.listResourceTemplates(options), []),\n true\n );\n }\n\n async #with<T>(run: (client: McpClient) => Promise<T>, readOnly = false): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n const client = await this.getClient();\n this.#busy.set(client, (this.#busy.get(client) ?? 0) + 1);\n try {\n return await run(client);\n } catch (error) {\n if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransient(error)) {\n await sleep(CONNECT_RETRY_DELAYS_MS[0] as number, this.#shutdown.signal);\n continue;\n }\n if (error instanceof McpSessionExpiredError && attempt === 1) {\n if (this.#client === client) {\n this.#client = undefined;\n this.#expired.add(client);\n }\n continue;\n }\n if (!signInNeeded(error)) throw error;\n await this.#drop(client);\n this.#mark('needs-auth');\n throw new Error(this.signInMessage());\n } finally {\n this.#release(client);\n }\n }\n }\n\n #release(client: McpClient): void {\n const left = (this.#busy.get(client) as number) - 1;\n this.#busy.set(client, left);\n if (left > 0 || !this.#expired.delete(client)) return;\n this.#busy.delete(client);\n void this.#drop(client);\n }\n\n signInMessage(): string {\n return usesOAuth(this.entry.config) && !secretNames(this.entry.config).length\n ? `MCP server \"${this.name}\" needs you to sign in. Sign in under Settings \u203A MCP servers.`\n : `MCP server \"${this.name}\" rejected its token (401). Update it in Settings \u203A MCP servers.`;\n }\n\n #mark(state: ServerState, error?: string): void {\n this.state = state;\n this.error = error;\n this.#options.onChange?.(this);\n }\n\n async #drop(client: McpClient): Promise<void> {\n if (this.#client === client) this.#client = undefined;\n await client.close().catch(() => undefined);\n }\n\n async #open(): Promise<McpClient> {\n this.#mark('connecting');\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.#connectOnce();\n } catch (error) {\n const delay = CONNECT_RETRY_DELAYS_MS[attempt];\n if (this.closed || delay === undefined || !isTransient(error)) throw this.#failed(error);\n await sleep(delay, this.#shutdown.signal);\n if (this.closed) throw this.#failed(error);\n }\n }\n }\n\n async #connectOnce(): Promise<McpClient> {\n const made = await this.#options.transport(this.entry);\n if ('missing' in made) {\n this.missing = made.missing;\n throw new MissingSecret(made.missing);\n }\n this.secrets = made.secrets;\n const client = new McpClient({\n name: CLIENT_NAME,\n version: this.#options.version,\n requestTimeoutMs: this.timeoutMs,\n });\n const log = this.#options.onLog;\n if (log) client.onNotification('notifications/message', (params) => log(this, params));\n let closing: Promise<void> | undefined;\n const closeClient = () => {\n closing ??= client.close().catch(() => undefined);\n return closing;\n };\n this.#shutdown.signal.addEventListener('abort', closeClient, { once: true });\n try {\n await client.connect(made.transport);\n client.onNotification('notifications/tools/list_changed', () => {\n void this.#refreshTools(client);\n });\n client.onNotification('notifications/resources/list_changed', () => {\n void this.#refreshResources(client);\n });\n const hasResources = client.serverCapabilities?.resources !== undefined;\n const [tools, resources] = await Promise.all([\n client.serverCapabilities?.tools ? client.listTools() : [],\n hasResources ? this.#fetchResources(client) : { resources: [], resourceTemplates: [] },\n ]);\n if (this.closed) throw new Error('shut down while connecting');\n if (client.connectionState !== 'connected') throw new Error('connection closed during setup');\n this.#client = client;\n this.tools = tools;\n this.hasResources = hasResources;\n this.resources = resources.resources;\n this.resourceTemplates = resources.resourceTemplates;\n this.instructions = client.instructions?.trim() || undefined;\n this.missing = [];\n this.#seen = true;\n this.state = 'connected';\n this.error = undefined;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n return client;\n } catch (error) {\n await closeClient();\n throw error;\n } finally {\n this.#shutdown.signal.removeEventListener('abort', closeClient);\n }\n }\n\n async #fetchResources(client: McpClient) {\n const [resources, resourceTemplates] = await Promise.all([\n client.listResources().catch(() => [] as Resource[]),\n withoutTemplates(() => client.listResourceTemplates(), [] as ResourceTemplate[]).catch(\n () => [] as ResourceTemplate[]\n ),\n ]);\n return {\n resources: resources.filter((resource) => !isMcpApp(resource)),\n resourceTemplates: resourceTemplates.filter((template) => !isMcpApp(template)),\n };\n }\n\n #failed(error: unknown): Error {\n if (error instanceof MissingSecret) {\n this.#mark('needs-secret');\n return new Error(error.message);\n }\n if (signInNeeded(error) && !this.closed) {\n this.#mark('needs-auth');\n return new Error(this.signInMessage());\n }\n this.#mark(this.closed ? 'closed' : 'failed', errorMessage(error));\n return new Error(`MCP server \"${this.name}\" failed to connect: ${this.error}`);\n }\n\n async #refreshTools(client: McpClient): Promise<void> {\n try {\n const tools = await client.listTools();\n if (this.#client !== client || this.closed) return;\n this.tools = tools;\n this.#options.onTools(this);\n } catch (error) {\n this.error = `Failed to refresh tools: ${errorMessage(error)}`;\n }\n this.#options.onChange?.(this);\n }\n\n async #refreshResources(client: McpClient): Promise<void> {\n const { resources, resourceTemplates } = await this.#fetchResources(client);\n if (this.#client !== client || this.closed) return;\n this.resources = resources;\n this.resourceTemplates = resourceTemplates;\n this.#options.onTools(this);\n this.#options.onChange?.(this);\n }\n\n async close(): Promise<void> {\n this.#shutdown.abort();\n this.state = 'closed';\n this.#options.onChange?.(this);\n const client = this.#client;\n this.#client = undefined;\n await Promise.allSettled([client?.close(), this.#opening]);\n }\n}\n\nexport class MissingSecret extends Error {\n readonly names: string[];\n\n constructor(names: string[]) {\n super(\n `needs ${names.map((name) => `\\${${name}}`).join(', ')}: add the value in Settings \u203A MCP servers`\n );\n this.names = names;\n }\n}\n"],
5
+ "mappings": "AAAA;AAAA,EAEE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAMK;AACP,SAAS,0CAA0C;AACnD,SAAS,yBAA8C,iBAAiB;AACxE,SAAS,mBAAmB;AAcrB,MAAM,0BAA0B,CAAC,KAAK,GAAK;AAC3C,MAAM,cAAc;AAEpB,SAAS,aAAa,OAAyB;AACpD,SACE,iBAAiB,wBAAwB,iBAAiB;AAE9D;AAEO,SAAS,aAAa,OAAwB;AACnD,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEO,SAAS,YAAY,OAAyB;AACnD,MAAI,iBAAiB;AACnB,WACE,MAAM,WAAW,OAAO,MAAM,WAAW,OAAQ,MAAM,UAAU,OAAO,MAAM,WAAW;AAE7F,SAAO,iBAAiB;AAC1B;AAEO,SAAS,SAAS,MAA0E;AACjG,QAAM,MAAM,KAAK,OAAO,KAAK,eAAe;AAC5C,SAAO,IAAI,WAAW,OAAO,KAAK,iCAAiC,KAAK,KAAK,YAAY,EAAE;AAC7F;AAEA,eAAe,iBAAoB,MAAwB,OAAsB;AAC/E,MAAI;AACF,WAAO,MAAM,KAAK;AAAA,EACpB,SAAS,OAAO;AACd,QAAI,iBAAiB,YAAY,MAAM,SAAS,qBAAqB;AACnE,aAAO;AACT,UAAM;AAAA,EACR;AACF;AAEA,MAAM,QAAQ,CAAC,IAAY,WACzB,IAAI,QAAc,CAAC,YAAY;AAC7B,QAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AACJ,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AAAA,IACA,EAAE,MAAM,KAAK;AAAA,EACf;AACF,CAAC;AAWI,MAAM,oBAAoB;AAAA,EACtB;AAAA,EACT,QAAqB;AAAA,EACrB;AAAA,EACA,UAAoB,CAAC;AAAA,EACrB,QAAmB,CAAC;AAAA,EACpB,eAAe;AAAA,EACf,YAAwB,CAAC;AAAA,EACzB,oBAAwC,CAAC;AAAA,EACzC;AAAA,EACA,UAAoB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACS,YAAY,IAAI,gBAAgB;AAAA,EAChC,QAAQ,oBAAI,IAAuB;AAAA,EAC5C,QAAQ;AAAA,EACC,WAAW,oBAAI,IAAe;AAAA,EAC9B;AAAA,EAET,YAAY,SAA4B;AACtC,SAAK,QAAQ,QAAQ;AACrB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,YAAoB;AACtB,YAAQ,KAAK,MAAM,OAAO,WAAW,2BAA2B;AAAA,EAClE;AAAA,EAEA,IAAI,OAAgB;AAClB,WAAO,KAAK,UAAU,eAAgB,KAAK,UAAU,gBAAgB,KAAK;AAAA,EAC5E;AAAA,EAEA,IAAI,SAAkB;AACpB,WAAO,KAAK,UAAU,OAAO;AAAA,EAC/B;AAAA,EAEA,YAAgC;AAC9B,QAAI,KAAK,OAAQ,QAAO,QAAQ,OAAO,IAAI,MAAM,eAAe,KAAK,IAAI,gBAAgB,CAAC;AAC1F,QAAI,KAAK,SAAS,oBAAoB,YAAa,QAAO,QAAQ,QAAQ,KAAK,OAAO;AACtF,SAAK,aAAa,KAAK,MAAM,EAAE,QAAQ,MAAM;AAC3C,WAAK,WAAW;AAAA,IAClB,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SACE,MACA,MACA,SACyB;AACzB,WAAO,KAAK,MAAM,CAAC,WAAW,OAAO,SAAS,MAAM,MAAM,OAAO,CAAC;AAAA,EACpE;AAAA,EAEA,aAAa,KAAa,SAAyD;AACjF,WAAO,KAAK,MAAM,CAAC,WAAW,OAAO,aAAa,KAAK,OAAO,GAAG,IAAI;AAAA,EACvE;AAAA,EAEA,cACE,QACA,SAC8B;AAC9B,WAAO,KAAK,MAAM,CAAC,WAAW,OAAO,kBAAkB,QAAQ,OAAO,GAAG,IAAI;AAAA,EAC/E;AAAA,EAEA,sBACE,QACA,SACsC;AACtC,WAAO,KAAK;AAAA,MACV,CAAC,WACC,iBAAiB,MAAM,OAAO,0BAA0B,QAAQ,OAAO,GAAG;AAAA,QACxE,mBAAmB,CAAC;AAAA,MACtB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAa,SAAiD;AAC5D,WAAO,KAAK,MAAM,CAAC,WAAW,OAAO,cAAc,OAAO,GAAG,IAAI;AAAA,EACnE;AAAA,EAEA,qBAAqB,SAAyD;AAC5E,WAAO,KAAK;AAAA,MACV,CAAC,WAAW,iBAAiB,MAAM,OAAO,sBAAsB,OAAO,GAAG,CAAC,CAAC;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,MAAS,KAAwC,WAAW,OAAmB;AACnF,aAAS,UAAU,KAAK,WAAW;AACjC,YAAM,SAAS,MAAM,KAAK,UAAU;AACpC,WAAK,MAAM,IAAI,SAAS,KAAK,MAAM,IAAI,MAAM,KAAK,KAAK,CAAC;AACxD,UAAI;AACF,eAAO,MAAM,IAAI,MAAM;AAAA,MACzB,SAAS,OAAO;AACd,YAAI,YAAY,YAAY,KAAK,iBAAiB,gBAAgB,YAAY,KAAK,GAAG;AACpF,gBAAM,MAAM,wBAAwB,CAAC,GAAa,KAAK,UAAU,MAAM;AACvE;AAAA,QACF;AACA,YAAI,iBAAiB,0BAA0B,YAAY,GAAG;AAC5D,cAAI,KAAK,YAAY,QAAQ;AAC3B,iBAAK,UAAU;AACf,iBAAK,SAAS,IAAI,MAAM;AAAA,UAC1B;AACA;AAAA,QACF;AACA,YAAI,CAAC,aAAa,KAAK,EAAG,OAAM;AAChC,cAAM,KAAK,MAAM,MAAM;AACvB,aAAK,MAAM,YAAY;AACvB,cAAM,IAAI,MAAM,KAAK,cAAc,CAAC;AAAA,MACtC,UAAE;AACA,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,QAAyB;AAChC,UAAM,OAAQ,KAAK,MAAM,IAAI,MAAM,IAAe;AAClD,SAAK,MAAM,IAAI,QAAQ,IAAI;AAC3B,QAAI,OAAO,KAAK,CAAC,KAAK,SAAS,OAAO,MAAM,EAAG;AAC/C,SAAK,MAAM,OAAO,MAAM;AACxB,SAAK,KAAK,MAAM,MAAM;AAAA,EACxB;AAAA,EAEA,gBAAwB;AACtB,WAAO,UAAU,KAAK,MAAM,MAAM,KAAK,CAAC,YAAY,KAAK,MAAM,MAAM,EAAE,SACnE,eAAe,KAAK,IAAI,uEACxB,eAAe,KAAK,IAAI;AAAA,EAC9B;AAAA,EAEA,MAAM,OAAoB,OAAsB;AAC9C,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,SAAK,SAAS,WAAW,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAM,QAAkC;AAC5C,QAAI,KAAK,YAAY,OAAQ,MAAK,UAAU;AAC5C,UAAM,OAAO,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAC5C;AAAA,EAEA,MAAM,QAA4B;AAChC,SAAK,MAAM,YAAY;AACvB,aAAS,UAAU,KAAK,WAAW;AACjC,UAAI;AACF,eAAO,MAAM,KAAK,aAAa;AAAA,MACjC,SAAS,OAAO;AACd,cAAM,QAAQ,wBAAwB,OAAO;AAC7C,YAAI,KAAK,UAAU,UAAU,UAAa,CAAC,YAAY,KAAK,EAAG,OAAM,KAAK,QAAQ,KAAK;AACvF,cAAM,MAAM,OAAO,KAAK,UAAU,MAAM;AACxC,YAAI,KAAK,OAAQ,OAAM,KAAK,QAAQ,KAAK;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,eAAmC;AACvC,UAAM,OAAO,MAAM,KAAK,SAAS,UAAU,KAAK,KAAK;AACrD,QAAI,aAAa,MAAM;AACrB,WAAK,UAAU,KAAK;AACpB,YAAM,IAAI,cAAc,KAAK,OAAO;AAAA,IACtC;AACA,SAAK,UAAU,KAAK;AACpB,UAAM,SAAS,IAAI,UAAU;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS,KAAK,SAAS;AAAA,MACvB,kBAAkB,KAAK;AAAA,IACzB,CAAC;AACD,UAAM,MAAM,KAAK,SAAS;AAC1B,QAAI,IAAK,QAAO,eAAe,yBAAyB,CAAC,WAAW,IAAI,MAAM,MAAM,CAAC;AACrF,QAAI;AACJ,UAAM,cAAc,MAAM;AACxB,kBAAY,OAAO,MAAM,EAAE,MAAM,MAAM,MAAS;AAChD,aAAO;AAAA,IACT;AACA,SAAK,UAAU,OAAO,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;AAC3E,QAAI;AACF,YAAM,OAAO,QAAQ,KAAK,SAAS;AACnC,aAAO,eAAe,oCAAoC,MAAM;AAC9D,aAAK,KAAK,cAAc,MAAM;AAAA,MAChC,CAAC;AACD,aAAO,eAAe,wCAAwC,MAAM;AAClE,aAAK,KAAK,kBAAkB,MAAM;AAAA,MACpC,CAAC;AACD,YAAM,eAAe,OAAO,oBAAoB,cAAc;AAC9D,YAAM,CAAC,OAAO,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC3C,OAAO,oBAAoB,QAAQ,OAAO,UAAU,IAAI,CAAC;AAAA,QACzD,eAAe,KAAK,gBAAgB,MAAM,IAAI,EAAE,WAAW,CAAC,GAAG,mBAAmB,CAAC,EAAE;AAAA,MACvF,CAAC;AACD,UAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,4BAA4B;AAC7D,UAAI,OAAO,oBAAoB,YAAa,OAAM,IAAI,MAAM,gCAAgC;AAC5F,WAAK,UAAU;AACf,WAAK,QAAQ;AACb,WAAK,eAAe;AACpB,WAAK,YAAY,UAAU;AAC3B,WAAK,oBAAoB,UAAU;AACnC,WAAK,eAAe,OAAO,cAAc,KAAK,KAAK;AACnD,WAAK,UAAU,CAAC;AAChB,WAAK,QAAQ;AACb,WAAK,QAAQ;AACb,WAAK,QAAQ;AACb,WAAK,SAAS,QAAQ,IAAI;AAC1B,WAAK,SAAS,WAAW,IAAI;AAC7B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,YAAY;AAClB,YAAM;AAAA,IACR,UAAE;AACA,WAAK,UAAU,OAAO,oBAAoB,SAAS,WAAW;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,QAAmB;AACvC,UAAM,CAAC,WAAW,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,MACvD,OAAO,cAAc,EAAE,MAAM,MAAM,CAAC,CAAe;AAAA,MACnD,iBAAiB,MAAM,OAAO,sBAAsB,GAAG,CAAC,CAAuB,EAAE;AAAA,QAC/E,MAAM,CAAC;AAAA,MACT;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL,WAAW,UAAU,OAAO,CAAC,aAAa,CAAC,SAAS,QAAQ,CAAC;AAAA,MAC7D,mBAAmB,kBAAkB,OAAO,CAAC,aAAa,CAAC,SAAS,QAAQ,CAAC;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,QAAQ,OAAuB;AAC7B,QAAI,iBAAiB,eAAe;AAClC,WAAK,MAAM,cAAc;AACzB,aAAO,IAAI,MAAM,MAAM,OAAO;AAAA,IAChC;AACA,QAAI,aAAa,KAAK,KAAK,CAAC,KAAK,QAAQ;AACvC,WAAK,MAAM,YAAY;AACvB,aAAO,IAAI,MAAM,KAAK,cAAc,CAAC;AAAA,IACvC;AACA,SAAK,MAAM,KAAK,SAAS,WAAW,UAAU,aAAa,KAAK,CAAC;AACjE,WAAO,IAAI,MAAM,eAAe,KAAK,IAAI,wBAAwB,KAAK,KAAK,EAAE;AAAA,EAC/E;AAAA,EAEA,MAAM,cAAc,QAAkC;AACpD,QAAI;AACF,YAAM,QAAQ,MAAM,OAAO,UAAU;AACrC,UAAI,KAAK,YAAY,UAAU,KAAK,OAAQ;AAC5C,WAAK,QAAQ;AACb,WAAK,SAAS,QAAQ,IAAI;AAAA,IAC5B,SAAS,OAAO;AACd,WAAK,QAAQ,4BAA4B,aAAa,KAAK,CAAC;AAAA,IAC9D;AACA,SAAK,SAAS,WAAW,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,kBAAkB,QAAkC;AACxD,UAAM,EAAE,WAAW,kBAAkB,IAAI,MAAM,KAAK,gBAAgB,MAAM;AAC1E,QAAI,KAAK,YAAY,UAAU,KAAK,OAAQ;AAC5C,SAAK,YAAY;AACjB,SAAK,oBAAoB;AACzB,SAAK,SAAS,QAAQ,IAAI;AAC1B,SAAK,SAAS,WAAW,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU,MAAM;AACrB,SAAK,QAAQ;AACb,SAAK,SAAS,WAAW,IAAI;AAC7B,UAAM,SAAS,KAAK;AACpB,SAAK,UAAU;AACf,UAAM,QAAQ,WAAW,CAAC,QAAQ,MAAM,GAAG,KAAK,QAAQ,CAAC;AAAA,EAC3D;AACF;AAEO,MAAM,sBAAsB,MAAM;AAAA,EAC9B;AAAA,EAET,YAAY,OAAiB;AAC3B;AAAA,MACE,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,IAAI,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,IACxD;AACA,SAAK,QAAQ;AAAA,EACf;AACF;",
6
6
  "names": []
7
7
  }
package/dist/mcp/index.js CHANGED
@@ -3,6 +3,9 @@ import { allows, McpAllowDoc, mcpEntries, serverAllowed } from "./allow.js";
3
3
  const loadRuntime = () => import("./runtime.js");
4
4
  const idle = {
5
5
  accounts: async () => [],
6
+ signIn: async (id) => {
7
+ throw new Error(`${id} is not an MCP server that signs in`);
8
+ },
6
9
  connect: async (id) => {
7
10
  throw new Error(`${id} is not an MCP server that takes a token`);
8
11
  },