@oxy-hq/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -7
- package/dist/main.mjs +4914 -3760
- package/dist/main.mjs.map +1 -1
- package/dist/{mcp-xxWL3_dz.mjs → mcp-Bdp0rX0l.mjs} +2 -2
- package/dist/{mcp-xxWL3_dz.mjs.map → mcp-Bdp0rX0l.mjs.map} +1 -1
- package/package.json +5 -5
- package/skills/oxy-agents/SKILL.md +318 -0
- package/skills/oxy-agents/references/quick-reference.md +222 -0
- package/skills/oxy-agents/templates/agentic-template.yml +141 -0
- package/skills/oxy-airway-pipelines/SKILL.md +223 -316
- package/skills/oxy-automations/SKILL.md +61 -15
- package/skills/oxy-cli/SKILL.md +32 -5
- package/skills/oxy-custom-apps/SKILL.md +193 -291
- package/skills/oxy-custom-apps/references/minimal-starter.md +63 -0
- package/skills/oxy-custom-apps/references/performance-and-usage.md +105 -0
- package/skills/oxy-custom-apps/references/reading-data.md +106 -0
- package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +267 -0
- package/skills/oxy-custom-apps/references/verifying-in-the-browser.md +65 -0
- package/skills/oxy-data-apps/SKILL.md +362 -0
- package/skills/oxy-data-apps/references/examples.md +263 -0
- package/skills/oxy-data-apps/references/quick-reference.md +171 -0
- package/skills/oxy-data-apps/templates/dashboard-with-controls.app.yml +128 -0
- package/skills/oxy-data-apps/templates/simple-dashboard.app.yml +102 -0
- package/skills/oxy-data-architecture/SKILL.md +137 -152
- package/skills/oxy-evals/SKILL.md +362 -0
- package/skills/oxy-evals/references/diagnosis.md +243 -0
- package/skills/oxy-evals/references/results-json.md +145 -0
- package/skills/oxy-evals/references/writing-expected.md +112 -0
- package/skills/oxy-semantic-modeling/SKILL.md +239 -59
- package/skills/oxy-semantic-modeling/references/warehouse-casts.md +57 -0
- package/skills/oxy-semantic-modeling/templates/topic-template.yml +38 -0
- package/skills/oxy-semantic-modeling/templates/view-template.yml +119 -0
- package/template/.github/workflows/publish.yaml +15 -13
- package/template/.nvmrc +1 -1
- package/template/.oxyc-managed +5 -5
- package/template/README.md +21 -10
- package/template/package.json +3 -2
- package/template/pnpm-workspace.yaml +28 -17
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as requireSandboxName, B as
|
|
2
|
+
import { A as requireSandboxName, B as forgetSandboxTokens, C as loadOpenApi, D as envDelete, E as envCreate, F as paramsToQuery, G as isExternalSurface, H as parseJson, I as parseFields, J as isSandboxAgentToken, K as normalizePath, L as hint, M as envOnly, N as notAuthenticated, O as envList, P as runJq, Q as usageError, R as warn, S as loadCatalog, T as runChecksCore, U as request, V as revokeCallingToken, W as markMcpSession, X as ExitCode, Y as CliError, Z as exitCodeName, _ as fnCall, a as previewSubmitRun, b as secretSet, c as previewChecks, d as previewFailed, f as previewList, g as invocationsList, h as invocationsHeld, i as previewRunsList, j as createContext, k as envShow, l as previewCreate, m as fetchLogs, n as parseVariables, o as requireRunKind, p as previewShow, q as substitutePlaceholders, r as previewRunGet, s as runFailed, t as publish, u as previewDelete, v as secretDelete, w as searchRoutes, x as comparablePath, y as secretList, z as describeSandboxToken } from "./main.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -1134,4 +1134,4 @@ async function runMcp(ctx) {
|
|
|
1134
1134
|
|
|
1135
1135
|
//#endregion
|
|
1136
1136
|
export { runMcp };
|
|
1137
|
-
//# sourceMappingURL=mcp-
|
|
1137
|
+
//# sourceMappingURL=mcp-Bdp0rX0l.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-xxWL3_dz.mjs","names":[],"sources":["../src/commands/mcp-sandbox.ts","../src/commands/mcp.ts"],"sourcesContent":["/**\n * What `oxyc mcp` serves when its credential is a sandbox agent token\n * (`oxy_sbx_…`): the sandbox loop, `oxy_whoami`, and four tools that exist\n * only here.\n *\n * A SMALLER LIST, NOT A FILTERED ONE. The generic tools — `oxy_request`,\n * `oxy_routes`, `oxy_schema` — and the workspace-preview tools are dropped:\n * the server answers this token `404` on everything they reach, and a tool\n * that can only fail still costs its schema on every turn. What replaces\n * `oxy_request` is named: a sandbox's secret is set with `oxy_env_secret_set`,\n * which can carry the one rule a generic request cannot — a `dev-<handle>`\n * environment, and nothing else.\n *\n * Tool DEFINITIONS and the startup decision live here. The calls themselves\n * are cases in `mcp.ts`'s `callTool`, beside the verbs they share code with.\n */\n\nimport { describeSandboxToken } from \"../apps/sandbox-token.js\";\nimport { isSandboxAgentToken } from \"../auth/token-kind.js\";\nimport { type Context, envOnly, notAuthenticated } from \"../context/resolve.js\";\nimport * as log from \"../ui/log.js\";\nimport { CliError, ExitCode } from \"../util/errors.js\";\n\nexport interface ToolDef {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n };\n}\n\n/** Which list this session serves. Decided once, at startup, from the credential. */\nexport type ToolSet = \"default\" | \"sandbox_agent\";\n\nconst APP = { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' };\nconst OWN_SANDBOX = {\n type: \"string\",\n description: \"dev-<handle> — a sandbox this token created. Production and staging are refused.\"\n};\n\n/** The sandbox tools whose `appEnv` is optional for staff and required here. */\nconst NEEDS_SANDBOX = new Set([\n \"oxy_fn_call\",\n \"oxy_checks_run\",\n \"oxy_invocations_list\",\n \"oxy_logs\"\n]);\n\nconst WHOAMI: ToolDef = {\n name: \"oxy_whoami\",\n description:\n \"What this sandbox agent token is: its kind, when it expires, the apps it reaches and who \" +\n \"minted it. Call it FIRST — confirm the app you were asked to work on is among `apps` and \" +\n \"that `expires_at` is later than the task will take; otherwise stop and ask your operator \" +\n \"for a new token. Call it again when a tool fails with [exit 4 AUTH].\",\n inputSchema: { type: \"object\", properties: {} }\n};\n\n/** The four tools only a sandbox agent token is served. */\nexport const SANDBOX_AGENT_TOOLS: ToolDef[] = [\n {\n name: \"oxy_env_secret_list\",\n description:\n \"List a sandbox's secret KEYS and flags — is_set, required, inherits_staging — never a \" +\n \"value. `missing_required` above zero means the build declares a secret nothing provides. \" +\n \"A sandbox reads its own value first, then staging's.\",\n inputSchema: {\n type: \"object\",\n properties: { app: APP, appEnv: OWN_SANDBOX },\n required: [\"app\", \"appEnv\"]\n }\n },\n {\n name: \"oxy_env_secret_set\",\n description:\n \"Set one secret in a sandbox — a third party's SANDBOX key, say, so a function stops \" +\n \"reading staging's value. Accepts only a dev-<handle> appEnv. Write-only: no tool returns \" +\n \"a value. A value shaped like an Oxy credential is refused by the server.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: APP,\n appEnv: OWN_SANDBOX,\n key: { type: \"string\", description: \"The secret's name, e.g. STRIPE_SECRET_KEY.\" },\n value: { type: \"string\", description: \"The value to store.\" }\n },\n required: [\"app\", \"appEnv\", \"key\", \"value\"]\n }\n },\n {\n name: \"oxy_env_secret_delete\",\n description:\n \"Delete a sandbox's own value of a secret. Reads of it then fall back to staging's. \" +\n \"Accepts only a dev-<handle> appEnv.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: APP,\n appEnv: OWN_SANDBOX,\n key: { type: \"string\", description: \"The secret's name.\" }\n },\n required: [\"app\", \"appEnv\", \"key\"]\n }\n },\n {\n name: \"oxy_token_revoke\",\n description:\n \"END THIS TOKEN — the last step of a task, after oxy_env_delete. It cannot be undone: \" +\n \"every later tool call fails with [exit 4 AUTH]. Requires confirm=true.\",\n inputSchema: {\n type: \"object\",\n properties: {\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" }\n },\n required: [\"confirm\"]\n }\n }\n];\n\n/** A sandbox tool as this credential must call it: the environment is not optional. */\nfunction forSandboxAgent(tool: ToolDef): ToolDef {\n if (tool.name === \"oxy_env_show\") {\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: { ...tool.inputSchema.properties, name: OWN_SANDBOX }\n }\n };\n }\n if (!NEEDS_SANDBOX.has(tool.name)) return tool;\n const required = tool.inputSchema.required ?? [];\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: { ...tool.inputSchema.properties, appEnv: OWN_SANDBOX },\n required: required.includes(\"appEnv\") ? required : [...required, \"appEnv\"]\n }\n };\n}\n\n/** The list for a sandbox agent token, built from the loop's own definitions. */\nexport function sandboxAgentTools(sandboxLoop: ToolDef[]): ToolDef[] {\n return [WHOAMI, ...sandboxLoop.map(forSandboxAgent), ...SANDBOX_AGENT_TOOLS];\n}\n\n/**\n * Decide the tool list, and FAIL CLOSED before serving one.\n *\n * `oxyc mcp` is what an agent runtime starts, with the agent's own token in\n * its environment. Started without `--login`, an unset variable is exit `4`\n * right here: falling back to the login cache would hand the agent its\n * operator's whole reach, silently, for the life of the session. An\n * `OXY_API_KEY` the caller also set is a credential it named, and is let by.\n *\n * A sandbox agent token is then asked what it is (`GET /api/auth/token`).\n * Refused there, it is dead and the session never starts. Unreachable, the\n * prefix still picks the smaller list — the safe direction to be wrong in.\n */\nexport async function startupToolSet(ctx: Context): Promise<ToolSet> {\n const bearer = ctx.storedBearer();\n if (envOnly(ctx.flags) && !bearer && !ctx.apiKey()) {\n throw notAuthenticated(ctx.target(), ctx.flags);\n }\n if (!isSandboxAgentToken(bearer)) return \"default\";\n\n try {\n await describeSandboxToken(ctx.target(), bearer);\n } catch (cause) {\n if (!(cause instanceof CliError) || cause.code === ExitCode.AUTH) throw cause;\n log.warn(`could not confirm the sandbox agent token with ${ctx.target()}: ${cause.message}`);\n log.hint(\"serving the sandbox tools anyway — each call will report what it meets\");\n }\n return \"sandbox_agent\";\n}\n\n/** A tool the session does not serve, called anyway. */\nexport function notServed(name: string, set: ToolSet): string {\n return set === \"sandbox_agent\"\n ? `${name} is not served to a sandbox agent token. It does the sandbox loop with the tools listed, and nothing else.`\n : `unknown tool: ${name}`;\n}\n","/**\n * `oxyc mcp` — the Oxy API as MCP tools, over stdio.\n *\n * NOT THE SAME THING AS `oxy mcp`. That one is workspace tooling: it takes a\n * local checkout and exposes the semantic model, automations and `.sql` files\n * in it. This one takes a TOKEN and exposes the deployment's HTTP API — the\n * same surface `oxyc api` reaches, for an agent that would rather call a tool\n * than shell out. Different input, different audience, no overlap.\n *\n * FOUR GENERIC TOOLS FOR THE WHOLE API — the design decision that makes most\n * of it affordable.\n *\n * The obvious shape is one tool per endpoint, and it is the wrong one: the API\n * has ~670 routes, an agent runtime ships every tool's JSON schema in every\n * request, and that is tens of kilobytes of context spent on each turn before\n * a single question is asked. It would also go stale on every deploy, since\n * the tool list would be baked into this package rather than read from the\n * deployment.\n *\n * So discovery stays a QUESTION the agent asks (`oxy_routes`, `oxy_schema`)\n * rather than a payload it carries, exactly as the CLI does it, for\n * everything a generic request can do safely.\n *\n * TWO NAMED EXCEPTIONS: `SANDBOX_TOOLS` and `PREVIEW_TOOLS` below. The sandbox\n * loop and the workspace-previews loop are meant to run UNSUPERVISED — create\n * a sandbox, publish into it, call a function, tear it down — and that needs\n * per-operation validation (the `dev-<handle>` grammar, a confirm gate before\n * a delete, a hard refusal of `--app-env` outside `dev-<handle>` on the\n * publish tool) that `oxy_request` has no way to carry. Each of those tools\n * calls its CLI verb's own request-building function (`env.ts`, `fn.ts`,\n * `checks.ts`, `preview.ts`, `preview-runs.ts`, …), so there is exactly one\n * implementation of each request, and a tool's result is the same JSON\n * document that verb's `--json` prints. `mcp.test.ts` pins the full tool list\n * and a raised (and justified) per-turn schema budget.\n *\n * Target resolution and placeholder substitution are the CLI's — the same\n * `Context`, so `{org}` / `{workspace}` resolve the same way.\n *\n * THE CREDENTIAL IS THE TOKEN VARIABLE AND NOTHING ELSE, unless the server is\n * started with `--login`. An agent runtime starts this with the agent's own\n * token in `OXY_TOKEN`; with the variable unset, the process exits `4` rather\n * than serving on whatever `oxyc login` cached for the machine's owner. A\n * person who does want their own login passes `--login`.\n *\n * A SANDBOX AGENT TOKEN (`oxy_sbx_…`) GETS A DIFFERENT, SMALLER LIST — the\n * sandbox loop, `oxy_whoami`, three secret tools and `oxy_token_revoke`; no\n * `oxy_request`, no discovery, no previews. See `mcp-sandbox.ts`.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { loadCatalog, loadOpenApi, searchRoutes } from \"../api/catalog.js\";\nimport { paramsToQuery, parseFields } from \"../api/fields.js\";\nimport { runJq } from \"../api/output.js\";\nimport { isExternalSurface, normalizePath, substitutePlaceholders } from \"../api/paths.js\";\nimport { parseJson, request } from \"../api/request.js\";\nimport { markMcpSession } from \"../api/user-agent.js\";\nimport { requireSandboxName } from \"../apps/environment.js\";\nimport { describeSandboxToken, forgetSandboxTokens } from \"../apps/sandbox-token.js\";\nimport { revokeCallingToken } from \"../auth/token-api.js\";\nimport { isSandboxAgentToken } from \"../auth/token-kind.js\";\nimport { type Context, createContext } from \"../context/resolve.js\";\nimport {\n CliError,\n ExitCode,\n type ExitCodeValue,\n exitCodeName,\n usageError\n} from \"../util/errors.js\";\nimport { runChecksCore } from \"./checks.js\";\nimport { comparablePath } from \"./discover.js\";\nimport { envCreate, envDelete, envList, envShow } from \"./env.js\";\nimport { secretDelete, secretList, secretSet } from \"./env-secrets.js\";\nimport { fnCall } from \"./fn.js\";\nimport { invocationsHeld, invocationsList } from \"./invocations.js\";\nimport { fetchLogs } from \"./logs.js\";\nimport {\n notServed,\n sandboxAgentTools,\n startupToolSet,\n type ToolDef,\n type ToolSet\n} from \"./mcp-sandbox.js\";\nimport {\n previewChecks,\n previewCreate,\n previewDelete,\n previewFailed,\n previewList,\n previewShow\n} from \"./preview.js\";\nimport {\n parseVariables,\n previewRunGet,\n previewRunsList,\n previewSubmitRun,\n requireRunKind,\n runFailed\n} from \"./preview-runs.js\";\nimport { publish } from \"./publish.js\";\n\n/**\n * Package version, reported in the MCP handshake.\n *\n * Read from `package.json` rather than restated, so a release bump cannot\n * leave the server announcing a version nobody shipped.\n */\nconst VERSION: string = (() => {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n for (const candidate of [\n resolve(here, \"..\", \"package.json\"),\n resolve(here, \"..\", \"..\", \"package.json\")\n ]) {\n if (existsSync(candidate)) {\n return (\n (JSON.parse(readFileSync(candidate, \"utf8\")) as { version?: string }).version ?? \"0.0.0\"\n );\n }\n }\n } catch {\n // A packaging shape we did not anticipate. The handshake needs a string,\n // not an accurate one.\n }\n return \"0.0.0\";\n})();\n\n/**\n * A tool result. MCP wants content blocks; everything here is text, because\n * the caller is a language model and the payloads are JSON or markdown.\n */\nfunction text(body: string, isError = false) {\n return { content: [{ type: \"text\" as const, text: body }], isError };\n}\n\n/**\n * A tool result carrying one JSON document — the same shape the matching\n * verb's `--json` prints, so a tool's result and the CLI's output never\n * drift apart. On failure the exit-code class is appended, same spelling as\n * the generic catch-all below (`[exit N NAME]`), so an agent branches on it\n * the way it would on `oxyc`'s own exit code.\n */\nfunction jsonResult(data: unknown, opts: { isError?: boolean; code?: ExitCodeValue } = {}) {\n const body = JSON.stringify(data);\n if (!opts.isError) return text(body);\n const code = opts.code ?? ExitCode.FAILURE;\n return text(`${body}\\n\\n[exit ${code} ${exitCodeName(code)}]`, true);\n}\n\nconst TOOLS = [\n {\n name: \"oxy_routes\",\n description:\n \"List the API endpoints this Oxy deployment mounts, with what each one does. \" +\n \"ALWAYS call this before oxy_request rather than guessing a path. \" +\n \"Pass a filter (matched against method, path, surface and description) to narrow it — \" +\n \"unfiltered is ~670 routes. Above 60 matches the descriptions are dropped and only \" +\n \"method, path and credential come back, so narrow enough to stay under that when you \" +\n \"need to know what an endpoint does; past ~400 it is refused outright.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filter: {\n type: \"string\",\n description: \"Substring to narrow by, e.g. 'threads', 'sql', 'admin', 'semantic'.\"\n },\n all: {\n type: \"boolean\",\n description:\n \"Include ide-only and worker-only routes. Off by default: those are mounted on one \" +\n \"instance of the fleet, so a caller hitting the load balancer cannot reach them directly.\"\n }\n }\n }\n },\n {\n name: \"oxy_schema\",\n description:\n \"The request and response schema for ONE endpoint, so a body can be built correctly. \" +\n \"Covers the data plane (SQL, semantic query, and the org/workspace lookups); a blank \" +\n \"result means undocumented, not nonexistent — use oxy_routes to confirm the endpoint exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Endpoint path, e.g. '{workspace}/sql/query'.\" },\n method: { type: \"string\", description: \"Narrow to one HTTP method.\" }\n },\n required: [\"path\"]\n }\n },\n {\n name: \"oxy_request\",\n description:\n \"Make an authenticated request to the Oxy API. The credential is picked from the path \" +\n \"(/api/** takes a bearer, /external/api/** an API key). Placeholders {org}, {workspace}, \" +\n \"{project}, {customer} and {me} are substituted from context. \" +\n \"Read freely; ask the human before any mutating request against production.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Path relative to /api, e.g. '{org}/workspaces'.\" },\n method: {\n type: \"string\",\n description: \"HTTP method. Defaults to GET, or POST with fields.\"\n },\n fields: {\n type: \"object\",\n description:\n \"Request parameters. Sent as a JSON body on POST/PUT/PATCH, or as query parameters \" +\n \"on GET/HEAD/DELETE. Values keep their JSON type.\"\n },\n jq: {\n type: \"string\",\n description:\n \"A jq program to reduce the response before it is returned. Use it — a full list \" +\n \"response is far more context than the two fields you need.\"\n }\n },\n required: [\"path\"]\n }\n },\n {\n name: \"oxy_whoami\",\n description:\n \"Which deployment this is pointed at and who the token belongs to. \" +\n \"Use it when a call returns 401/403, or a 200 with a null body — on this API an expired \" +\n \"session answers 200 null rather than 401, and this tells the two apart.\",\n inputSchema: { type: \"object\", properties: {} }\n }\n];\n\n/**\n * THE SANDBOX LOOP, AS TOOLS — the deliberate exception to \"four, not one per\n * endpoint\" above. A generic `oxy_request` can call these same routes, but it\n * cannot carry the per-operation validation that makes the loop safe for an\n * agent to drive unsupervised: the dev-<handle> grammar, the confirm-before-\n * delete gate, and the hard refusal of a sandbox publish outside dev-<handle>.\n * Every one of these reuses its CLI verb's own request-building function —\n * see `env.ts` / `fn.ts` / `checks.ts` / `invocations.ts` / `logs.ts` /\n * `publish.ts` — so there is exactly one implementation of each request, and\n * the tool result is the same JSON document that verb's `--json` prints.\n *\n * Each description says what it returns AND what to call next, so the loop\n * reads off the tool list alone: env_create → publish_sandbox → fn_call /\n * checks_run → invocations_list / invocations_held / logs → iterate from\n * publish_sandbox → env_delete.\n */\nconst SANDBOX_TOOLS = [\n {\n name: \"oxy_env_create\",\n description:\n \"Create a dev-<handle> SANDBOX of a custom app — its own build pointer, storage silo, \" +\n \"secrets and Airhouse sibling, isolated from production and from every other sandbox. \" +\n \"Starts with no build. Returns the Environment (status, build, url). Call \" +\n \"oxy_publish_sandbox next to give it one.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: {\n type: \"string\",\n description: \"dev-<handle> — 1-12 lowercase letters, digits and single hyphens.\"\n }\n },\n required: [\"app\", \"name\"]\n }\n },\n {\n name: \"oxy_env_list\",\n description:\n \"List a custom app's environments — production, staging and every dev-<handle> sandbox \" +\n \"— each with its status and build. Use it to find a sandbox's name before oxy_env_show, \" +\n \"oxy_publish_sandbox or oxy_env_delete.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_env_show\",\n description:\n \"One environment's detail — status, build, url. Call it after oxy_publish_sandbox to \" +\n \"confirm a build landed, or before oxy_fn_call to confirm one exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: { type: \"string\", description: \"production, staging, or dev-<handle>.\" }\n },\n required: [\"app\", \"name\"]\n }\n },\n {\n name: \"oxy_env_delete\",\n description:\n \"DESTRUCTIVE — tears down a sandbox's storage silo, secrets and Airhouse sibling for \" +\n \"good. Requires confirm=true: there is no terminal to ask on here, so the delete refuses \" +\n \"without it, the same way `oxyc env delete` refuses without --yes off a TTY. Set \" +\n \"waitSeconds to avoid polling oxy_env_show yourself.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: {\n type: \"string\",\n description: \"dev-<handle> — production and staging can never be deleted.\"\n },\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" },\n waitSeconds: {\n type: \"number\",\n description: \"Block until torn down, or this many seconds pass (no wait by default).\"\n }\n },\n required: [\"app\", \"name\", \"confirm\"]\n }\n },\n {\n name: \"oxy_publish_sandbox\",\n description:\n \"DESTRUCTIVE (to the sandbox's build, not production) — build the app in `dir` (default: \" +\n \"the current directory) and publish it to a dev-<handle> sandbox's build pointer. \" +\n \"REQUIRES a dev-<handle> appEnv and refuses production, staging, or any attempt to \" +\n \"promote — a sandbox build is never promoted; publish the same tree to staging and \" +\n \"promote that instead, outside this tool. Runs the app's own build command, so it \" +\n \"executes whatever package scripts oxy-app.json declares. Returns the publish result \" +\n \"(build id, url). Call oxy_fn_call or oxy_checks_run with the same appEnv next.\",\n inputSchema: {\n type: \"object\",\n properties: {\n appEnv: {\n type: \"string\",\n description: \"dev-<handle> — anything else (production, staging) is refused.\"\n },\n dir: { type: \"string\", description: \"App directory to publish (default: cwd).\" },\n app: {\n type: \"string\",\n description: \"App slug (default: oxy-app.json's slug, or OXY_APP).\"\n },\n org: {\n type: \"string\",\n description: \"Org slug (default: oxy-app.json's orgSlug, or OXY_ORG).\"\n }\n },\n required: [\"appEnv\"]\n }\n },\n {\n name: \"oxy_fn_call\",\n description:\n \"Call a custom app's Oxy Function directly (POST .../fn/<function>) and return its \" +\n \"result: {ok, status, body, logs, invocationId, error}. isError is set when the \" +\n \"FUNCTION failed (distinct from a transport failure, which throws). Needs a build in \" +\n \"that environment — oxy_publish_sandbox or oxy_env_show confirms one exists. Call \" +\n \"oxy_logs or oxy_invocations_held with the returned invocationId to see why it failed.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n function: { type: \"string\", description: \"The Oxy Function's name.\" },\n appEnv: { type: \"string\", description: \"production (default), staging, or dev-<handle>.\" },\n data: { type: \"string\", description: 'JSON request body (default \"{}\").' },\n timeoutSeconds: { type: \"number\", description: \"Client-side timeout (default 60).\" }\n },\n required: [\"app\", \"function\"]\n }\n },\n {\n name: \"oxy_checks_run\",\n description:\n 'Run every function the app marked \"check\": true in one environment, and wait for each ' +\n \"to a terminal status. Returns {app, checks:[{name, status, passed, error, durationMs}]}; \" +\n \"isError is set when any check failed or timed out. Run this right after \" +\n \"oxy_publish_sandbox to verify a build.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: { type: \"string\", description: \"production (default), staging, or dev-<handle>.\" },\n timeoutSeconds: { type: \"number\", description: \"Per-check timeout (default 300).\" }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_invocations_list\",\n description:\n \"List what ran in an app's environment — every function invocation, newest first, with \" +\n \"its status. Use it to confirm oxy_fn_call or oxy_checks_run reached the sandbox, or to \" +\n \"find an invocation id for oxy_invocations_held.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: { type: \"string\", description: \"Narrow to one environment.\" },\n build: { type: \"string\", description: \"Narrow to one build (its id, or the build UUID).\" },\n function: { type: \"string\", description: \"Narrow to one function.\" },\n limit: { type: \"number\", description: \"Max rows (default 50).\" }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_invocations_held\",\n description:\n \"What a non-production invocation's write policy HELD instead of performing — \" +\n \"production's would-be effect, recorded rather than run. Empty for a production \" +\n \"invocation, since nothing is held there. Call with an id from oxy_invocations_list or \" +\n \"oxy_fn_call's invocationId.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n invocationId: { type: \"string\", description: \"From oxy_invocations_list or oxy_fn_call.\" }\n },\n required: [\"app\", \"invocationId\"]\n }\n },\n {\n name: \"oxy_logs\",\n description:\n \"An app's persisted ctx.log() / console.* output, newest-relevant window first. Use it \" +\n \"to debug a function that oxy_fn_call or oxy_checks_run reported as failed.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: {\n type: \"string\",\n description: \"Narrow to one environment (default: production only).\"\n },\n invocation: { type: \"string\", description: \"Narrow to one invocation id.\" },\n request: { type: \"string\", description: \"Narrow to one request id.\" },\n hours: { type: \"number\", description: \"Window size in hours (default 24, max 168).\" },\n limit: { type: \"number\", description: \"Max rows (default 100, max 500).\" }\n },\n required: [\"app\"]\n }\n }\n];\n\n/**\n * WORKSPACE PREVIEWS, AS TOOLS — the same exception as `SANDBOX_TOOLS` above,\n * for the same reason: the run-kind grammar and the create/wait/timeout\n * contract are per-operation validation a generic `oxy_request` cannot carry.\n * Each reuses its `oxyc preview` verb's own request-building function (see\n * `preview.ts` / `preview-runs.ts`), so there is one implementation of each\n * request and the tool result matches that verb's `--json` output.\n *\n * The loop: preview_create (waits for the compile) → preview_checks → \" +\n * preview_run (procedure or airway_sample) → preview_run_show (waits for the \" +\n * run) → preview_runs_list to see transform_build/compare runs the server \" +\n * queued on its own → preview_delete when done.\n */\nconst PREVIEW_TOOLS = [\n {\n name: \"oxy_preview_create\",\n description:\n \"Preview a workspace branch: compile its head into a staging revision (or reuse the \" +\n \"ready one for that commit) and start serving it under x-oxy-preview-revision. Returns \" +\n \"the PreviewItem ({branch, status, revision_id, checks, ...}) — idempotent, so calling it \" +\n \"again on the same commit is free. Set waitSeconds to avoid polling oxy_preview_show \" +\n \"yourself; isError is then set on a compile that failed. Call oxy_preview_checks next.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The branch to preview. Not the default branch.\" },\n waitSeconds: {\n type: \"number\",\n description:\n \"Block until the compile is ready or failed, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_list\",\n description:\n \"List every branch staff are previewing in this workspace, most recently touched first, \" +\n \"each with its compile status and checks summary. Use it to find a branch name before \" +\n \"the other preview tools.\",\n inputSchema: { type: \"object\", properties: {} }\n },\n {\n name: \"oxy_preview_show\",\n description:\n \"One branch's preview — status and checks summary (PreviewItem). Not a server route: \" +\n \"filters oxy_preview_list's result by branch, and fails NOT_FOUND when the branch has \" +\n \"no preview.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_delete\",\n description:\n \"DESTRUCTIVE — stop previewing a branch: cancels its queued runs and releases its \" +\n \"staging revision. Requires confirm=true (there is no terminal to ask on here).\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The previewed branch to stop previewing.\" },\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" }\n },\n required: [\"branch\", \"confirm\"]\n }\n },\n {\n name: \"oxy_preview_checks\",\n description:\n \"The Airway change check of a preview's current revision: each changed .airway.yml \" +\n \"pipeline and whether merging it needs a Reset schema, and each changed automation \" +\n '(\"auto\" transforms are built in the preview and compared with live; \"manual\" ones say ' +\n 'why not). \"pending\" with no pipelines until the check has run — call oxy_preview_show ' +\n \"or retry shortly.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_run\",\n description:\n \"Start a held dry run of a previewed branch: a procedure (every write held or, on the \" +\n \"managed Airhouse, redirected into the preview's own schemas) or an airway_sample (a \" +\n \"bounded window of a pipeline into the preview's schemas). transform_build and compare \" +\n \"runs are queued by the server's own change check and cannot be started here — read \" +\n \"them back with oxy_preview_runs_list / oxy_preview_run_show instead. Returns \" +\n \"{run_id, state}. Set waitSeconds to avoid polling oxy_preview_run_show yourself; isError \" +\n \"is then set if the run failed or was cancelled. Needs OXY_PREVIEW_RUNS enabled on the \" +\n \"deployment.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The previewed branch to run against.\" },\n kind: { type: \"string\", description: '\"procedure\" or \"airway_sample\".' },\n ref: {\n type: \"string\",\n description: \"The automation path (procedure) or .airway.yml path (airway_sample).\"\n },\n variables: {\n type: \"string\",\n description: \"procedure: JSON object of the automation's variables.\"\n },\n readLiveOnly: {\n type: \"boolean\",\n description: \"procedure: read live tables even where the preview holds a copy.\"\n },\n windowFrom: { type: \"string\", description: \"airway_sample: window start, RFC 3339.\" },\n windowTo: { type: \"string\", description: \"airway_sample: window end, RFC 3339.\" },\n resources: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"airway_sample: the resources to read, for a multi-resource source.\"\n },\n waitSeconds: {\n type: \"number\",\n description: \"Block until the run finishes, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"branch\", \"kind\", \"ref\"]\n }\n },\n {\n name: \"oxy_preview_runs_list\",\n description:\n \"A branch's held runs, newest first — procedure, transform_build, compare and \" +\n \"airway_sample alike, with each one's state and outcome. Use it to find a run_id for \" +\n \"oxy_preview_run_show, including the transform_build/compare runs the server queues on \" +\n \"its own that oxy_preview_run cannot start.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_run_show\",\n description:\n \"One run's detail: its steps (with what each held or redirected), and — for \" +\n \"transform_build/compare — the compare-with-live outcome, or — for airway_sample — the \" +\n \"sample's ask and result. Set waitSeconds to avoid polling yourself; isError is then set \" +\n \"if the run failed or was cancelled.\",\n inputSchema: {\n type: \"object\",\n properties: {\n runId: { type: \"string\", description: \"From oxy_preview_run or oxy_preview_runs_list.\" },\n waitSeconds: {\n type: \"number\",\n description: \"Block until the run finishes, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"runId\"]\n }\n }\n];\n\nconst ALL_TOOLS: ToolDef[] = [...TOOLS, ...SANDBOX_TOOLS, ...PREVIEW_TOOLS];\n\n/** The list a session serves — decided once, from its credential (`mcp-sandbox.ts`). */\nfunction toolsFor(set: ToolSet): ToolDef[] {\n return set === \"sandbox_agent\" ? sandboxAgentTools(SANDBOX_TOOLS) : ALL_TOOLS;\n}\n\n/**\n * Above this many matches, `oxy_routes` drops the descriptions.\n *\n * DEGRADES, IT DOES NOT REFUSE — the same shape as `oxyc routes`, which picks\n * `renderDetailed` under 40 matches and the compact method+path table above it\n * (`discover.ts`). The expensive field is `description`, not the row: a trimmed\n * row is a few tens of bytes, so several hundred still fit in a result an agent\n * can read, while the descriptions are what turn a broad match into hundreds of\n * KB of context spent before a question is asked.\n *\n * The threshold is on the COUNT, not on whether a `filter` was supplied — a\n * filter is not evidence of narrowing. `searchRoutes` matches a substring of\n * method, path, surface or description, so `\"admin\"`, `\"workspace\"` and\n * `\"query\"` are all filters a model reaches for in good faith that clear this.\n */\nconst MAX_DESCRIBED_ROUTES = 60;\n\n/**\n * And above THIS many, there is nothing useful to return at all.\n *\n * Deliberately far above the threshold that drops descriptions: refusing is the\n * answer only when even the trimmed listing would be a context dump, which for\n * a deployment of ~670 routes means \"you asked for effectively all of them\".\n */\nconst MAX_ROUTES_PER_RESULT = 400;\n\n/** `{ a: 1 }` → the `-f`/`-F` pairs `parseFields` understands. */\nfunction fieldPairs(fields: Record<string, unknown> | undefined): string[] {\n if (!fields) return [];\n return Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`);\n}\n\n/**\n * Check the arguments the schema says are required.\n *\n * `inputSchema.required` is ADVISORY on the low-level `Server` — it is handed\n * to the model, not enforced by the transport. Without this, `oxy_schema` with\n * no `path` matched every path and returned the whole OpenAPI document, and\n * `oxy_request` with none built a request to `/api/`.\n */\nfunction requireArgs(name: string, args: Record<string, unknown>, required: string[]): void {\n const missing = required.filter((k) => {\n const v = args[k];\n return v === undefined || v === null || (typeof v === \"string\" && v.trim() === \"\");\n });\n if (missing.length > 0) {\n throw new CliError(`${name} needs ${missing.join(\", \")}`, { code: ExitCode.USAGE });\n }\n}\n\nasync function callTool(ctx: Context, name: string, args: Record<string, unknown>) {\n switch (name) {\n case \"oxy_routes\": {\n const catalog = await loadCatalog({ target: ctx.target(), bearer: await ctx.maybeBearer() });\n const filter = (args.filter as string | undefined)?.trim();\n let matches = searchRoutes(catalog, filter || undefined);\n if (!args.all) matches = matches.filter((r) => r.role === \"fleet-ok\");\n\n // A cache stale enough to be wrong is worth saying INSIDE the result:\n // `loadCatalog` warns on stderr, which an MCP client never shows anyone.\n const staleNote = catalog.stale\n ? \"NOTE: this route table came from a stale local cache; the deployment was unreachable.\\n\\n\"\n : \"\";\n\n const asked = filter ? ` ${JSON.stringify(filter)}` : \"\";\n\n // Only a match set too large even to LIST is refused. Below that the\n // result degrades instead, because a filter like \"admin\" or \"query\" can\n // clear the description threshold in good faith and \"guess something\n // narrower\" is a dead end for a model with no way to see what it hit.\n if (matches.length > MAX_ROUTES_PER_RESULT) {\n return text(\n `${staleNote}${matches.length} routes match${asked}, which is effectively the whole ` +\n `deployment. Call oxy_routes again with a narrower \\`filter\\` (matched against ` +\n `method, path, surface and description), e.g. \"threads\", \"sql\", \"semantic\", \"admin\".`,\n true\n );\n }\n\n if (matches.length === 0) {\n // An empty array reads as \"this deployment has no such endpoint\",\n // which is usually false — the filter was wrong, or the route is\n // ide-only and hidden by default.\n return text(\n `${staleNote}No route matches ${JSON.stringify(filter ?? \"\")}. Try a broader filter, ` +\n `or pass all=true to include ide-only and worker-only mounts.`,\n true\n );\n }\n // Trimmed to the fields that help a caller build a request. The full\n // record carries the handler path and mount notes, which are for a\n // maintainer reading source, not for an agent composing a call.\n const described = matches.length <= MAX_DESCRIBED_ROUTES;\n // `credential` SURVIVES THE TRIM. It is what separates the bearer mount\n // from the `/external/api` API-key one, so a caller composing a request\n // needs it in either form — and `renderCompact`, the CLI shape this\n // mirrors, keeps it as a per-surface heading. It is also not the\n // expensive field: ~25 bytes a row, ~10 KB across the whole ceiling,\n // against descriptions that run to hundreds of KB.\n const trimmed = matches.map((r) =>\n described\n ? { method: r.method, path: r.path, credential: r.credential, description: r.description }\n : { method: r.method, path: r.path, credential: r.credential }\n );\n // The note says the listing is trimmed AND how to get the descriptions\n // back. Without it a model reads a description-less row as an endpoint\n // that has no documentation, rather than one it did not ask narrowly\n // enough to see.\n const trimNote = described\n ? \"\"\n : `NOTE: ${matches.length} routes match${asked} — too many to describe, so each row is ` +\n `method, path and credential WITHOUT its description. Narrow the \\`filter\\` to ` +\n `${MAX_DESCRIBED_ROUTES} or fewer matches to get the descriptions back.\\n\\n`;\n return text(`${staleNote}${trimNote}${JSON.stringify(trimmed)}`);\n }\n\n case \"oxy_schema\": {\n requireArgs(\"oxy_schema\", args, [\"path\"]);\n const doc = (await loadOpenApi({\n target: ctx.target(),\n bearer: await ctx.maybeBearer()\n })) as {\n paths?: Record<string, Record<string, unknown>>;\n components?: unknown;\n };\n const wanted = typeof args.path === \"string\" ? args.path : \"\";\n const method = args.method as string | undefined;\n // `comparablePath` is imported from `discover.ts` rather than copied:\n // a second reduction of the same two spellings is a second thing to keep\n // in step, and the copy here was untested. Exact first, then substring —\n // the order `runSchema` uses, so the two commands answer alike.\n const paths = Object.entries(doc.paths ?? {});\n const exact = paths.filter(([p]) => comparablePath(p) === comparablePath(wanted));\n const hit =\n exact.length > 0\n ? exact\n : paths.filter(([p]) => comparablePath(p).includes(comparablePath(wanted)));\n if (hit.length === 0) {\n return text(\n `No documented schema for ${wanted}. The OpenAPI document covers the data plane only — ` +\n `call oxy_routes with a filter to confirm the endpoint exists.`,\n true\n );\n }\n const selected = Object.fromEntries(\n hit.map(([p, ops]) => [\n p,\n method\n ? Object.fromEntries(\n Object.entries(ops).filter(([verb]) => verb.toLowerCase() === method.toLowerCase())\n )\n : ops\n ])\n );\n return text(JSON.stringify({ paths: selected, components: doc.components }, null, 2));\n }\n\n case \"oxy_request\": {\n requireArgs(\"oxy_request\", args, [\"path\"]);\n const raw = typeof args.path === \"string\" ? args.path : \"\";\n const method =\n (typeof args.method === \"string\" ? args.method.toUpperCase() : \"\") || undefined;\n const fields = parseFields([], fieldPairs(args.fields as Record<string, unknown>));\n const verb = method ?? (fields.present ? \"POST\" : \"GET\");\n const carriesBody = ![\"GET\", \"HEAD\", \"DELETE\"].includes(verb);\n\n let path = substitutePlaceholders(normalizePath(raw), ctx.placeholders());\n let body: string | undefined;\n if (fields.present) {\n if (carriesBody) body = JSON.stringify(fields.params);\n else {\n const query = paramsToQuery(fields.params);\n if (query) path += (path.includes(\"?\") ? \"&\" : \"?\") + query;\n }\n }\n\n const apiKey = ctx.apiKey();\n const external = isExternalSurface(path);\n const response = await request({\n target: ctx.target(),\n path,\n method: verb,\n body,\n bearer: external && apiKey ? ctx.storedBearer() : await ctx.bearer(),\n apiKey\n });\n\n if (response.status < 200 || response.status >= 300) {\n // The STATUS and the body, not a thrown error: the model can act on\n // \"403, here is what the server said\" and cannot act on a transport\n // exception. It is marked `isError` so the runtime shows it as one.\n return text(`HTTP ${response.status} ${response.statusText}\\n${response.body}`, true);\n }\n\n if (response.body.trim() === \"null\") {\n return text(\n \"null\\n\\n(NOTE: a 200 with a null body can mean an expired session on this API, \" +\n \"not 'no such thing'. Call oxy_whoami to tell them apart.)\"\n );\n }\n\n const jq = args.jq as string | undefined;\n if (jq) return text(runJq(response.body, jq));\n return text(response.body);\n }\n\n case \"oxy_whoami\": {\n const target = ctx.target();\n const bearer = await ctx.bearer();\n // `/api/user` answers a sandbox agent token 404. Its own description is\n // the answer, asked fresh: this tool is what tells a dead token from a\n // live one.\n if (isSandboxAgentToken(bearer)) {\n const described = await describeSandboxToken(target, bearer, { fresh: true });\n return text(JSON.stringify({ target, token: parseJson(described.body) }, null, 2));\n }\n const response = await request({\n target,\n path: \"/api/user\",\n method: \"GET\",\n bearer\n });\n const payload = parseJson(response.body);\n if (payload === null || payload === undefined) {\n return text(\n `target: ${target}\\nThe token no longer resolves to a user — the session has expired. ` +\n `Ask the human to run \\`oxyc login\\`.`,\n true\n );\n }\n return text(JSON.stringify({ target, user: payload }, null, 2));\n }\n\n // ── sandbox loop ───────────────────────────────────────────────────────\n\n case \"oxy_env_create\": {\n requireArgs(\"oxy_env_create\", args, [\"app\", \"name\"]);\n return jsonResult(await envCreate(ctx, String(args.app), String(args.name)));\n }\n\n case \"oxy_env_list\": {\n requireArgs(\"oxy_env_list\", args, [\"app\"]);\n return jsonResult({ environments: await envList(ctx, String(args.app)) });\n }\n\n case \"oxy_env_show\": {\n requireArgs(\"oxy_env_show\", args, [\"app\", \"name\"]);\n return jsonResult(await envShow(ctx, String(args.app), String(args.name)));\n }\n\n case \"oxy_env_delete\": {\n requireArgs(\"oxy_env_delete\", args, [\"app\", \"name\"]);\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_env_delete needs confirm=true\",\n \"there is no terminal to ask on here — pass confirm=true once you mean it\"\n );\n }\n const result = await envDelete(ctx, String(args.app), String(args.name), {\n yes: true,\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(result);\n }\n\n case \"oxy_publish_sandbox\": {\n requireArgs(\"oxy_publish_sandbox\", args, [\"appEnv\"]);\n // Client-side, before any build or request — the same grammar `oxyc\n // publish --app-env` validates, and the same refusal of \"production\" /\n // \"staging\": a sandbox publish tool that accepted either would be the\n // promote path this tool exists to foreclose.\n const appEnv = requireSandboxName(String(args.appEnv));\n // `org` is a GLOBAL flag (`resolveIdentity` reads `ctx.flags.org`), not a\n // `PublishFlags` field — a fresh context carries it the way `--org`\n // would on the CLI, without mutating the one `runMcp` was given.\n const org = args.org as string | undefined;\n const publishCtx = org === undefined ? ctx : createContext({ ...ctx.flags, org }, ctx.cwd);\n const outcome = await publish(publishCtx, {\n appEnv,\n dir: args.dir as string | undefined,\n app: args.app as string | undefined\n });\n return jsonResult(outcome.result ?? { bundle_dir: outcome.bundleDir });\n }\n\n case \"oxy_fn_call\": {\n requireArgs(\"oxy_fn_call\", args, [\"app\", \"function\"]);\n const result = await fnCall(ctx, String(args.app), String(args.function), {\n appEnv: args.appEnv as string | undefined,\n data: args.data as string | undefined,\n timeoutSeconds: args.timeoutSeconds === undefined ? 60 : Number(args.timeoutSeconds)\n });\n return jsonResult(result, { isError: !result.ok, code: ExitCode.FAILURE });\n }\n\n case \"oxy_checks_run\": {\n requireArgs(\"oxy_checks_run\", args, [\"app\"]);\n const report = await runChecksCore(ctx, String(args.app), {\n timeoutSeconds: args.timeoutSeconds === undefined ? 300 : Number(args.timeoutSeconds),\n appEnv: args.appEnv as string | undefined\n });\n const failed = report.checks.some((c) => !c.passed);\n return jsonResult(report, { isError: failed, code: ExitCode.CHECK_FAILED });\n }\n\n case \"oxy_invocations_list\": {\n requireArgs(\"oxy_invocations_list\", args, [\"app\"]);\n const invocations = await invocationsList(ctx, String(args.app), {\n appEnv: args.appEnv as string | undefined,\n build: args.build as string | undefined,\n fn: args.function as string | undefined,\n limit: args.limit === undefined ? undefined : Number(args.limit)\n });\n return jsonResult({ invocations });\n }\n\n case \"oxy_invocations_held\": {\n requireArgs(\"oxy_invocations_held\", args, [\"app\", \"invocationId\"]);\n return jsonResult(await invocationsHeld(ctx, String(args.app), String(args.invocationId)));\n }\n\n case \"oxy_logs\": {\n requireArgs(\"oxy_logs\", args, [\"app\"]);\n const logs = await fetchLogs(ctx, String(args.app), {\n appEnv: args.appEnv as string | undefined,\n invocation: args.invocation as string | undefined,\n request: args.request as string | undefined,\n hours: args.hours === undefined ? undefined : Number(args.hours),\n limit: args.limit === undefined ? undefined : Number(args.limit)\n });\n return jsonResult({ logs });\n }\n\n // ── sandbox agent token only (`mcp-sandbox.ts`) ─────────────────────────\n\n case \"oxy_env_secret_list\": {\n requireArgs(name, args, [\"app\", \"appEnv\"]);\n return jsonResult(await secretList(ctx, String(args.app), String(args.appEnv)));\n }\n\n case \"oxy_env_secret_set\": {\n requireArgs(name, args, [\"app\", \"appEnv\", \"key\", \"value\"]);\n return jsonResult(\n await secretSet(\n ctx,\n String(args.app),\n String(args.appEnv),\n String(args.key),\n String(args.value)\n )\n );\n }\n\n case \"oxy_env_secret_delete\": {\n requireArgs(name, args, [\"app\", \"appEnv\", \"key\"]);\n return jsonResult(\n await secretDelete(ctx, String(args.app), String(args.appEnv), String(args.key))\n );\n }\n\n case \"oxy_token_revoke\": {\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_token_revoke needs confirm=true\",\n \"it ends this token for good — pass confirm=true once the sandbox is deleted and the task is done\"\n );\n }\n const target = ctx.target();\n const outcome = await revokeCallingToken(target, await ctx.bearer());\n forgetSandboxTokens();\n if (outcome === \"revoked\" || outcome === \"already_invalid\") {\n return jsonResult({ revoked: true, already: outcome === \"already_invalid\" });\n }\n throw new CliError(`${target} did not confirm the revoke`, {\n code: ExitCode.UNAVAILABLE,\n hint: \"call oxy_token_revoke once more; the token also ends on its own at its expiry\"\n });\n }\n\n // ── workspace previews ──────────────────────────────────────────────────\n\n case \"oxy_preview_create\": {\n requireArgs(\"oxy_preview_create\", args, [\"branch\"]);\n const item = await previewCreate(ctx, String(args.branch), {\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(item, { isError: previewFailed(item), code: ExitCode.FAILURE });\n }\n\n case \"oxy_preview_list\":\n return jsonResult({ items: await previewList(ctx) });\n\n case \"oxy_preview_show\": {\n requireArgs(\"oxy_preview_show\", args, [\"branch\"]);\n return jsonResult(await previewShow(ctx, String(args.branch)));\n }\n\n case \"oxy_preview_delete\": {\n requireArgs(\"oxy_preview_delete\", args, [\"branch\"]);\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_preview_delete needs confirm=true\",\n \"there is no terminal to ask on here — pass confirm=true once you mean it\"\n );\n }\n return jsonResult(await previewDelete(ctx, String(args.branch), { yes: true }));\n }\n\n case \"oxy_preview_checks\": {\n requireArgs(\"oxy_preview_checks\", args, [\"branch\"]);\n return jsonResult(await previewChecks(ctx, String(args.branch)));\n }\n\n case \"oxy_preview_run\": {\n requireArgs(\"oxy_preview_run\", args, [\"branch\", \"kind\", \"ref\"]);\n const kind = requireRunKind(String(args.kind));\n const windowFrom = args.windowFrom as string | undefined;\n const windowTo = args.windowTo as string | undefined;\n const submitted = await previewSubmitRun(ctx, {\n branch: String(args.branch),\n kind,\n ref: String(args.ref),\n variables: parseVariables(args.variables as string | undefined),\n readLiveOnly: args.readLiveOnly as boolean | undefined,\n window:\n windowFrom === undefined && windowTo === undefined\n ? undefined\n : { from: windowFrom, to: windowTo },\n resources: (args.resources as string[] | undefined) ?? []\n });\n const waitSeconds = args.waitSeconds === undefined ? undefined : Number(args.waitSeconds);\n if (waitSeconds === undefined) return jsonResult(submitted);\n const detail = await previewRunGet(ctx, submitted.run_id, { waitSeconds });\n return jsonResult(detail, { isError: runFailed(detail), code: ExitCode.FAILURE });\n }\n\n case \"oxy_preview_runs_list\": {\n requireArgs(\"oxy_preview_runs_list\", args, [\"branch\"]);\n return jsonResult({ runs: await previewRunsList(ctx, String(args.branch)) });\n }\n\n case \"oxy_preview_run_show\": {\n requireArgs(\"oxy_preview_run_show\", args, [\"runId\"]);\n const detail = await previewRunGet(ctx, String(args.runId), {\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(detail, { isError: runFailed(detail), code: ExitCode.FAILURE });\n }\n\n default:\n return text(`unknown tool: ${name}`, true);\n }\n}\n\n/**\n * Serve MCP on stdio until the client disconnects.\n *\n * STDIO IS THE WHOLE TRANSPORT, deliberately. An HTTP/SSE server would need a\n * port, a lifetime and an auth story of its own; stdio is what every agent\n * runtime already launches, and the process inherits the caller's environment\n * — which is where the token and the target come from.\n *\n * NOTHING MAY BE WRITTEN TO STDOUT except protocol frames. `log.info` and\n * friends already go to stderr, which is why this can share the CLI's own\n * plumbing without corrupting the stream.\n */\nexport async function runMcp(ctx: Context): Promise<void> {\n // Every request from here on is a tool call's, and its user agent says so\n // (`… mcp`) — the startup check below included.\n markMcpSession();\n\n // BEFORE THE TRANSPORT IS UP. A missing or dead credential ends the process\n // with exit 4 here, where an agent runtime reports \"the server failed to\n // start\" — rather than a session whose every tool fails, or one that runs\n // on a credential nobody chose (`startupToolSet`).\n const set = await startupToolSet(ctx);\n const tools = toolsFor(set);\n const served = new Set(tools.map((tool) => tool.name));\n\n const server = new Server({ name: \"oxyc\", version: VERSION }, { capabilities: { tools: {} } });\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n // A tool left off the list is not callable by name either: the list is the\n // surface, not a suggestion.\n if (!served.has(req.params.name)) return text(notServed(req.params.name, set), true);\n try {\n return await callTool(\n ctx,\n req.params.name,\n (req.params.arguments ?? {}) as Record<string, unknown>\n );\n } catch (cause) {\n // A throw here would kill the session. The model can act on a message;\n // it cannot act on a dead transport.\n const message = (cause as Error).message ?? String(cause);\n // BOTH CHANNELS. A model gets one string, so there is no marker to\n // distinguish them — but reading `hint` alone drops the one line that\n // says what to DO, which is the half a model can act on.\n const { hint, remedy } = cause as { hint?: string; remedy?: string };\n // Blank line between them, the way every other renderer of these two\n // separates them — a model given two consecutive imperatives with no\n // boundary loses the distinction the second field exists to make.\n // `!== undefined` for the narrowing TS gives it, and a length check\n // because an empty string would open the block with a blank line —\n // `filter(Boolean)` covered that and cost the narrowing, so both.\n const extra = [hint, remedy].filter((line) => line !== undefined && line !== \"\").join(\"\\n\\n\");\n // THE EXIT-CODE CLASS, APPENDED — not substituted for the message, and\n // not only on the sandbox/preview tools: every CliError carries a code,\n // so every tool's failure now lets a caller branch the way it would on\n // `oxyc`'s own exit code, by NAME rather than a digit it has to look up.\n // The server's own `code` field (previews, sandboxes) rides along when\n // the response carried one.\n const codeLine =\n cause instanceof CliError\n ? `[exit ${cause.code} ${exitCodeName(cause.code)}]` +\n (cause.serverCode ? ` server code: ${cause.serverCode}` : \"\")\n : undefined;\n const body = [message, extra, codeLine]\n .filter((s) => s !== undefined && s !== \"\")\n .join(\"\\n\\n\");\n return text(body, true);\n }\n });\n\n await server.connect(new StdioServerTransport());\n\n // `connect` returns once the transport is wired; the process must stay up\n // until the client closes stdin.\n await new Promise<void>((resolve) => {\n process.stdin.on(\"close\", resolve);\n server.onclose = resolve;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp-Bdp0rX0l.mjs","names":[],"sources":["../src/commands/mcp-sandbox.ts","../src/commands/mcp.ts"],"sourcesContent":["/**\n * What `oxyc mcp` serves when its credential is a sandbox agent token\n * (`oxy_sbx_…`): the sandbox loop, `oxy_whoami`, and four tools that exist\n * only here.\n *\n * A SMALLER LIST, NOT A FILTERED ONE. The generic tools — `oxy_request`,\n * `oxy_routes`, `oxy_schema` — and the workspace-preview tools are dropped:\n * the server answers this token `404` on everything they reach, and a tool\n * that can only fail still costs its schema on every turn. What replaces\n * `oxy_request` is named: a sandbox's secret is set with `oxy_env_secret_set`,\n * which can carry the one rule a generic request cannot — a `dev-<handle>`\n * environment, and nothing else.\n *\n * Tool DEFINITIONS and the startup decision live here. The calls themselves\n * are cases in `mcp.ts`'s `callTool`, beside the verbs they share code with.\n */\n\nimport { describeSandboxToken } from \"../apps/sandbox-token.js\";\nimport { isSandboxAgentToken } from \"../auth/token-kind.js\";\nimport { type Context, envOnly, notAuthenticated } from \"../context/resolve.js\";\nimport * as log from \"../ui/log.js\";\nimport { CliError, ExitCode } from \"../util/errors.js\";\n\nexport interface ToolDef {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n };\n}\n\n/** Which list this session serves. Decided once, at startup, from the credential. */\nexport type ToolSet = \"default\" | \"sandbox_agent\";\n\nconst APP = { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' };\nconst OWN_SANDBOX = {\n type: \"string\",\n description: \"dev-<handle> — a sandbox this token created. Production and staging are refused.\"\n};\n\n/** The sandbox tools whose `appEnv` is optional for staff and required here. */\nconst NEEDS_SANDBOX = new Set([\n \"oxy_fn_call\",\n \"oxy_checks_run\",\n \"oxy_invocations_list\",\n \"oxy_logs\"\n]);\n\nconst WHOAMI: ToolDef = {\n name: \"oxy_whoami\",\n description:\n \"What this sandbox agent token is: its kind, when it expires, the apps it reaches and who \" +\n \"minted it. Call it FIRST — confirm the app you were asked to work on is among `apps` and \" +\n \"that `expires_at` is later than the task will take; otherwise stop and ask your operator \" +\n \"for a new token. Call it again when a tool fails with [exit 4 AUTH].\",\n inputSchema: { type: \"object\", properties: {} }\n};\n\n/** The four tools only a sandbox agent token is served. */\nexport const SANDBOX_AGENT_TOOLS: ToolDef[] = [\n {\n name: \"oxy_env_secret_list\",\n description:\n \"List a sandbox's secret KEYS and flags — is_set, required, inherits_staging — never a \" +\n \"value. `missing_required` above zero means the build declares a secret nothing provides. \" +\n \"A sandbox reads its own value first, then staging's.\",\n inputSchema: {\n type: \"object\",\n properties: { app: APP, appEnv: OWN_SANDBOX },\n required: [\"app\", \"appEnv\"]\n }\n },\n {\n name: \"oxy_env_secret_set\",\n description:\n \"Set one secret in a sandbox — a third party's SANDBOX key, say, so a function stops \" +\n \"reading staging's value. Accepts only a dev-<handle> appEnv. Write-only: no tool returns \" +\n \"a value. A value shaped like an Oxy credential is refused by the server.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: APP,\n appEnv: OWN_SANDBOX,\n key: { type: \"string\", description: \"The secret's name, e.g. STRIPE_SECRET_KEY.\" },\n value: { type: \"string\", description: \"The value to store.\" }\n },\n required: [\"app\", \"appEnv\", \"key\", \"value\"]\n }\n },\n {\n name: \"oxy_env_secret_delete\",\n description:\n \"Delete a sandbox's own value of a secret. Reads of it then fall back to staging's. \" +\n \"Accepts only a dev-<handle> appEnv.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: APP,\n appEnv: OWN_SANDBOX,\n key: { type: \"string\", description: \"The secret's name.\" }\n },\n required: [\"app\", \"appEnv\", \"key\"]\n }\n },\n {\n name: \"oxy_token_revoke\",\n description:\n \"END THIS TOKEN — the last step of a task, after oxy_env_delete. It cannot be undone: \" +\n \"every later tool call fails with [exit 4 AUTH]. Requires confirm=true.\",\n inputSchema: {\n type: \"object\",\n properties: {\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" }\n },\n required: [\"confirm\"]\n }\n }\n];\n\n/** A sandbox tool as this credential must call it: the environment is not optional. */\nfunction forSandboxAgent(tool: ToolDef): ToolDef {\n if (tool.name === \"oxy_env_show\") {\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: { ...tool.inputSchema.properties, name: OWN_SANDBOX }\n }\n };\n }\n if (!NEEDS_SANDBOX.has(tool.name)) return tool;\n const required = tool.inputSchema.required ?? [];\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: { ...tool.inputSchema.properties, appEnv: OWN_SANDBOX },\n required: required.includes(\"appEnv\") ? required : [...required, \"appEnv\"]\n }\n };\n}\n\n/** The list for a sandbox agent token, built from the loop's own definitions. */\nexport function sandboxAgentTools(sandboxLoop: ToolDef[]): ToolDef[] {\n return [WHOAMI, ...sandboxLoop.map(forSandboxAgent), ...SANDBOX_AGENT_TOOLS];\n}\n\n/**\n * Decide the tool list, and FAIL CLOSED before serving one.\n *\n * `oxyc mcp` is what an agent runtime starts, with the agent's own token in\n * its environment. Started without `--login`, an unset variable is exit `4`\n * right here: falling back to the login cache would hand the agent its\n * operator's whole reach, silently, for the life of the session. An\n * `OXY_API_KEY` the caller also set is a credential it named, and is let by.\n *\n * A sandbox agent token is then asked what it is (`GET /api/auth/token`).\n * Refused there, it is dead and the session never starts. Unreachable, the\n * prefix still picks the smaller list — the safe direction to be wrong in.\n */\nexport async function startupToolSet(ctx: Context): Promise<ToolSet> {\n const bearer = ctx.storedBearer();\n if (envOnly(ctx.flags) && !bearer && !ctx.apiKey()) {\n throw notAuthenticated(ctx.target(), ctx.flags);\n }\n if (!isSandboxAgentToken(bearer)) return \"default\";\n\n try {\n await describeSandboxToken(ctx.target(), bearer);\n } catch (cause) {\n if (!(cause instanceof CliError) || cause.code === ExitCode.AUTH) throw cause;\n log.warn(`could not confirm the sandbox agent token with ${ctx.target()}: ${cause.message}`);\n log.hint(\"serving the sandbox tools anyway — each call will report what it meets\");\n }\n return \"sandbox_agent\";\n}\n\n/** A tool the session does not serve, called anyway. */\nexport function notServed(name: string, set: ToolSet): string {\n return set === \"sandbox_agent\"\n ? `${name} is not served to a sandbox agent token. It does the sandbox loop with the tools listed, and nothing else.`\n : `unknown tool: ${name}`;\n}\n","/**\n * `oxyc mcp` — the Oxy API as MCP tools, over stdio.\n *\n * NOT THE SAME THING AS `oxy mcp`. That one is workspace tooling: it takes a\n * local checkout and exposes the semantic model, automations and `.sql` files\n * in it. This one takes a TOKEN and exposes the deployment's HTTP API — the\n * same surface `oxyc api` reaches, for an agent that would rather call a tool\n * than shell out. Different input, different audience, no overlap.\n *\n * FOUR GENERIC TOOLS FOR THE WHOLE API — the design decision that makes most\n * of it affordable.\n *\n * The obvious shape is one tool per endpoint, and it is the wrong one: the API\n * has ~670 routes, an agent runtime ships every tool's JSON schema in every\n * request, and that is tens of kilobytes of context spent on each turn before\n * a single question is asked. It would also go stale on every deploy, since\n * the tool list would be baked into this package rather than read from the\n * deployment.\n *\n * So discovery stays a QUESTION the agent asks (`oxy_routes`, `oxy_schema`)\n * rather than a payload it carries, exactly as the CLI does it, for\n * everything a generic request can do safely.\n *\n * TWO NAMED EXCEPTIONS: `SANDBOX_TOOLS` and `PREVIEW_TOOLS` below. The sandbox\n * loop and the workspace-previews loop are meant to run UNSUPERVISED — create\n * a sandbox, publish into it, call a function, tear it down — and that needs\n * per-operation validation (the `dev-<handle>` grammar, a confirm gate before\n * a delete, a hard refusal of `--app-env` outside `dev-<handle>` on the\n * publish tool) that `oxy_request` has no way to carry. Each of those tools\n * calls its CLI verb's own request-building function (`env.ts`, `fn.ts`,\n * `checks.ts`, `preview.ts`, `preview-runs.ts`, …), so there is exactly one\n * implementation of each request, and a tool's result is the same JSON\n * document that verb's `--json` prints. `mcp.test.ts` pins the full tool list\n * and a raised (and justified) per-turn schema budget.\n *\n * Target resolution and placeholder substitution are the CLI's — the same\n * `Context`, so `{org}` / `{workspace}` resolve the same way.\n *\n * THE CREDENTIAL IS THE TOKEN VARIABLE AND NOTHING ELSE, unless the server is\n * started with `--login`. An agent runtime starts this with the agent's own\n * token in `OXY_TOKEN`; with the variable unset, the process exits `4` rather\n * than serving on whatever `oxyc login` cached for the machine's owner. A\n * person who does want their own login passes `--login`.\n *\n * A SANDBOX AGENT TOKEN (`oxy_sbx_…`) GETS A DIFFERENT, SMALLER LIST — the\n * sandbox loop, `oxy_whoami`, three secret tools and `oxy_token_revoke`; no\n * `oxy_request`, no discovery, no previews. See `mcp-sandbox.ts`.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { loadCatalog, loadOpenApi, searchRoutes } from \"../api/catalog.js\";\nimport { paramsToQuery, parseFields } from \"../api/fields.js\";\nimport { runJq } from \"../api/output.js\";\nimport { isExternalSurface, normalizePath, substitutePlaceholders } from \"../api/paths.js\";\nimport { parseJson, request } from \"../api/request.js\";\nimport { markMcpSession } from \"../api/user-agent.js\";\nimport { requireSandboxName } from \"../apps/environment.js\";\nimport { describeSandboxToken, forgetSandboxTokens } from \"../apps/sandbox-token.js\";\nimport { revokeCallingToken } from \"../auth/token-api.js\";\nimport { isSandboxAgentToken } from \"../auth/token-kind.js\";\nimport { type Context, createContext } from \"../context/resolve.js\";\nimport {\n CliError,\n ExitCode,\n type ExitCodeValue,\n exitCodeName,\n usageError\n} from \"../util/errors.js\";\nimport { runChecksCore } from \"./checks.js\";\nimport { comparablePath } from \"./discover.js\";\nimport { envCreate, envDelete, envList, envShow } from \"./env.js\";\nimport { secretDelete, secretList, secretSet } from \"./env-secrets.js\";\nimport { fnCall } from \"./fn.js\";\nimport { invocationsHeld, invocationsList } from \"./invocations.js\";\nimport { fetchLogs } from \"./logs.js\";\nimport {\n notServed,\n sandboxAgentTools,\n startupToolSet,\n type ToolDef,\n type ToolSet\n} from \"./mcp-sandbox.js\";\nimport {\n previewChecks,\n previewCreate,\n previewDelete,\n previewFailed,\n previewList,\n previewShow\n} from \"./preview.js\";\nimport {\n parseVariables,\n previewRunGet,\n previewRunsList,\n previewSubmitRun,\n requireRunKind,\n runFailed\n} from \"./preview-runs.js\";\nimport { publish } from \"./publish.js\";\n\n/**\n * Package version, reported in the MCP handshake.\n *\n * Read from `package.json` rather than restated, so a release bump cannot\n * leave the server announcing a version nobody shipped.\n */\nconst VERSION: string = (() => {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n for (const candidate of [\n resolve(here, \"..\", \"package.json\"),\n resolve(here, \"..\", \"..\", \"package.json\")\n ]) {\n if (existsSync(candidate)) {\n return (\n (JSON.parse(readFileSync(candidate, \"utf8\")) as { version?: string }).version ?? \"0.0.0\"\n );\n }\n }\n } catch {\n // A packaging shape we did not anticipate. The handshake needs a string,\n // not an accurate one.\n }\n return \"0.0.0\";\n})();\n\n/**\n * A tool result. MCP wants content blocks; everything here is text, because\n * the caller is a language model and the payloads are JSON or markdown.\n */\nfunction text(body: string, isError = false) {\n return { content: [{ type: \"text\" as const, text: body }], isError };\n}\n\n/**\n * A tool result carrying one JSON document — the same shape the matching\n * verb's `--json` prints, so a tool's result and the CLI's output never\n * drift apart. On failure the exit-code class is appended, same spelling as\n * the generic catch-all below (`[exit N NAME]`), so an agent branches on it\n * the way it would on `oxyc`'s own exit code.\n */\nfunction jsonResult(data: unknown, opts: { isError?: boolean; code?: ExitCodeValue } = {}) {\n const body = JSON.stringify(data);\n if (!opts.isError) return text(body);\n const code = opts.code ?? ExitCode.FAILURE;\n return text(`${body}\\n\\n[exit ${code} ${exitCodeName(code)}]`, true);\n}\n\nconst TOOLS = [\n {\n name: \"oxy_routes\",\n description:\n \"List the API endpoints this Oxy deployment mounts, with what each one does. \" +\n \"ALWAYS call this before oxy_request rather than guessing a path. \" +\n \"Pass a filter (matched against method, path, surface and description) to narrow it — \" +\n \"unfiltered is ~670 routes. Above 60 matches the descriptions are dropped and only \" +\n \"method, path and credential come back, so narrow enough to stay under that when you \" +\n \"need to know what an endpoint does; past ~400 it is refused outright.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filter: {\n type: \"string\",\n description: \"Substring to narrow by, e.g. 'threads', 'sql', 'admin', 'semantic'.\"\n },\n all: {\n type: \"boolean\",\n description:\n \"Include ide-only and worker-only routes. Off by default: those are mounted on one \" +\n \"instance of the fleet, so a caller hitting the load balancer cannot reach them directly.\"\n }\n }\n }\n },\n {\n name: \"oxy_schema\",\n description:\n \"The request and response schema for ONE endpoint, so a body can be built correctly. \" +\n \"Covers the data plane (SQL, semantic query, and the org/workspace lookups); a blank \" +\n \"result means undocumented, not nonexistent — use oxy_routes to confirm the endpoint exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Endpoint path, e.g. '{workspace}/sql/query'.\" },\n method: { type: \"string\", description: \"Narrow to one HTTP method.\" }\n },\n required: [\"path\"]\n }\n },\n {\n name: \"oxy_request\",\n description:\n \"Make an authenticated request to the Oxy API. The credential is picked from the path \" +\n \"(/api/** takes a bearer, /external/api/** an API key). Placeholders {org}, {workspace}, \" +\n \"{project}, {customer} and {me} are substituted from context. \" +\n \"Read freely; ask the human before any mutating request against production.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Path relative to /api, e.g. '{org}/workspaces'.\" },\n method: {\n type: \"string\",\n description: \"HTTP method. Defaults to GET, or POST with fields.\"\n },\n fields: {\n type: \"object\",\n description:\n \"Request parameters. Sent as a JSON body on POST/PUT/PATCH, or as query parameters \" +\n \"on GET/HEAD/DELETE. Values keep their JSON type.\"\n },\n jq: {\n type: \"string\",\n description:\n \"A jq program to reduce the response before it is returned. Use it — a full list \" +\n \"response is far more context than the two fields you need.\"\n }\n },\n required: [\"path\"]\n }\n },\n {\n name: \"oxy_whoami\",\n description:\n \"Which deployment this is pointed at and who the token belongs to. \" +\n \"Use it when a call returns 401/403, or a 200 with a null body — on this API an expired \" +\n \"session answers 200 null rather than 401, and this tells the two apart.\",\n inputSchema: { type: \"object\", properties: {} }\n }\n];\n\n/**\n * THE SANDBOX LOOP, AS TOOLS — the deliberate exception to \"four, not one per\n * endpoint\" above. A generic `oxy_request` can call these same routes, but it\n * cannot carry the per-operation validation that makes the loop safe for an\n * agent to drive unsupervised: the dev-<handle> grammar, the confirm-before-\n * delete gate, and the hard refusal of a sandbox publish outside dev-<handle>.\n * Every one of these reuses its CLI verb's own request-building function —\n * see `env.ts` / `fn.ts` / `checks.ts` / `invocations.ts` / `logs.ts` /\n * `publish.ts` — so there is exactly one implementation of each request, and\n * the tool result is the same JSON document that verb's `--json` prints.\n *\n * Each description says what it returns AND what to call next, so the loop\n * reads off the tool list alone: env_create → publish_sandbox → fn_call /\n * checks_run → invocations_list / invocations_held / logs → iterate from\n * publish_sandbox → env_delete.\n */\nconst SANDBOX_TOOLS = [\n {\n name: \"oxy_env_create\",\n description:\n \"Create a dev-<handle> SANDBOX of a custom app — its own build pointer, storage silo, \" +\n \"secrets and Airhouse sibling, isolated from production and from every other sandbox. \" +\n \"Starts with no build. Returns the Environment (status, build, url). Call \" +\n \"oxy_publish_sandbox next to give it one.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: {\n type: \"string\",\n description: \"dev-<handle> — 1-12 lowercase letters, digits and single hyphens.\"\n }\n },\n required: [\"app\", \"name\"]\n }\n },\n {\n name: \"oxy_env_list\",\n description:\n \"List a custom app's environments — production, staging and every dev-<handle> sandbox \" +\n \"— each with its status and build. Use it to find a sandbox's name before oxy_env_show, \" +\n \"oxy_publish_sandbox or oxy_env_delete.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_env_show\",\n description:\n \"One environment's detail — status, build, url. Call it after oxy_publish_sandbox to \" +\n \"confirm a build landed, or before oxy_fn_call to confirm one exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: { type: \"string\", description: \"production, staging, or dev-<handle>.\" }\n },\n required: [\"app\", \"name\"]\n }\n },\n {\n name: \"oxy_env_delete\",\n description:\n \"DESTRUCTIVE — tears down a sandbox's storage silo, secrets and Airhouse sibling for \" +\n \"good. Requires confirm=true: there is no terminal to ask on here, so the delete refuses \" +\n \"without it, the same way `oxyc env delete` refuses without --yes off a TTY. Set \" +\n \"waitSeconds to avoid polling oxy_env_show yourself.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n name: {\n type: \"string\",\n description: \"dev-<handle> — production and staging can never be deleted.\"\n },\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" },\n waitSeconds: {\n type: \"number\",\n description: \"Block until torn down, or this many seconds pass (no wait by default).\"\n }\n },\n required: [\"app\", \"name\", \"confirm\"]\n }\n },\n {\n name: \"oxy_publish_sandbox\",\n description:\n \"DESTRUCTIVE (to the sandbox's build, not production) — build the app in `dir` (default: \" +\n \"the current directory) and publish it to a dev-<handle> sandbox's build pointer. \" +\n \"REQUIRES a dev-<handle> appEnv and refuses production, staging, or any attempt to \" +\n \"promote — a sandbox build is never promoted; publish the same tree to staging and \" +\n \"promote that instead, outside this tool. Runs the app's own build command, so it \" +\n \"executes whatever package scripts oxy-app.json declares. Returns the publish result \" +\n \"(build id, url). Call oxy_fn_call or oxy_checks_run with the same appEnv next.\",\n inputSchema: {\n type: \"object\",\n properties: {\n appEnv: {\n type: \"string\",\n description: \"dev-<handle> — anything else (production, staging) is refused.\"\n },\n dir: { type: \"string\", description: \"App directory to publish (default: cwd).\" },\n app: {\n type: \"string\",\n description: \"App slug (default: oxy-app.json's slug, or OXY_APP).\"\n },\n org: {\n type: \"string\",\n description: \"Org slug (default: oxy-app.json's orgSlug, or OXY_ORG).\"\n }\n },\n required: [\"appEnv\"]\n }\n },\n {\n name: \"oxy_fn_call\",\n description:\n \"Call a custom app's Oxy Function directly (POST .../fn/<function>) and return its \" +\n \"result: {ok, status, body, logs, invocationId, error}. isError is set when the \" +\n \"FUNCTION failed (distinct from a transport failure, which throws). Needs a build in \" +\n \"that environment — oxy_publish_sandbox or oxy_env_show confirms one exists. Call \" +\n \"oxy_logs or oxy_invocations_held with the returned invocationId to see why it failed.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n function: { type: \"string\", description: \"The Oxy Function's name.\" },\n appEnv: { type: \"string\", description: \"production (default), staging, or dev-<handle>.\" },\n data: { type: \"string\", description: 'JSON request body (default \"{}\").' },\n timeoutSeconds: { type: \"number\", description: \"Client-side timeout (default 60).\" }\n },\n required: [\"app\", \"function\"]\n }\n },\n {\n name: \"oxy_checks_run\",\n description:\n 'Run every function the app marked \"check\": true in one environment, and wait for each ' +\n \"to a terminal status. Returns {app, checks:[{name, status, passed, error, durationMs}]}; \" +\n \"isError is set when any check failed or timed out. Run this right after \" +\n \"oxy_publish_sandbox to verify a build.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: { type: \"string\", description: \"production (default), staging, or dev-<handle>.\" },\n timeoutSeconds: { type: \"number\", description: \"Per-check timeout (default 300).\" }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_invocations_list\",\n description:\n \"List what ran in an app's environment — every function invocation, newest first, with \" +\n \"its status. Use it to confirm oxy_fn_call or oxy_checks_run reached the sandbox, or to \" +\n \"find an invocation id for oxy_invocations_held.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: { type: \"string\", description: \"Narrow to one environment.\" },\n build: { type: \"string\", description: \"Narrow to one build (its id, or the build UUID).\" },\n function: { type: \"string\", description: \"Narrow to one function.\" },\n limit: { type: \"number\", description: \"Max rows (default 50).\" }\n },\n required: [\"app\"]\n }\n },\n {\n name: \"oxy_invocations_held\",\n description:\n \"What a non-production invocation's write policy HELD instead of performing — \" +\n \"production's would-be effect, recorded rather than run. Empty for a production \" +\n \"invocation, since nothing is held there. Call with an id from oxy_invocations_list or \" +\n \"oxy_fn_call's invocationId.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n invocationId: { type: \"string\", description: \"From oxy_invocations_list or oxy_fn_call.\" }\n },\n required: [\"app\", \"invocationId\"]\n }\n },\n {\n name: \"oxy_logs\",\n description:\n \"An app's persisted ctx.log() / console.* output, newest-relevant window first. Use it \" +\n \"to debug a function that oxy_fn_call or oxy_checks_run reported as failed.\",\n inputSchema: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: '\"<org-slug>/<app-slug>\" or the app UUID.' },\n appEnv: {\n type: \"string\",\n description: \"Narrow to one environment (default: production only).\"\n },\n invocation: { type: \"string\", description: \"Narrow to one invocation id.\" },\n request: { type: \"string\", description: \"Narrow to one request id.\" },\n hours: { type: \"number\", description: \"Window size in hours (default 24, max 168).\" },\n limit: { type: \"number\", description: \"Max rows (default 100, max 500).\" }\n },\n required: [\"app\"]\n }\n }\n];\n\n/**\n * WORKSPACE PREVIEWS, AS TOOLS — the same exception as `SANDBOX_TOOLS` above,\n * for the same reason: the run-kind grammar and the create/wait/timeout\n * contract are per-operation validation a generic `oxy_request` cannot carry.\n * Each reuses its `oxyc preview` verb's own request-building function (see\n * `preview.ts` / `preview-runs.ts`), so there is one implementation of each\n * request and the tool result matches that verb's `--json` output.\n *\n * The loop: preview_create (waits for the compile) → preview_checks → \" +\n * preview_run (procedure or airway_sample) → preview_run_show (waits for the \" +\n * run) → preview_runs_list to see transform_build/compare runs the server \" +\n * queued on its own → preview_delete when done.\n */\nconst PREVIEW_TOOLS = [\n {\n name: \"oxy_preview_create\",\n description:\n \"Preview a workspace branch: compile its head into a staging revision (or reuse the \" +\n \"ready one for that commit) and start serving it under x-oxy-preview-revision. Returns \" +\n \"the PreviewItem ({branch, status, revision_id, checks, ...}) — idempotent, so calling it \" +\n \"again on the same commit is free. Set waitSeconds to avoid polling oxy_preview_show \" +\n \"yourself; isError is then set on a compile that failed. Call oxy_preview_checks next.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The branch to preview. Not the default branch.\" },\n waitSeconds: {\n type: \"number\",\n description:\n \"Block until the compile is ready or failed, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_list\",\n description:\n \"List every branch staff are previewing in this workspace, most recently touched first, \" +\n \"each with its compile status and checks summary. Use it to find a branch name before \" +\n \"the other preview tools.\",\n inputSchema: { type: \"object\", properties: {} }\n },\n {\n name: \"oxy_preview_show\",\n description:\n \"One branch's preview — status and checks summary (PreviewItem). Not a server route: \" +\n \"filters oxy_preview_list's result by branch, and fails NOT_FOUND when the branch has \" +\n \"no preview.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_delete\",\n description:\n \"DESTRUCTIVE — stop previewing a branch: cancels its queued runs and releases its \" +\n \"staging revision. Requires confirm=true (there is no terminal to ask on here).\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The previewed branch to stop previewing.\" },\n confirm: { type: \"boolean\", description: \"Must be true, or the call is refused.\" }\n },\n required: [\"branch\", \"confirm\"]\n }\n },\n {\n name: \"oxy_preview_checks\",\n description:\n \"The Airway change check of a preview's current revision: each changed .airway.yml \" +\n \"pipeline and whether merging it needs a Reset schema, and each changed automation \" +\n '(\"auto\" transforms are built in the preview and compared with live; \"manual\" ones say ' +\n 'why not). \"pending\" with no pipelines until the check has run — call oxy_preview_show ' +\n \"or retry shortly.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_run\",\n description:\n \"Start a held dry run of a previewed branch: a procedure (every write held or, on the \" +\n \"managed Airhouse, redirected into the preview's own schemas) or an airway_sample (a \" +\n \"bounded window of a pipeline into the preview's schemas). transform_build and compare \" +\n \"runs are queued by the server's own change check and cannot be started here — read \" +\n \"them back with oxy_preview_runs_list / oxy_preview_run_show instead. Returns \" +\n \"{run_id, state}. Set waitSeconds to avoid polling oxy_preview_run_show yourself; isError \" +\n \"is then set if the run failed or was cancelled. Needs OXY_PREVIEW_RUNS enabled on the \" +\n \"deployment.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"The previewed branch to run against.\" },\n kind: { type: \"string\", description: '\"procedure\" or \"airway_sample\".' },\n ref: {\n type: \"string\",\n description: \"The automation path (procedure) or .airway.yml path (airway_sample).\"\n },\n variables: {\n type: \"string\",\n description: \"procedure: JSON object of the automation's variables.\"\n },\n readLiveOnly: {\n type: \"boolean\",\n description: \"procedure: read live tables even where the preview holds a copy.\"\n },\n windowFrom: { type: \"string\", description: \"airway_sample: window start, RFC 3339.\" },\n windowTo: { type: \"string\", description: \"airway_sample: window end, RFC 3339.\" },\n resources: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"airway_sample: the resources to read, for a multi-resource source.\"\n },\n waitSeconds: {\n type: \"number\",\n description: \"Block until the run finishes, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"branch\", \"kind\", \"ref\"]\n }\n },\n {\n name: \"oxy_preview_runs_list\",\n description:\n \"A branch's held runs, newest first — procedure, transform_build, compare and \" +\n \"airway_sample alike, with each one's state and outcome. Use it to find a run_id for \" +\n \"oxy_preview_run_show, including the transform_build/compare runs the server queues on \" +\n \"its own that oxy_preview_run cannot start.\",\n inputSchema: {\n type: \"object\",\n properties: { branch: { type: \"string\", description: \"The previewed branch.\" } },\n required: [\"branch\"]\n }\n },\n {\n name: \"oxy_preview_run_show\",\n description:\n \"One run's detail: its steps (with what each held or redirected), and — for \" +\n \"transform_build/compare — the compare-with-live outcome, or — for airway_sample — the \" +\n \"sample's ask and result. Set waitSeconds to avoid polling yourself; isError is then set \" +\n \"if the run failed or was cancelled.\",\n inputSchema: {\n type: \"object\",\n properties: {\n runId: { type: \"string\", description: \"From oxy_preview_run or oxy_preview_runs_list.\" },\n waitSeconds: {\n type: \"number\",\n description: \"Block until the run finishes, up to this many seconds (no wait by default).\"\n }\n },\n required: [\"runId\"]\n }\n }\n];\n\nconst ALL_TOOLS: ToolDef[] = [...TOOLS, ...SANDBOX_TOOLS, ...PREVIEW_TOOLS];\n\n/** The list a session serves — decided once, from its credential (`mcp-sandbox.ts`). */\nfunction toolsFor(set: ToolSet): ToolDef[] {\n return set === \"sandbox_agent\" ? sandboxAgentTools(SANDBOX_TOOLS) : ALL_TOOLS;\n}\n\n/**\n * Above this many matches, `oxy_routes` drops the descriptions.\n *\n * DEGRADES, IT DOES NOT REFUSE — the same shape as `oxyc routes`, which picks\n * `renderDetailed` under 40 matches and the compact method+path table above it\n * (`discover.ts`). The expensive field is `description`, not the row: a trimmed\n * row is a few tens of bytes, so several hundred still fit in a result an agent\n * can read, while the descriptions are what turn a broad match into hundreds of\n * KB of context spent before a question is asked.\n *\n * The threshold is on the COUNT, not on whether a `filter` was supplied — a\n * filter is not evidence of narrowing. `searchRoutes` matches a substring of\n * method, path, surface or description, so `\"admin\"`, `\"workspace\"` and\n * `\"query\"` are all filters a model reaches for in good faith that clear this.\n */\nconst MAX_DESCRIBED_ROUTES = 60;\n\n/**\n * And above THIS many, there is nothing useful to return at all.\n *\n * Deliberately far above the threshold that drops descriptions: refusing is the\n * answer only when even the trimmed listing would be a context dump, which for\n * a deployment of ~670 routes means \"you asked for effectively all of them\".\n */\nconst MAX_ROUTES_PER_RESULT = 400;\n\n/** `{ a: 1 }` → the `-f`/`-F` pairs `parseFields` understands. */\nfunction fieldPairs(fields: Record<string, unknown> | undefined): string[] {\n if (!fields) return [];\n return Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`);\n}\n\n/**\n * Check the arguments the schema says are required.\n *\n * `inputSchema.required` is ADVISORY on the low-level `Server` — it is handed\n * to the model, not enforced by the transport. Without this, `oxy_schema` with\n * no `path` matched every path and returned the whole OpenAPI document, and\n * `oxy_request` with none built a request to `/api/`.\n */\nfunction requireArgs(name: string, args: Record<string, unknown>, required: string[]): void {\n const missing = required.filter((k) => {\n const v = args[k];\n return v === undefined || v === null || (typeof v === \"string\" && v.trim() === \"\");\n });\n if (missing.length > 0) {\n throw new CliError(`${name} needs ${missing.join(\", \")}`, { code: ExitCode.USAGE });\n }\n}\n\nasync function callTool(ctx: Context, name: string, args: Record<string, unknown>) {\n switch (name) {\n case \"oxy_routes\": {\n const catalog = await loadCatalog({ target: ctx.target(), bearer: await ctx.maybeBearer() });\n const filter = (args.filter as string | undefined)?.trim();\n let matches = searchRoutes(catalog, filter || undefined);\n if (!args.all) matches = matches.filter((r) => r.role === \"fleet-ok\");\n\n // A cache stale enough to be wrong is worth saying INSIDE the result:\n // `loadCatalog` warns on stderr, which an MCP client never shows anyone.\n const staleNote = catalog.stale\n ? \"NOTE: this route table came from a stale local cache; the deployment was unreachable.\\n\\n\"\n : \"\";\n\n const asked = filter ? ` ${JSON.stringify(filter)}` : \"\";\n\n // Only a match set too large even to LIST is refused. Below that the\n // result degrades instead, because a filter like \"admin\" or \"query\" can\n // clear the description threshold in good faith and \"guess something\n // narrower\" is a dead end for a model with no way to see what it hit.\n if (matches.length > MAX_ROUTES_PER_RESULT) {\n return text(\n `${staleNote}${matches.length} routes match${asked}, which is effectively the whole ` +\n `deployment. Call oxy_routes again with a narrower \\`filter\\` (matched against ` +\n `method, path, surface and description), e.g. \"threads\", \"sql\", \"semantic\", \"admin\".`,\n true\n );\n }\n\n if (matches.length === 0) {\n // An empty array reads as \"this deployment has no such endpoint\",\n // which is usually false — the filter was wrong, or the route is\n // ide-only and hidden by default.\n return text(\n `${staleNote}No route matches ${JSON.stringify(filter ?? \"\")}. Try a broader filter, ` +\n `or pass all=true to include ide-only and worker-only mounts.`,\n true\n );\n }\n // Trimmed to the fields that help a caller build a request. The full\n // record carries the handler path and mount notes, which are for a\n // maintainer reading source, not for an agent composing a call.\n const described = matches.length <= MAX_DESCRIBED_ROUTES;\n // `credential` SURVIVES THE TRIM. It is what separates the bearer mount\n // from the `/external/api` API-key one, so a caller composing a request\n // needs it in either form — and `renderCompact`, the CLI shape this\n // mirrors, keeps it as a per-surface heading. It is also not the\n // expensive field: ~25 bytes a row, ~10 KB across the whole ceiling,\n // against descriptions that run to hundreds of KB.\n const trimmed = matches.map((r) =>\n described\n ? { method: r.method, path: r.path, credential: r.credential, description: r.description }\n : { method: r.method, path: r.path, credential: r.credential }\n );\n // The note says the listing is trimmed AND how to get the descriptions\n // back. Without it a model reads a description-less row as an endpoint\n // that has no documentation, rather than one it did not ask narrowly\n // enough to see.\n const trimNote = described\n ? \"\"\n : `NOTE: ${matches.length} routes match${asked} — too many to describe, so each row is ` +\n `method, path and credential WITHOUT its description. Narrow the \\`filter\\` to ` +\n `${MAX_DESCRIBED_ROUTES} or fewer matches to get the descriptions back.\\n\\n`;\n return text(`${staleNote}${trimNote}${JSON.stringify(trimmed)}`);\n }\n\n case \"oxy_schema\": {\n requireArgs(\"oxy_schema\", args, [\"path\"]);\n const doc = (await loadOpenApi({\n target: ctx.target(),\n bearer: await ctx.maybeBearer()\n })) as {\n paths?: Record<string, Record<string, unknown>>;\n components?: unknown;\n };\n const wanted = typeof args.path === \"string\" ? args.path : \"\";\n const method = args.method as string | undefined;\n // `comparablePath` is imported from `discover.ts` rather than copied:\n // a second reduction of the same two spellings is a second thing to keep\n // in step, and the copy here was untested. Exact first, then substring —\n // the order `runSchema` uses, so the two commands answer alike.\n const paths = Object.entries(doc.paths ?? {});\n const exact = paths.filter(([p]) => comparablePath(p) === comparablePath(wanted));\n const hit =\n exact.length > 0\n ? exact\n : paths.filter(([p]) => comparablePath(p).includes(comparablePath(wanted)));\n if (hit.length === 0) {\n return text(\n `No documented schema for ${wanted}. The OpenAPI document covers the data plane only — ` +\n `call oxy_routes with a filter to confirm the endpoint exists.`,\n true\n );\n }\n const selected = Object.fromEntries(\n hit.map(([p, ops]) => [\n p,\n method\n ? Object.fromEntries(\n Object.entries(ops).filter(([verb]) => verb.toLowerCase() === method.toLowerCase())\n )\n : ops\n ])\n );\n return text(JSON.stringify({ paths: selected, components: doc.components }, null, 2));\n }\n\n case \"oxy_request\": {\n requireArgs(\"oxy_request\", args, [\"path\"]);\n const raw = typeof args.path === \"string\" ? args.path : \"\";\n const method =\n (typeof args.method === \"string\" ? args.method.toUpperCase() : \"\") || undefined;\n const fields = parseFields([], fieldPairs(args.fields as Record<string, unknown>));\n const verb = method ?? (fields.present ? \"POST\" : \"GET\");\n const carriesBody = ![\"GET\", \"HEAD\", \"DELETE\"].includes(verb);\n\n let path = substitutePlaceholders(normalizePath(raw), ctx.placeholders());\n let body: string | undefined;\n if (fields.present) {\n if (carriesBody) body = JSON.stringify(fields.params);\n else {\n const query = paramsToQuery(fields.params);\n if (query) path += (path.includes(\"?\") ? \"&\" : \"?\") + query;\n }\n }\n\n const apiKey = ctx.apiKey();\n const external = isExternalSurface(path);\n const response = await request({\n target: ctx.target(),\n path,\n method: verb,\n body,\n bearer: external && apiKey ? ctx.storedBearer() : await ctx.bearer(),\n apiKey\n });\n\n if (response.status < 200 || response.status >= 300) {\n // The STATUS and the body, not a thrown error: the model can act on\n // \"403, here is what the server said\" and cannot act on a transport\n // exception. It is marked `isError` so the runtime shows it as one.\n return text(`HTTP ${response.status} ${response.statusText}\\n${response.body}`, true);\n }\n\n if (response.body.trim() === \"null\") {\n return text(\n \"null\\n\\n(NOTE: a 200 with a null body can mean an expired session on this API, \" +\n \"not 'no such thing'. Call oxy_whoami to tell them apart.)\"\n );\n }\n\n const jq = args.jq as string | undefined;\n if (jq) return text(runJq(response.body, jq));\n return text(response.body);\n }\n\n case \"oxy_whoami\": {\n const target = ctx.target();\n const bearer = await ctx.bearer();\n // `/api/user` answers a sandbox agent token 404. Its own description is\n // the answer, asked fresh: this tool is what tells a dead token from a\n // live one.\n if (isSandboxAgentToken(bearer)) {\n const described = await describeSandboxToken(target, bearer, { fresh: true });\n return text(JSON.stringify({ target, token: parseJson(described.body) }, null, 2));\n }\n const response = await request({\n target,\n path: \"/api/user\",\n method: \"GET\",\n bearer\n });\n const payload = parseJson(response.body);\n if (payload === null || payload === undefined) {\n return text(\n `target: ${target}\\nThe token no longer resolves to a user — the session has expired. ` +\n `Ask the human to run \\`oxyc login\\`.`,\n true\n );\n }\n return text(JSON.stringify({ target, user: payload }, null, 2));\n }\n\n // ── sandbox loop ───────────────────────────────────────────────────────\n\n case \"oxy_env_create\": {\n requireArgs(\"oxy_env_create\", args, [\"app\", \"name\"]);\n return jsonResult(await envCreate(ctx, String(args.app), String(args.name)));\n }\n\n case \"oxy_env_list\": {\n requireArgs(\"oxy_env_list\", args, [\"app\"]);\n return jsonResult({ environments: await envList(ctx, String(args.app)) });\n }\n\n case \"oxy_env_show\": {\n requireArgs(\"oxy_env_show\", args, [\"app\", \"name\"]);\n return jsonResult(await envShow(ctx, String(args.app), String(args.name)));\n }\n\n case \"oxy_env_delete\": {\n requireArgs(\"oxy_env_delete\", args, [\"app\", \"name\"]);\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_env_delete needs confirm=true\",\n \"there is no terminal to ask on here — pass confirm=true once you mean it\"\n );\n }\n const result = await envDelete(ctx, String(args.app), String(args.name), {\n yes: true,\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(result);\n }\n\n case \"oxy_publish_sandbox\": {\n requireArgs(\"oxy_publish_sandbox\", args, [\"appEnv\"]);\n // Client-side, before any build or request — the same grammar `oxyc\n // publish --app-env` validates, and the same refusal of \"production\" /\n // \"staging\": a sandbox publish tool that accepted either would be the\n // promote path this tool exists to foreclose.\n const appEnv = requireSandboxName(String(args.appEnv));\n // `org` is a GLOBAL flag (`resolveIdentity` reads `ctx.flags.org`), not a\n // `PublishFlags` field — a fresh context carries it the way `--org`\n // would on the CLI, without mutating the one `runMcp` was given.\n const org = args.org as string | undefined;\n const publishCtx = org === undefined ? ctx : createContext({ ...ctx.flags, org }, ctx.cwd);\n const outcome = await publish(publishCtx, {\n appEnv,\n dir: args.dir as string | undefined,\n app: args.app as string | undefined\n });\n return jsonResult(outcome.result ?? { bundle_dir: outcome.bundleDir });\n }\n\n case \"oxy_fn_call\": {\n requireArgs(\"oxy_fn_call\", args, [\"app\", \"function\"]);\n const result = await fnCall(ctx, String(args.app), String(args.function), {\n appEnv: args.appEnv as string | undefined,\n data: args.data as string | undefined,\n timeoutSeconds: args.timeoutSeconds === undefined ? 60 : Number(args.timeoutSeconds)\n });\n return jsonResult(result, { isError: !result.ok, code: ExitCode.FAILURE });\n }\n\n case \"oxy_checks_run\": {\n requireArgs(\"oxy_checks_run\", args, [\"app\"]);\n const report = await runChecksCore(ctx, String(args.app), {\n timeoutSeconds: args.timeoutSeconds === undefined ? 300 : Number(args.timeoutSeconds),\n appEnv: args.appEnv as string | undefined\n });\n const failed = report.checks.some((c) => !c.passed);\n return jsonResult(report, { isError: failed, code: ExitCode.CHECK_FAILED });\n }\n\n case \"oxy_invocations_list\": {\n requireArgs(\"oxy_invocations_list\", args, [\"app\"]);\n const invocations = await invocationsList(ctx, String(args.app), {\n appEnv: args.appEnv as string | undefined,\n build: args.build as string | undefined,\n fn: args.function as string | undefined,\n limit: args.limit === undefined ? undefined : Number(args.limit)\n });\n return jsonResult({ invocations });\n }\n\n case \"oxy_invocations_held\": {\n requireArgs(\"oxy_invocations_held\", args, [\"app\", \"invocationId\"]);\n return jsonResult(await invocationsHeld(ctx, String(args.app), String(args.invocationId)));\n }\n\n case \"oxy_logs\": {\n requireArgs(\"oxy_logs\", args, [\"app\"]);\n const logs = await fetchLogs(ctx, String(args.app), {\n appEnv: args.appEnv as string | undefined,\n invocation: args.invocation as string | undefined,\n request: args.request as string | undefined,\n hours: args.hours === undefined ? undefined : Number(args.hours),\n limit: args.limit === undefined ? undefined : Number(args.limit)\n });\n return jsonResult({ logs });\n }\n\n // ── sandbox agent token only (`mcp-sandbox.ts`) ─────────────────────────\n\n case \"oxy_env_secret_list\": {\n requireArgs(name, args, [\"app\", \"appEnv\"]);\n return jsonResult(await secretList(ctx, String(args.app), String(args.appEnv)));\n }\n\n case \"oxy_env_secret_set\": {\n requireArgs(name, args, [\"app\", \"appEnv\", \"key\", \"value\"]);\n return jsonResult(\n await secretSet(\n ctx,\n String(args.app),\n String(args.appEnv),\n String(args.key),\n String(args.value)\n )\n );\n }\n\n case \"oxy_env_secret_delete\": {\n requireArgs(name, args, [\"app\", \"appEnv\", \"key\"]);\n return jsonResult(\n await secretDelete(ctx, String(args.app), String(args.appEnv), String(args.key))\n );\n }\n\n case \"oxy_token_revoke\": {\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_token_revoke needs confirm=true\",\n \"it ends this token for good — pass confirm=true once the sandbox is deleted and the task is done\"\n );\n }\n const target = ctx.target();\n const outcome = await revokeCallingToken(target, await ctx.bearer());\n forgetSandboxTokens();\n if (outcome === \"revoked\" || outcome === \"already_invalid\") {\n return jsonResult({ revoked: true, already: outcome === \"already_invalid\" });\n }\n throw new CliError(`${target} did not confirm the revoke`, {\n code: ExitCode.UNAVAILABLE,\n hint: \"call oxy_token_revoke once more; the token also ends on its own at its expiry\"\n });\n }\n\n // ── workspace previews ──────────────────────────────────────────────────\n\n case \"oxy_preview_create\": {\n requireArgs(\"oxy_preview_create\", args, [\"branch\"]);\n const item = await previewCreate(ctx, String(args.branch), {\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(item, { isError: previewFailed(item), code: ExitCode.FAILURE });\n }\n\n case \"oxy_preview_list\":\n return jsonResult({ items: await previewList(ctx) });\n\n case \"oxy_preview_show\": {\n requireArgs(\"oxy_preview_show\", args, [\"branch\"]);\n return jsonResult(await previewShow(ctx, String(args.branch)));\n }\n\n case \"oxy_preview_delete\": {\n requireArgs(\"oxy_preview_delete\", args, [\"branch\"]);\n if (args.confirm !== true) {\n throw usageError(\n \"oxy_preview_delete needs confirm=true\",\n \"there is no terminal to ask on here — pass confirm=true once you mean it\"\n );\n }\n return jsonResult(await previewDelete(ctx, String(args.branch), { yes: true }));\n }\n\n case \"oxy_preview_checks\": {\n requireArgs(\"oxy_preview_checks\", args, [\"branch\"]);\n return jsonResult(await previewChecks(ctx, String(args.branch)));\n }\n\n case \"oxy_preview_run\": {\n requireArgs(\"oxy_preview_run\", args, [\"branch\", \"kind\", \"ref\"]);\n const kind = requireRunKind(String(args.kind));\n const windowFrom = args.windowFrom as string | undefined;\n const windowTo = args.windowTo as string | undefined;\n const submitted = await previewSubmitRun(ctx, {\n branch: String(args.branch),\n kind,\n ref: String(args.ref),\n variables: parseVariables(args.variables as string | undefined),\n readLiveOnly: args.readLiveOnly as boolean | undefined,\n window:\n windowFrom === undefined && windowTo === undefined\n ? undefined\n : { from: windowFrom, to: windowTo },\n resources: (args.resources as string[] | undefined) ?? []\n });\n const waitSeconds = args.waitSeconds === undefined ? undefined : Number(args.waitSeconds);\n if (waitSeconds === undefined) return jsonResult(submitted);\n const detail = await previewRunGet(ctx, submitted.run_id, { waitSeconds });\n return jsonResult(detail, { isError: runFailed(detail), code: ExitCode.FAILURE });\n }\n\n case \"oxy_preview_runs_list\": {\n requireArgs(\"oxy_preview_runs_list\", args, [\"branch\"]);\n return jsonResult({ runs: await previewRunsList(ctx, String(args.branch)) });\n }\n\n case \"oxy_preview_run_show\": {\n requireArgs(\"oxy_preview_run_show\", args, [\"runId\"]);\n const detail = await previewRunGet(ctx, String(args.runId), {\n waitSeconds: args.waitSeconds === undefined ? undefined : Number(args.waitSeconds)\n });\n return jsonResult(detail, { isError: runFailed(detail), code: ExitCode.FAILURE });\n }\n\n default:\n return text(`unknown tool: ${name}`, true);\n }\n}\n\n/**\n * Serve MCP on stdio until the client disconnects.\n *\n * STDIO IS THE WHOLE TRANSPORT, deliberately. An HTTP/SSE server would need a\n * port, a lifetime and an auth story of its own; stdio is what every agent\n * runtime already launches, and the process inherits the caller's environment\n * — which is where the token and the target come from.\n *\n * NOTHING MAY BE WRITTEN TO STDOUT except protocol frames. `log.info` and\n * friends already go to stderr, which is why this can share the CLI's own\n * plumbing without corrupting the stream.\n */\nexport async function runMcp(ctx: Context): Promise<void> {\n // Every request from here on is a tool call's, and its user agent says so\n // (`… mcp`) — the startup check below included.\n markMcpSession();\n\n // BEFORE THE TRANSPORT IS UP. A missing or dead credential ends the process\n // with exit 4 here, where an agent runtime reports \"the server failed to\n // start\" — rather than a session whose every tool fails, or one that runs\n // on a credential nobody chose (`startupToolSet`).\n const set = await startupToolSet(ctx);\n const tools = toolsFor(set);\n const served = new Set(tools.map((tool) => tool.name));\n\n const server = new Server({ name: \"oxyc\", version: VERSION }, { capabilities: { tools: {} } });\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n // A tool left off the list is not callable by name either: the list is the\n // surface, not a suggestion.\n if (!served.has(req.params.name)) return text(notServed(req.params.name, set), true);\n try {\n return await callTool(\n ctx,\n req.params.name,\n (req.params.arguments ?? {}) as Record<string, unknown>\n );\n } catch (cause) {\n // A throw here would kill the session. The model can act on a message;\n // it cannot act on a dead transport.\n const message = (cause as Error).message ?? String(cause);\n // BOTH CHANNELS. A model gets one string, so there is no marker to\n // distinguish them — but reading `hint` alone drops the one line that\n // says what to DO, which is the half a model can act on.\n const { hint, remedy } = cause as { hint?: string; remedy?: string };\n // Blank line between them, the way every other renderer of these two\n // separates them — a model given two consecutive imperatives with no\n // boundary loses the distinction the second field exists to make.\n // `!== undefined` for the narrowing TS gives it, and a length check\n // because an empty string would open the block with a blank line —\n // `filter(Boolean)` covered that and cost the narrowing, so both.\n const extra = [hint, remedy].filter((line) => line !== undefined && line !== \"\").join(\"\\n\\n\");\n // THE EXIT-CODE CLASS, APPENDED — not substituted for the message, and\n // not only on the sandbox/preview tools: every CliError carries a code,\n // so every tool's failure now lets a caller branch the way it would on\n // `oxyc`'s own exit code, by NAME rather than a digit it has to look up.\n // The server's own `code` field (previews, sandboxes) rides along when\n // the response carried one.\n const codeLine =\n cause instanceof CliError\n ? `[exit ${cause.code} ${exitCodeName(cause.code)}]` +\n (cause.serverCode ? ` server code: ${cause.serverCode}` : \"\")\n : undefined;\n const body = [message, extra, codeLine]\n .filter((s) => s !== undefined && s !== \"\")\n .join(\"\\n\\n\");\n return text(body, true);\n }\n });\n\n await server.connect(new StdioServerTransport());\n\n // `connect` returns once the transport is wired; the process must stay up\n // until the client closes stdin.\n await new Promise<void>((resolve) => {\n process.stdin.on(\"close\", resolve);\n server.onclose = resolve;\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy-hq/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "oxyc — the Oxy CLI: a gh-api-style client for the Oxy HTTP API, plus the customer-account tooling that scopes a session to one customer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,19 +45,19 @@
|
|
|
45
45
|
"license": "MIT",
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@clack/prompts": "^1.8.1",
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
49
49
|
"ajv": "^8.20.0",
|
|
50
50
|
"ajv-formats": "^3.0.1",
|
|
51
51
|
"commander": "^15.0.0",
|
|
52
52
|
"picocolors": "^1.1.1",
|
|
53
53
|
"yaml": "^2.9.1",
|
|
54
|
-
"zod": "^4.6.
|
|
54
|
+
"zod": "^4.6.6"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^26.6.
|
|
57
|
+
"@types/node": "^26.6.4",
|
|
58
58
|
"tsdown": "^0.23.0",
|
|
59
59
|
"typescript": "^7.0.2",
|
|
60
|
-
"vitest": "^5.0.
|
|
60
|
+
"vitest": "^5.0.3"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=20"
|