@hoststack.dev/mcp 0.10.1 → 0.11.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 +16 -16
- package/dist/hoststack-mcp.js +109 -3
- package/dist/hoststack-mcp.js.map +1 -1
- package/dist/index.js +109 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/server-factory.ts","../src/api-client.ts","../src/prompts/registry.ts","../src/resources/registry.ts","../src/registry.ts","../src/prompts/deploy-prompts.ts","../src/lib/shape.ts","../src/resources/hoststack-resources.ts","../src/tools/activity-log.ts","../src/lib/respond.ts","../src/tools/alerts.ts","../src/tools/cron.ts","../src/tools/databases.ts","../src/tools/deploys.ts","../src/tools/dns-records.ts","../src/tools/domains.ts","../src/tools/env-vars.ts","../src/tools/environments.ts","../src/tools/meta.ts","../src/tools/notifications.ts","../src/tools/projects.ts","../src/tools/services.ts","../src/tools/volumes.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\nimport { createMcpServer, type ToolCallEvent } from './server-factory.js';\n\n// ─── --print-config <client> ─────────────────────────────────────────\n// Generate a client-specific JSON config snippet so the user can copy/paste\n// into Claude Desktop / Cursor without hand-writing the env block. Runs\n// before the API key check so users without a key set can still discover\n// the right config shape.\n\nconst args = process.argv.slice(2);\nconst printIdx = args.indexOf('--print-config');\nif (printIdx !== -1) {\n\tconst target = args[printIdx + 1];\n\tconst validTargets = new Set(['claude-desktop', 'cursor', 'claude-code']);\n\tif (!target || !validTargets.has(target)) {\n\t\tconsole.error(\n\t\t\t`Usage: hoststack-mcp --print-config <${[...validTargets].join('|')}>\\n` +\n\t\t\t\t`(--print-config emits a config snippet to stdout; you still need to put your API key into it.)`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\tconst apiKeyPlaceholder = process.env['HOSTSTACK_API_KEY'] ?? 'hs_live_your_api_key_here';\n\n\tif (target === 'claude-desktop' || target === 'cursor') {\n\t\tconst config = {\n\t\t\tmcpServers: {\n\t\t\t\thoststack: {\n\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\targs: ['-y', '@hoststack.dev/mcp'],\n\t\t\t\t\tenv: {\n\t\t\t\t\t\tHOSTSTACK_API_KEY: apiKeyPlaceholder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tconst payload = target === 'cursor' ? config.mcpServers : config;\n\t\tprocess.stdout.write(JSON.stringify(payload, null, '\\t') + '\\n');\n\t\tprocess.exit(0);\n\t}\n\n\tif (target === 'claude-code') {\n\t\tprocess.stdout.write(\n\t\t\t`claude mcp add hoststack --env HOSTSTACK_API_KEY=${apiKeyPlaceholder} -- npx -y @hoststack.dev/mcp\\n`,\n\t\t);\n\t\tprocess.exit(0);\n\t}\n}\n\nconst apiKey = process.env['HOSTSTACK_API_KEY'];\nif (!apiKey) {\n\tconsole.error('HOSTSTACK_API_KEY environment variable is required');\n\tprocess.exit(1);\n}\n\nconst baseUrl = process.env['HOSTSTACK_BASE_URL'] ?? 'https://hoststack.dev';\n\n// stdio mode: NDJSON to stderr so the user can `2> mcp-trace.log` if they\n// want a local trace, without ever colliding with the JSON-RPC body on\n// stdout. Disabled when HOSTSTACK_MCP_TRACE is unset to keep the default\n// quiet for Claude Desktop / Cursor.\nconst traceEnabled = process.env['HOSTSTACK_MCP_TRACE'] === '1';\nconst onToolCall = traceEnabled\n\t? (event: ToolCallEvent) => {\n\t\t\tprocess.stderr.write(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tts: event.startedAt.toISOString(),\n\t\t\t\t\ttool: event.tool,\n\t\t\t\t\tok: event.ok,\n\t\t\t\t\tduration_ms: event.durationMs,\n\t\t\t\t\terror_code: event.errorCode,\n\t\t\t\t\ttransport: 'stdio',\n\t\t\t\t}) + '\\n',\n\t\t\t);\n\t\t}\n\t: undefined;\n\nconst server = createMcpServer({\n\tapiKey,\n\tbaseUrl,\n\t...(onToolCall ? { onToolCall } : {}),\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { HostStack } from '@hoststack.dev/sdk';\n\nimport { ApiClient } from './api-client.js';\nimport { attachPrompts } from './prompts/registry.js';\nimport { attachResources } from './resources/registry.js';\nimport { attachTools, type ToolCallSink, type ToolContext } from './registry.js';\n\nexport type { ToolCallEvent, ToolCallSink } from './registry.js';\n\n// Importing each tool/prompt/resource module runs its top-level define*()\n// calls and populates the shared registries. ESM caches modules, so each\n// registry is built exactly once per process.\nimport './prompts/deploy-prompts.js';\nimport './resources/hoststack-resources.js';\nimport './tools/activity-log.js';\nimport './tools/alerts.js';\nimport './tools/cron.js';\nimport './tools/databases.js';\nimport './tools/deploys.js';\nimport './tools/dns-records.js';\nimport './tools/domains.js';\nimport './tools/env-vars.js';\nimport './tools/environments.js';\nimport './tools/meta.js';\nimport './tools/notifications.js';\nimport './tools/projects.js';\nimport './tools/services.js';\nimport './tools/volumes.js';\n\nexport interface CreateServerOptions {\n\t/** HostStack API key (hs_live_… or hs_test_…). Required. */\n\tapiKey: string;\n\t/** Base URL for the HostStack API. Defaults to https://hoststack.dev */\n\tbaseUrl?: string;\n\t/**\n\t * Server identity advertised in the MCP `initialize` response. Defaults\n\t * to `{ name: \"hoststack\", version: PACKAGE_VERSION }`.\n\t */\n\tserverInfo?: { name: string; version: string };\n\t/**\n\t * Optional telemetry sink invoked once per tool call (success or failure).\n\t * Runs on the microtask queue; sink errors are swallowed so tool execution\n\t * never blocks on telemetry.\n\t */\n\tonToolCall?: ToolCallSink;\n}\n\nexport const PACKAGE_NAME = 'hoststack';\nexport const PACKAGE_VERSION = '0.9.1';\n\ninterface MeResponse {\n\tuser: { id: number };\n\tteam?: { id: number };\n}\n\n/**\n * Build a fully-wired MCP server. Used by both the stdio CLI entry-point and\n * the HTTP transport mounted by the API server. A new server (and its\n * underlying SDK + ApiClient) is created per call, so the HTTP transport can\n * scope each connection to a single API key without cross-contamination.\n */\nexport function createMcpServer(options: CreateServerOptions): McpServer {\n\tconst baseUrl = (options.baseUrl ?? 'https://hoststack.dev').replace(/\\/$/, '');\n\tconst hoststack = new HostStack({ apiKey: options.apiKey, baseUrl });\n\tconst api = new ApiClient(options.apiKey, baseUrl);\n\n\tconst server = new McpServer({\n\t\tname: options.serverInfo?.name ?? PACKAGE_NAME,\n\t\tversion: options.serverInfo?.version ?? PACKAGE_VERSION,\n\t});\n\n\t// Cache the team lookup. The first tool call pays one /auth/me round-trip;\n\t// every subsequent call reuses the cached promise. We store the in-flight\n\t// promise (not the resolved value) so concurrent first-calls don't fan out\n\t// into duplicate /auth/me requests.\n\tlet teamIdPromise: Promise<number> | null = null;\n\tconst resolveTeamId = (): Promise<number> => {\n\t\tif (!teamIdPromise) {\n\t\t\tteamIdPromise = api.get<MeResponse>('/api/auth/me').then((me) => {\n\t\t\t\tif (!me.team?.id) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'No team bound to API key. Generate a team-scoped key in the dashboard.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn me.team.id;\n\t\t\t});\n\t\t\t// If the lookup fails, clear the cache so a retry can succeed once\n\t\t\t// the user fixes their key. Without this, every subsequent tool\n\t\t\t// call would re-throw the same cached rejection.\n\t\t\tteamIdPromise.catch(() => {\n\t\t\t\tteamIdPromise = null;\n\t\t\t});\n\t\t}\n\t\treturn teamIdPromise;\n\t};\n\n\tconst ctx: ToolContext = { hoststack, api, resolveTeamId };\n\tattachTools(server, ctx, options.onToolCall);\n\tattachPrompts(server, ctx);\n\tattachResources(server, ctx);\n\n\treturn server;\n}\n","/**\n * Lightweight API client for HostStack endpoints not yet covered by the SDK\n * (logs snapshots, activity log, runtime metrics, /me lookup). Uses the same\n * auth and base URL as the SDK instance.\n */\nexport class ApiClient {\n\tconstructor(\n\t\tprivate readonly apiKey: string,\n\t\tprivate readonly baseUrl: string,\n\t) {}\n\n\tprivate get headers(): Record<string, string> {\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t'Content-Type': 'application/json',\n\t\t};\n\t}\n\n\tasync get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n\t\tconst url = new URL(`${this.baseUrl}${path}`);\n\t\tif (params) {\n\t\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\t\tif (value !== undefined) url.searchParams.set(key, String(value));\n\t\t\t}\n\t\t}\n\t\tconst res = await fetch(url.toString(), { headers: this.headers });\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\tconst init: RequestInit = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.headers,\n\t\t};\n\t\tif (body !== undefined) init.body = JSON.stringify(body);\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, init);\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync patch<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync put<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PUT',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync delete<T>(path: string): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.headers,\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tprivate async handle<T>(res: Response): Promise<T> {\n\t\tif (!res.ok) {\n\t\t\tconst error = (await res.json().catch(() => ({ error: res.statusText }))) as {\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tthrow new Error(error.error ?? `API error: ${res.status}`);\n\t\t}\n\t\tif (res.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn res.json() as Promise<T>;\n\t}\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from '../api-client.js';\n\nexport interface PromptContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalPromptDefinition {\n\tname: string;\n\tdescription: string;\n\targs: ZodRawShape;\n\thandler: (\n\t\targs: Record<string, string | undefined>,\n\t\tctx: PromptContext,\n\t) => Promise<GetPromptResult>;\n}\n\nexport type PromptDefinition = Readonly<InternalPromptDefinition>;\n\nconst prompts: InternalPromptDefinition[] = [];\n\n/**\n * Register a single prompt. The generic over `TArgs` lets the handler receive\n * precisely-typed args while the registry stores the widened shape — same\n * trick as `defineTool` to keep the compiler's instantiation budget in check.\n */\nexport function definePrompt<TArgs extends ZodRawShape>(def: {\n\tname: string;\n\tdescription: string;\n\targs: TArgs;\n\thandler: (args: z.infer<z.ZodObject<TArgs>>, ctx: PromptContext) => Promise<GetPromptResult>;\n}): void {\n\tprompts.push({\n\t\tname: def.name,\n\t\tdescription: def.description,\n\t\targs: def.args,\n\t\thandler: def.handler as InternalPromptDefinition['handler'],\n\t});\n}\n\nexport function listPromptDefinitions(): ReadonlyArray<PromptDefinition> {\n\treturn prompts;\n}\n\n/** Iterate the registry and wire each prompt into the MCP server. */\nexport function attachPrompts(server: McpServer, ctx: PromptContext): void {\n\ttype PromptMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\targsSchema: ZodRawShape,\n\t\tcb: (args: Record<string, string | undefined>) => Promise<GetPromptResult>,\n\t) => unknown;\n\n\tconst register = server.prompt.bind(server) as unknown as PromptMethod;\n\n\tfor (const def of prompts) {\n\t\tregister(def.name, def.description, def.args, (args) => def.handler(args, ctx));\n\t}\n}\n\n/**\n * Helper to build a single user-role text message — the most common shape for\n * these prompts (we're guiding the agent, not modeling assistant responses).\n */\nexport function userMessage(text: string): GetPromptResult['messages'][number] {\n\treturn { role: 'user', content: { type: 'text', text } };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\n\nimport type { ApiClient } from '../api-client.js';\n\ninterface ResourceContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface StaticResourceDef {\n\tkind: 'static';\n\tname: string;\n\turi: string;\n\tdescription: string;\n\tmimeType?: string;\n\tread: (uri: URL, ctx: ResourceContext) => Promise<ReadResourceResult>;\n}\n\nexport type ResourceDefinition = Readonly<StaticResourceDef>;\n\nconst resources: StaticResourceDef[] = [];\n\nexport function defineResource(def: StaticResourceDef): void {\n\tresources.push(def);\n}\n\nexport function listResourceDefinitions(): ReadonlyArray<ResourceDefinition> {\n\treturn resources;\n}\n\nexport function attachResources(server: McpServer, ctx: ResourceContext): void {\n\tfor (const def of resources) {\n\t\tserver.resource(\n\t\t\tdef.name,\n\t\t\tdef.uri,\n\t\t\t{\n\t\t\t\tdescription: def.description,\n\t\t\t\t...(def.mimeType ? { mimeType: def.mimeType } : {}),\n\t\t\t},\n\t\t\t(uri) => def.read(uri, ctx),\n\t\t);\n\t}\n}\n\n/**\n * Pack a JSON value into the resource read-result envelope. The MCP spec\n * requires `text` for application/json resources — we serialise here so each\n * resource handler stays a one-liner.\n */\nexport function jsonResource(uri: URL, data: unknown): ReadResourceResult {\n\treturn {\n\t\tcontents: [\n\t\t\t{\n\t\t\t\turi: uri.toString(),\n\t\t\t\tmimeType: 'application/json',\n\t\t\t\ttext: JSON.stringify(data, null, 2),\n\t\t\t},\n\t\t],\n\t};\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from './api-client.js';\n\nexport type ToolCategory =\n\t| 'projects'\n\t| 'services'\n\t| 'deploys'\n\t| 'databases'\n\t| 'domains'\n\t| 'dns'\n\t| 'volumes'\n\t| 'env-vars'\n\t| 'environments'\n\t| 'cron'\n\t| 'logs'\n\t| 'activity-log'\n\t| 'alerts'\n\t| 'meta';\n\nexport interface ToolContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\t/**\n\t * Lazy resolver for the team ID bound to the API key. The agent never has\n\t * to pass `teamId` to a tool — every SDK route is team-scoped, so the MCP\n\t * looks it up once via `/api/auth/me` and caches it for the rest of the\n\t * server's lifetime. Throws if the API key is invalid or unbound.\n\t */\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalToolDefinition {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: ZodRawShape;\n\thandler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;\n}\n\nexport type ToolDefinition = Readonly<InternalToolDefinition>;\n\nconst tools: InternalToolDefinition[] = [];\n\n/**\n * Register a single tool definition. The generic over `TInput` lets the\n * handler receive precisely-typed arguments while the registry stores the\n * widened shape.\n */\nexport function defineTool<TInput extends ZodRawShape>(def: {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: TInput;\n\thandler: (args: z.infer<z.ZodObject<TInput>>, ctx: ToolContext) => Promise<CallToolResult>;\n}): void {\n\ttools.push({\n\t\tname: def.name,\n\t\tcategory: def.category,\n\t\tdescription: def.description,\n\t\tinput: def.input,\n\t\thandler: def.handler as InternalToolDefinition['handler'],\n\t});\n}\n\n/** Snapshot the currently-registered tool definitions (for tests / docs). */\nexport function listToolDefinitions(): ReadonlyArray<ToolDefinition> {\n\treturn tools;\n}\n\n/**\n * Telemetry event emitted once per tool invocation. The MCP package never\n * persists these — it just hands them to the optional `onToolCall` sink so\n * the host (HTTP transport, CLI, tests) can decide where they go.\n *\n * Privacy: `inputHash` is a hash over the JSON-stringified args; we never\n * expose the args themselves so PII from tool inputs (env-var values, domain\n * names) never reaches a sink.\n */\nexport interface ToolCallEvent {\n\ttool: string;\n\tdurationMs: number;\n\tok: boolean;\n\terrorCode: string | null;\n\tinputHash: string | null;\n\tstartedAt: Date;\n}\n\nexport type ToolCallSink = (event: ToolCallEvent) => void | Promise<void>;\n\n/** Iterate the registry and wire each tool into the MCP server. */\nexport function attachTools(server: McpServer, ctx: ToolContext, sink?: ToolCallSink): void {\n\ttype ToolMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\tschema: ZodRawShape,\n\t\tcb: (args: Record<string, unknown>) => Promise<CallToolResult>,\n\t) => unknown;\n\n\tconst register = server.tool.bind(server) as unknown as ToolMethod;\n\n\tfor (const def of tools) {\n\t\tregister(def.name, def.description, def.input, async (args) => {\n\t\t\tconst startedAt = new Date();\n\t\t\tconst start = performance.now();\n\t\t\tlet ok = true;\n\t\t\tlet errorCode: string | null = null;\n\t\t\ttry {\n\t\t\t\tconst result = await def.handler(args, ctx);\n\t\t\t\tif (result.isError) {\n\t\t\t\t\tok = false;\n\t\t\t\t\terrorCode = 'tool_error';\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tok = false;\n\t\t\t\terrorCode =\n\t\t\t\t\terr instanceof Error ? (err.name || 'Error').slice(0, 64) : 'unknown_error';\n\t\t\t\tthrow err;\n\t\t\t} finally {\n\t\t\t\tif (sink) {\n\t\t\t\t\tconst durationMs = Math.round(performance.now() - start);\n\t\t\t\t\tconst event: ToolCallEvent = {\n\t\t\t\t\t\ttool: def.name,\n\t\t\t\t\t\tdurationMs,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\tinputHash: hashArgs(args),\n\t\t\t\t\t\tstartedAt,\n\t\t\t\t\t};\n\t\t\t\t\tPromise.resolve()\n\t\t\t\t\t\t.then(() => sink(event))\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction hashArgs(args: Record<string, unknown>): string | null {\n\ttry {\n\t\tconst json = JSON.stringify(args);\n\t\tconst bunGlobal = (globalThis as { Bun?: { hash?: (data: string) => bigint } }).Bun;\n\t\tif (bunGlobal?.hash) {\n\t\t\treturn bunGlobal.hash(json).toString(16);\n\t\t}\n\t\treturn `len:${json.length}`;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import { z } from 'zod';\n\nimport { definePrompt, userMessage } from './registry.js';\n\ndefinePrompt({\n\tname: 'diagnose_failed_deploy',\n\tdescription:\n\t\t'Walk the agent through diagnosing a failed deploy: pull the latest deploys for the named service, find the most recent failure, fetch its build/deploy logs, and propose a fix. Stops at suggestion — does not retrigger automatically.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async ({ service_id }) => {\n\t\tconst text = `Goal: diagnose the most recent failed deploy on service ${service_id} and propose a fix.\n\nPlan (use these tools in order):\n1. \\`list_deploys({ service_id: \"${service_id}\" })\\` — pull recent deploys, newest first. Identify the most recent deploy with status \\`failed\\` (or \\`cancelled\\` if the user wants to investigate that too). Capture its publicId, branch, and commitSha.\n2. \\`get_deploy_logs({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — read the full build output. Scan for: stack traces, \"ERROR\" / \"error:\" lines, exit codes, missing env vars, OOM-killed signals, network failures pulling dependencies.\n3. \\`get_service({ service_id: \"${service_id}\" })\\` — confirm the service's runtime, plan, and whether autoDeploy is on. Plan tier matters because OOMs at small tiers point at \\`maxMemoryMb\\`.\n4. (Optional) \\`list_env_vars({ service_id: \"${service_id}\" })\\` — only if the build error mentions a missing variable. Don't fetch otherwise; values are masked anyway.\n\nThen write a short diagnosis for the user with three sections:\n- **Root cause** — one sentence pointing at the actual failure line.\n- **Evidence** — 3–6 line excerpt from the logs that proves the diagnosis.\n- **Suggested fix** — concrete next steps, including which tool to call (e.g. \"set MISSING_KEY with set_env_var, then trigger_deploy\"). Do NOT call trigger_deploy automatically; the user decides.\n\nIf the deploy logs are empty or only contain queued/pending markers, surface that — the build never started, which is its own class of bug (likely a builder-pool or quota issue).`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'deploy_status_check',\n\tdescription:\n\t\t\"Quick orientation prompt: who am I, what's running, and is anything currently deploying? Useful at the start of a session.\",\n\targs: {},\n\thandler: async () => {\n\t\tconst text = `Goal: produce a 30-second status check on this HostStack team.\n\nPlan:\n1. \\`get_me()\\` — confirm which user + team the API key belongs to.\n2. \\`list_services()\\` — every service with current status (running, suspended, building, deploying, failed). Note any service in a non-running state.\n3. For each service whose status is \\`building\\` or \\`deploying\\`, call \\`list_deploys({ service_id })\\` and surface the latest deploy's commitMessage + author.\n4. \\`list_activity_log({ per_page: 10 })\\` — last 10 audit events to spot recent changes (deploys triggered, env vars edited, services restarted).\n\nWrite a status report shaped like:\n\n> **Team**: <name>\n> **Services**: <n> total — <breakdown by status>\n> **In flight**: <list of building/deploying services with commit info, or \"none\">\n> **Recent activity**: <3-5 most relevant events from the audit log, newest first>\n\nKeep it tight (under 200 words). The user can drill in with follow-up tools.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'rotate_secret',\n\tdescription:\n\t\t'Guide the agent through rotating an env-var secret on a service. Lists current vars to confirm the target key exists, asks the user for the new value, sets it, and offers to trigger a deploy.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().describe('Env-var key to rotate (e.g. DATABASE_URL).'),\n\t},\n\thandler: async ({ service_id, key }) => {\n\t\tconst text = `Goal: rotate the secret \\`${key}\\` on service ${service_id}.\n\nPlan:\n1. \\`list_env_vars({ service_id: \"${service_id}\" })\\` — confirm \\`${key}\\` exists. Note its \\`isSecret\\` flag (it should be true; if not, flag this — you're about to mark it secret on rotation, which is a behaviour change).\n2. **Ask the user for the new value before calling any write tool.** The agent must NOT invent or generate the secret; it has to come from the user's clipboard / vault. Phrase the ask explicitly: \"Paste the new value for ${key} (it will be encrypted at rest and masked everywhere except the running container).\"\n3. Once the user provides the value:\n - \\`set_env_var({ service_id: \"${service_id}\", key: \"${key}\", value: \"<new value>\", is_secret: true })\\` — upserts by key.\n4. After the write succeeds, ask the user whether to redeploy (so the new value lands in the running container). If yes:\n - \\`trigger_deploy({ service_id: \"${service_id}\" })\\` — kicks off a build with the rotated secret.\n - \\`get_deploy({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — poll until status is \\`live\\` or \\`failed\\`. On failure, hand off to \\`diagnose_failed_deploy\\`.\n\nSafety rails:\n- Never echo the new value back in your reply; the user should verify it from their own source of truth, not from your context window.\n- If the user says \"generate a new one\", suggest \\`openssl rand -hex 32\\` (or similar) for them to run locally and paste in. Do NOT generate it for them — secrets that pass through an LLM context are no longer secrets.\n- If \\`set_env_var\\` returned \\`action: \"created\"\\` instead of \\`\"updated\"\\`, the key didn't exist before. Pause and confirm with the user — they may have typo'd the key name.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n","/**\n * Per-resource shape pickers. Each function takes a raw API/SDK response and\n * returns the agent-friendly subset: drops internal foreign keys (teamId,\n * userId, ...) and null-valued columns, leaves ISO-8601 timestamps as-is.\n *\n * Pickers must be permissive about input shape — different SDK methods may\n * return slightly different fields (e.g. list vs get) and the API may grow\n * fields over time. We never throw on missing fields.\n */\n\nconst INTERNAL_FIELDS = new Set([\n\t'teamId',\n\t'team_id',\n\t'userId',\n\t'user_id',\n\t'accountId',\n\t'account_id',\n\t'tenantId',\n\t'tenant_id',\n\t'createdById',\n\t'updatedById',\n]);\n\ntype Obj = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is Obj {\n\treturn value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction dropNullsAndInternals(obj: Obj): Obj {\n\tconst out: Obj = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (INTERNAL_FIELDS.has(key)) continue;\n\t\tif (value === null || value === undefined) continue;\n\t\tout[key] = value;\n\t}\n\treturn out;\n}\n\n/** Generic shape — drop internals, drop nulls, keep everything else. */\nexport function shape(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(shape);\n\tif (isObject(value)) return dropNullsAndInternals(value);\n\treturn value;\n}\n\nfunction shapeAll<T>(arr: unknown, shaper: (item: unknown) => T): T[] {\n\tif (!Array.isArray(arr)) return [];\n\treturn arr.map(shaper);\n}\n\n/**\n * Unwrap a HostStack list response. The API returns shapes like\n * `{ projects: [...] }`, `{ services: [...] }`, etc. — the key varies by\n * resource. Pass the key explicitly so the wrapper stays type-loose.\n */\nexport function shapeList<T>(\n\tresponse: unknown,\n\tkey: string,\n\tshaper: (item: unknown) => T,\n): { items: T[] } {\n\tif (!isObject(response)) return { items: [] };\n\treturn { items: shapeAll(response[key], shaper) };\n}\n\n// ──────────────────────────────────────────────────────────────────────\n// Per-resource pickers — typed convenience wrappers around shape().\n// All return a plain Obj (never null), keeping the registry handlers simple.\n// ──────────────────────────────────────────────────────────────────────\n\nexport function shapeProject(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeService(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDeploy(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDatabase(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDomain(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeEnvVar(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeCronExecution(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeActivity(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeUser(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeTeam(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeVolume(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n","import { shapeProject, shapeService, shapeTeam, shapeUser } from '../lib/shape.js';\nimport { defineResource, jsonResource } from './registry.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineResource({\n\tkind: 'static',\n\tname: 'team',\n\turi: 'hoststack://team',\n\tdescription:\n\t\t'Authenticated team identity + a quick orientation snapshot. Includes the team record, the user behind the API key, and counts of projects and services so an agent can decide whether to fan out into list_projects / list_services without an extra round-trip.',\n\tmimeType: 'application/json',\n\tread: async (uri, { hoststack, api, resolveTeamId }) => {\n\t\tconst teamId = await resolveTeamId();\n\n\t\t// Resource handlers must not throw on transient downstream failures —\n\t\t// clients call resources/read for orientation and a network blip\n\t\t// shouldn't poison the entire session. Tolerate partial failure and\n\t\t// surface what we can.\n\t\tconst [me, projectsResult, servicesResult] = await Promise.all([\n\t\t\tapi.get<MeResponse>('/api/auth/me').catch(() => null),\n\t\t\thoststack.projects.list(teamId).catch(() => ({ projects: [] as unknown[] })),\n\t\t\thoststack.services.list(teamId).catch(() => ({ services: [] as unknown[] })),\n\t\t]);\n\n\t\tconst projects = (projectsResult.projects as unknown[]).slice(0, 10).map(shapeProject);\n\t\tconst services = (servicesResult.services as unknown[]).slice(0, 10).map(shapeService);\n\n\t\treturn jsonResource(uri, {\n\t\t\tuser: me?.user ? shapeUser(me.user) : null,\n\t\t\tteam: me?.team ? shapeTeam(me.team) : null,\n\t\t\tproject_count: (projectsResult.projects as unknown[]).length,\n\t\t\tservice_count: (servicesResult.services as unknown[]).length,\n\t\t\t// First 10 of each so the resource stays small while still useful\n\t\t\t// for orientation. Use list_projects / list_services for the full set.\n\t\t\tprojects_preview: projects,\n\t\t\tservices_preview: services,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeActivity } from '../lib/shape.js';\n\ninterface ActivityLogResponse {\n\tdata?: unknown[];\n\t// Older API style: pagination meta as a single nested object. Some\n\t// callers still serve this shape, so keep accepting it.\n\tmeta?: unknown;\n\t// Current API style: pagination meta fields are spread flat on the\n\t// response root (`{ data, page, perPage, total, totalPages }`).\n\tpage?: number;\n\tperPage?: number;\n\ttotal?: number;\n\ttotalPages?: number;\n}\n\ndefineTool({\n\tname: 'list_activity_log',\n\tcategory: 'activity-log',\n\tdescription: [\n\t\t'List the team activity audit log: who did what, when. Backed by /api/activity-log/:teamId.',\n\t\t'',\n\t\t'When to use: investigate why a service was changed unexpectedly (\"who deleted that env var?\"), correlate a deploy with a user, or pull recent admin events for a status update.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - page: 1-based page index (default 1).',\n\t\t' - per_page: items per page (default 25, hard cap 100).',\n\t\t' - action: filter to a specific action (e.g. \"service.created\", \"deploy.triggered\").',\n\t\t' - resource_type: filter by resource type (e.g. \"service\", \"deploy\", \"domain\").',\n\t\t' - user_id: filter by acting user numeric ID.',\n\t\t' - since/until: ISO-8601 timestamp OR a relative offset like \"-15m\" / \"-2h\" / \"-7d\" — narrow to a time window (handy for correlating a deploy with a log spike).',\n\t\t'',\n\t\t'Returns: { items: ActivityLogEntry[], meta: { page, perPage, total, totalPages } } — each entry has id, action, resourceType, resourceId, actorEmail, ipAddress, createdAt, and a context payload. Use meta.totalPages to tell whether you have more.',\n\t\t'',\n\t\t'Example: list_activity_log({ resource_type: \"deploy\", per_page: 10 }) → { items: [{ action: \"deploy.triggered\", actorEmail: \"ada@…\", … }], meta: { total: 47, page: 1 } }',\n\t].join('\\n'),\n\tinput: {\n\t\tpage: z.number().int().positive().optional().describe('Page index, 1-based.'),\n\t\tper_page: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Items per page, hard cap 100.'),\n\t\taction: z.string().optional().describe('Action filter, e.g. \"service.created\".'),\n\t\tresource_type: z.string().optional().describe('Resource type filter.'),\n\t\tuser_id: z.number().int().positive().optional().describe('Numeric acting-user filter.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-15m\", \"-2h\", \"-7d\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.page !== undefined) params['page'] = String(args.page);\n\t\tif (args.per_page !== undefined) params['perPage'] = String(args.per_page);\n\t\tif (args.action !== undefined) params['action'] = args.action;\n\t\tif (args.resource_type !== undefined) params['resourceType'] = args.resource_type;\n\t\tif (args.user_id !== undefined) params['userId'] = String(args.user_id);\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\n\t\tconst response = await ctx.api.get<ActivityLogResponse>(\n\t\t\t`/api/activity-log/${teamId}`,\n\t\t\tparams,\n\t\t);\n\t\tconst items = Array.isArray(response.data) ? response.data.map(shapeActivity) : [];\n\t\tconst data: { items: unknown[]; meta?: unknown } = { items };\n\t\t// The API spreads pagination at the top level (`{ data, page, perPage,\n\t\t// total, totalPages }`). Older variants nested it under `meta`.\n\t\t// Surface a single `meta` object regardless so MCP callers don't have\n\t\t// to learn two shapes.\n\t\tif (response.meta !== undefined) {\n\t\t\tdata.meta = shape(response.meta);\n\t\t} else if (\n\t\t\tresponse.page !== undefined ||\n\t\t\tresponse.perPage !== undefined ||\n\t\t\tresponse.total !== undefined ||\n\t\t\tresponse.totalPages !== undefined\n\t\t) {\n\t\t\tdata.meta = shape({\n\t\t\t\tpage: response.page,\n\t\t\t\tperPage: response.perPage,\n\t\t\t\ttotal: response.total,\n\t\t\t\ttotalPages: response.totalPages,\n\t\t\t});\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\titems.length === 0\n\t\t\t\t\t? 'No activity log entries match the given filters.'\n\t\t\t\t\t: `Returned ${items.length} activity log entr${items.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import type { CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';\n\ninterface RespondInput {\n\tsummary: string;\n\tdata?: unknown;\n}\n\nexport const SUMMARY_MAX_LEN = 500;\n\n/**\n * Build an MCP tool response with a 1–3 line plain-text summary, a fenced\n * JSON block for human readability, and a structuredContent object that\n * agents can parse without re-tokenising the JSON text.\n */\nexport function respond({ summary, data }: RespondInput): CallToolResult {\n\tconst trimmed = summary.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('respond(): summary must be non-empty');\n\t}\n\tif (trimmed.length > SUMMARY_MAX_LEN) {\n\t\tthrow new Error(`respond(): summary too long (${trimmed.length} > ${SUMMARY_MAX_LEN})`);\n\t}\n\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: trimmed }];\n\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\n\tconst result: CallToolResult = { content: blocks };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nexport function respondError(message: string, data?: unknown): CallToolResult {\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: `Error: ${message}` }];\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\tconst result: CallToolResult = { content: blocks, isError: true };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nfunction toStructuredContent(data: unknown): Record<string, unknown> {\n\tif (data !== null && typeof data === 'object' && !Array.isArray(data)) {\n\t\treturn data as Record<string, unknown>;\n\t}\n\treturn { result: data };\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface AlertsResponse {\n\talerts: unknown[];\n\taggregated: boolean;\n}\n\ndefineTool({\n\tname: 'list_alerts',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List recent alert-shaped events for the team: deploy failures, git auth losses, service health failures, auto-restarts, ACME cert failures, resource alerts (high CPU), database backup/restore failures, registrant verification lapses.',\n\t\t'',\n\t\t\"When to use: triage 'what's currently broken or recently broke for this team'. Pairs with list_activity_log for the full audit feed; this tool is the alert-shaped subset.\",\n\t\t'',\n\t\t'By default events are AGGREGATED by (action, resourceId) so flapping events collapse to one row with a fire count + first/last timestamps — e.g. \"service.auto_restarted on service 31, 8 times in the last hour, last at 14:22\". Pass aggregate=false to see every raw row.',\n\t\t'',\n\t\t'ACTIVE vs HISTORICAL (v89): each aggregated entry now has an `active` boolean and a `lastResolvedAt` timestamp. `active=true` means the most recent occurrence has NOT been resolved — the alert is still on fire. The default `active=true` filter hides resolved/historical alerts so triage starts with \"what is broken right now\". Pass `active=false` to include resolved entries too (useful for post-mortems). Resolution is automatic for some alert kinds — e.g. `deploy.failed_consecutive` is cleared when the next deploy of the same service goes live.',\n\t\t'',\n\t\t'Database backup_failed entries (v89) include `containerStatus` / `containerHealth` / `containerExitCode` in lastMetadata when the agent could resolve the container at failure time, so you can tell \"Redis is overloaded\" apart from \"Redis is restarting\" without a second tool call.',\n\t\t'',\n\t\t'Deploy.failed_consecutive entries (v89) carry the offending `commitHash` in lastMetadata, and the streak now dedupes per (service, commitHash) — one critical per bad commit, not one every 6 retries.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - since: ISO-8601 timestamp OR relative offset like \"-1h\" / \"-2d\". Default: -24h.',\n\t\t' - until: ISO-8601 upper bound (ignored when aggregating — aggregated view always extends to now).',\n\t\t' - limit: max rows (default 100, hard cap 500).',\n\t\t' - aggregate: true (default) collapses by (action, resourceId); false returns raw rows.',\n\t\t' - active: true (default) shows only alerts that are still on fire (resolved_at IS NULL on the most recent row). false includes resolved historical alerts.',\n\t\t'',\n\t\t'Returns: { alerts: Array, aggregated: boolean, activeOnly: boolean }. Each aggregated entry includes action, resourceType, resourceId, severity, count, firstFiredAt, lastFiredAt, lastResolvedAt (nullable), active (boolean), lastMetadata. Each raw entry adds resolvedAt (nullable).',\n\t\t'',\n\t\t\"Example: list_alerts({ since: '-1h' }) → { alerts: [{ action: 'deploy.failed_consecutive', resourceId: 31, severity: 'critical', active: true, count: 3, lastFiredAt: '…', lastResolvedAt: null, lastMetadata: { commitHash: 'abc1234' }, … }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-1h\", \"-2d\"). Default: -24h.'),\n\t\tuntil: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 upper bound. Only honored when aggregate=false.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Max rows (default 100, hard cap 500).'),\n\t\taggregate: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Collapse by (action, resourceId). Default true.'),\n\t\tactive: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Only return alerts still on fire (resolved_at IS NULL on the most recent row). Default true. Pass false to include resolved historical alerts.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\t\tif (args.limit !== undefined) params['limit'] = String(args.limit);\n\t\t// API expects aggregate=1|0; default behavior on the API side is\n\t\t// aggregate=true. Pass explicit \"0\" only when caller opts out so\n\t\t// we don't ship spurious query params.\n\t\tif (args.aggregate === false) params['aggregate'] = '0';\n\t\t// v89: same convention for active=1|0. Default true on the API\n\t\t// side, so we only ship `active=0` when the caller opts in to\n\t\t// historical entries.\n\t\tif (args.active === false) params['active'] = '0';\n\n\t\tconst response = await ctx.api.get<AlertsResponse>(`/api/alerts/${teamId}`, params);\n\t\tconst items = Array.isArray(response.alerts) ? response.alerts.map(shape) : [];\n\t\tconst aggregated = Boolean(response.aggregated);\n\t\tconst activeOnly = args.active !== false;\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? activeOnly\n\t\t\t\t\t? 'No active alerts in the requested window — everything is operating normally.'\n\t\t\t\t\t: 'No alerts in the requested window (active or resolved).'\n\t\t\t\t: aggregated\n\t\t\t\t\t? `Returned ${items.length} alert group${items.length === 1 ? '' : 's'}${activeOnly ? ' (active only — pass active=false for resolved history)' : ''} (flapping events collapsed).`\n\t\t\t\t\t: `Returned ${items.length} raw alert event${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { alerts: items, aggregated, activeOnly } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeCronExecution, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_cron_executions',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'List recent execution records for a cron service (newest first).',\n\t\t'',\n\t\t'When to use: investigating whether a scheduled job ran on time, finding the most recent failure, or auditing cron run-times. Only works on services of type \"cron\"; for web services this returns an error.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - limit (optional): how many executions to fetch (default 50, max enforced server-side).',\n\t\t'',\n\t\t'Returns: { items: CronExecution[] } — id, publicId, status (succeeded|failed|running|queued), startedAt, finishedAt, exitCode, triggeredBy.',\n\t\t'',\n\t\t'Example: list_cron_executions({ service_id: \"svc_cron\" }) → { items: [{ status: \"succeeded\", exitCode: 0, … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Max executions to return (default 50).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts = args.limit !== undefined ? { limit: args.limit } : undefined;\n\t\tconst response = await ctx.hoststack.cron.list(teamId, args.service_id, opts);\n\t\tconst data = shapeList(response, 'executions', shapeCronExecution);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No cron executions yet for ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} cron execution${data.items.length === 1 ? '' : 's'} for ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_cron_execution',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'Fetch a single cron execution record. Includes status, exit code, and timing.',\n\t\t'',\n\t\t'When to use: drilling into a specific run after list_cron_executions surfaced a failure, or correlating an execution timestamp with logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - execution_id: publicId of the execution record.',\n\t\t'',\n\t\t'Returns: { execution: CronExecution }.',\n\t\t'',\n\t\t'Example: get_cron_execution({ service_id: \"svc_cron\", execution_id: \"exe_xyz\" }) → { execution: { status: \"failed\", exitCode: 1, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\texecution_id: z.string().describe('Execution publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.cron.get(teamId, args.service_id, args.execution_id);\n\t\tconst data = { execution: shapeCronExecution(response.execution) };\n\t\tconst status =\n\t\t\tdata.execution && 'status' in data.execution\n\t\t\t\t? (data.execution as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Execution ${args.execution_id} ${status}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDatabase, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_databases',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'List managed databases (Postgres, Redis, MySQL, MariaDB, MongoDB, Meilisearch, NATS) inside a project.',\n\t\t'',\n\t\t'When to use: an agent needs to know what data stores exist in a project before connecting a service or running a migration. Pair with list_projects to discover project IDs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project ID (from list_projects).',\n\t\t'',\n\t\t'Returns: { items: Database[] } — id, publicId, name, type, status, version, projectId, createdAt.',\n\t\t'',\n\t\t'Example: list_databases({ project_id: 12 }) → { items: [{ publicId: \"db_…\", type: \"postgres\", status: \"running\", … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric project ID (from list_projects).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'databases', shapeDatabase);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No databases in project ${args.project_id}.`\n\t\t\t\t: `Found ${data.items.length} database${data.items.length === 1 ? '' : 's'} in project ${args.project_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Update a managed database — rename, change plan tier (memory/CPU), or grow disk size.',\n\t\t'',\n\t\t'When to use: the user wants to resize a database (disk or plan), or rename it. Disk can only grow — shrinking is rejected because the underlying filesystem would orphan data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t' - name (optional): new database name.',\n\t\t' - plan (optional): \"free\" | \"micro\" | \"starter\" | \"standard\" | \"pro\" — changes memory tier.',\n\t\t' - disk_size_gb (optional): new disk size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { database: Database } — the updated record.',\n\t\t'',\n\t\t'Example: update_database({ database_id: \"db_xyz\", disk_size_gb: 50 }) → { database: { diskSizeGb: 50, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t\tname: z.string().min(1).max(100).optional().describe('New database name.'),\n\t\tplan: z\n\t\t\t.enum(['free', 'micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1024)\n\t\t\t.optional()\n\t\t\t.describe('New disk size in GB. Must be ≥ current.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname?: string;\n\t\t\tplan?: 'free' | 'micro' | 'starter' | 'standard' | 'pro';\n\t\t\tdiskSizeGb?: number;\n\t\t} = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.disk_size_gb !== undefined) input.diskSizeGb = args.disk_size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.databases.update(teamId, args.database_id, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on database ${args.database_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Fetch a single managed database by ID. Includes status, type, version, and project link.',\n\t\t'',\n\t\t'When to use: confirming the version of a database before generating connection strings, or checking the status of a recently created database. To retrieve the actual connection credentials, use the dashboard — credentials are intentionally not exposed via this MCP tool.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { database: Database }.',\n\t\t'',\n\t\t'Example: get_database({ database_id: \"db_xyz\" }) → { database: { type: \"postgres\", version: \"16\", status: \"running\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.get(teamId, args.database_id);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst type =\n\t\t\tdata.database && 'type' in data.database\n\t\t\t\t? (data.database as { type: string }).type\n\t\t\t\t: 'unknown';\n\t\tconst status =\n\t\t\tdata.database && 'status' in data.database\n\t\t\t\t? (data.database as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Database ${args.database_id} (${type}) is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'query_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Run a single READ-ONLY SQL statement against a managed Postgres database.',\n\t\t'',\n\t\t'When to use: ad-hoc data inspection — checking row counts, sampling rows, debugging a constraint. For schema changes or seed data, ship a Drizzle migration instead.',\n\t\t'',\n\t\t'Safety model (server-side, not bypassable):',\n\t\t' - Wrapped in `BEGIN TRANSACTION READ ONLY` — INSERT/UPDATE/DELETE/DDL fail with a clear \"cannot execute … in a read-only transaction\" error.',\n\t\t' - 30s statement_timeout — runaway queries are killed.',\n\t\t' - 1000-row cap; result CSV is also size-capped at 1 MiB. Both surface via `truncated: true`.',\n\t\t' - Postgres engine only (v1). Other engines return a 400.',\n\t\t' - Single statement only; no embedded `;` and no psql meta-commands (`\\\\…`).',\n\t\t' - Every call is audit-logged (success or failure) as `database.query` so an admin can answer \"who queried this DB, when, with what SQL\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (e.g. \"db_…\"). Resolved via the SDK\\'s publicId helper.',\n\t\t' - sql: a single SELECT/WITH/SHOW/EXPLAIN statement, no trailing `;`. Max 16 KiB.',\n\t\t'',\n\t\t'Returns: { columns: string[], rows: string[][], rowCount: number, truncated: boolean, durationMs: number }. Values come back as strings (psql --csv); cast on the caller side when needed.',\n\t\t'',\n\t\t'Example:',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT count(*) FROM users\" }) → { columns: [\"count\"], rows: [[\"1234\"]], … }',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT id, email FROM users WHERE created_at > now() - interval \\'1 day\\' LIMIT 50\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc).'),\n\t\tsql: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(16_384)\n\t\t\t.describe(\n\t\t\t\t'A single READ-ONLY SQL statement. No trailing `;`, no embedded `;`, no psql meta-commands.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.query(teamId, args.database_id, args.sql);\n\t\tconst summary = result.truncated\n\t\t\t? `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} (truncated; raise LIMIT if needed) in ${result.durationMs}ms.`\n\t\t\t: `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} in ${result.durationMs}ms.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_to_ha',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Trigger a single-node → HA migration on a managed Postgres database. The agent runs pg_dump → bootstrap 3-node Patroni cluster (1 leader + 2 sync replicas + HAProxy) → pg_restore. Customer connections to this database are briefly read-only during the cutover; the standalone container stays running (read-only) for 24 h as a rollback target before automatic decommission.',\n\t\t'',\n\t\t'Requires: PATRONI_ENABLED env on the HostStack deployment + `teams.ha_beta = true` on the calling team. Returns 403 otherwise. Returns 400 if the database is already HA, not Postgres, not on the standard or pro tier, or has a pg_database_size over 1 GB (in-memory archive transfer cap).',\n\t\t'',\n\t\t'When to use: a customer needs failover protection on an existing standalone Postgres. For new databases on an HA-eligible team, just call create_database — the cluster path is automatic.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database to upgrade.',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `pg_engine_type === \"patroni\"` and `status === \"available\"` to confirm completion. The Cluster tab on the dashboard mirrors the same data.',\n\t\t'',\n\t\t'Example: upgrade_database_to_ha({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade to HA.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeToHa(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `HA migration started for ${args.database_id}. Poll get_database until pg_engine_type=patroni to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_database_cluster',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Get the live + retired cluster members and failover history for a Patroni HA Postgres database.',\n\t\t'',\n\t\t'When to use: confirm an HA cluster is healthy (1 leader + 2 sync replicas + 1 proxy + 1 etcd live) or investigate a failover that fired. For standalone databases this returns 400 — call get_database first if you need to branch.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (must be HA).',\n\t\t'',\n\t\t\"Returns: { members: { id, memberRole: 'etcd' | 'primary-candidate' | 'replica' | 'proxy', containerId, workerHostId, joinedAt, leftAt }[], failovers: { id, createdAt, oldLeader, newLeader }[] }.\",\n\t\t'',\n\t\t'Example: get_database_cluster({ database_id: \"db_abc\" }) → { members: [...5 live rows], failovers: [] }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId for the HA cluster.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.getCluster(teamId, args.database_id);\n\t\tconst live = result.members.filter((m) => m.leftAt === null);\n\t\tconst summary = `${live.length} live members (${live.map((m) => m.memberRole).join(', ')}); ${result.failovers.length} historical failovers.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeDeploy, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_deploys',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'List deploys for a service in reverse-chronological order (newest first).',\n\t\t'',\n\t\t'When to use: investigating a recent deploy outcome, finding the last successful deploy ID, comparing run-times, or just checking deploy history before triggering a new one.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\"). Use list_services to find it.',\n\t\t'',\n\t\t'Returns: { items: Deploy[] } — each deploy includes id, publicId, status (pending|building|deploying|live|failed|cancelled), commitSha, commitMessage, branch, triggeredBy, startedAt, finishedAt, imageBuildMs (docker build / image-pull only; null on skip-build redeploys — v89), containerBootMs (deploying → live wall-clock; null on builds that failed before container start — v89), buildDurationMs (legacy alias of imageBuildMs kept for back-compat), totalDurationMs (full deploy wall-clock = finishedAt − startedAt). Use imageBuildMs + containerBootMs together to tell \"build is slow\" apart from \"boot is slow\".',\n\t\t'',\n\t\t'Example: list_deploys({ service_id: \"svc_abc\" }) → { items: [{ publicId: \"dpl_…\", status: \"live\", commitMessage: \"Fix login\", … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.list(teamId, args.service_id);\n\t\t// Deploys list is paginated and returns `{ data, page, ... }` — not the\n\t\t// bare `{ deploys: [] }` shape every other list endpoint uses.\n\t\tconst data = shapeList(response, 'data', shapeDeploy);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No deploys yet for service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} deploy${data.items.length === 1 ? '' : 's'} for service ${args.service_id} (page ${response.page} of ${response.totalPages}, ${response.total} total).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch a single deploy by ID, including its current status, commit metadata, and timestamps.',\n\t\t'',\n\t\t\"When to use: drilling into the details of a specific deploy after list_deploys, polling a build's status, or grabbing the commit SHA so you can correlate logs with code.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy (e.g. \"dpl_…\").',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — full deploy record (status, commitSha, commitMessage, branch, imageBuildMs + containerBootMs (v89 split timings; legacy buildDurationMs preserved as alias), totalDurationMs, finishedAt, etc).',\n\t\t'',\n\t\t'Example: get_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { deploy: { status: \"live\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst status =\n\t\t\tdata.deploy && typeof data.deploy === 'object' && 'status' in data.deploy\n\t\t\t\t? (data.deploy as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Deploy ${args.deploy_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'trigger_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Kick off a new deploy. Defaults to the latest commit on the service branch; pass commit_hash or branch to deploy something else.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to deploy (\"ship it\", \"redeploy\", \"kick off a new build\"). For services with auto-deploy enabled, a fresh git push already triggers a deploy automatically — only call this for manual triggers or after a config change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - commit_hash (optional): build a specific commit instead of HEAD on the configured branch.',\n\t\t' - branch (optional): build the tip of a non-default branch.',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy record. Status will start as \"pending\" then transition through \"building\" → \"deploying\" → \"live\" or \"failed\".',\n\t\t'',\n\t\t'Example: trigger_deploy({ service_id: \"svc_abc\" }) → { deploy: { publicId: \"dpl_…\", status: \"pending\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tcommit_hash: z\n\t\t\t.string()\n\t\t\t.max(40)\n\t\t\t.optional()\n\t\t\t.describe('Specific commit to build (default: branch HEAD).'),\n\t\tbranch: z\n\t\t\t.string()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Branch to build (default: service configured branch).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { commitHash?: string; branch?: string } = {};\n\t\tif (args.commit_hash !== undefined) input.commitHash = args.commit_hash;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tconst response = await ctx.hoststack.deploys.trigger(teamId, args.service_id, input);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst publicId =\n\t\t\tdata.deploy && 'publicId' in data.deploy\n\t\t\t\t? (data.deploy as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({\n\t\t\tsummary: `Triggered deploy ${publicId} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'cancel_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Cancel a running deploy. Stops the build/deploy pipeline mid-flight; the previously live revision keeps serving traffic.',\n\t\t'',\n\t\t'When to use: the user notices a bad commit was pushed and wants to abort before it lands, or a build is hanging. Has no effect on already-finished deploys.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to cancel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: cancel_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.deploys.cancel(teamId, args.service_id, args.deploy_id);\n\t\treturn respond({\n\t\t\tsummary: `Cancelled deploy ${args.deploy_id} on service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy_logs',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch the build/deploy logs for a single deploy. Returns the entire log buffer the agent can then search for errors.',\n\t\t'',\n\t\t'When to use: a deploy failed and you need to read the build output to diagnose. Pair with get_deploy to find a failed deploy_id, then call this. For runtime (post-deploy) logs of the running container, use get_service_logs instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy.',\n\t\t'',\n\t\t'Returns: { logs: string, lineCount: number, nextAfterId: number | null } — flattened build output joined with newlines, line count, and a cursor for pagination (pass back as after_id to fetch the next page).',\n\t\t'',\n\t\t'Example: get_deploy_logs({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { logs: \"Building…\\\\nStep 1/8…\\\\n…\", lineCount: 42, nextAfterId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tafter_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.optional()\n\t\t\t.describe('Pagination cursor from a previous response’s nextAfterId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(5000)\n\t\t\t.optional()\n\t\t\t.describe('Max log entries to fetch (default 500, hard cap 5000).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.getLogs(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\t{\n\t\t\t\t...(args.after_id !== undefined ? { afterId: args.after_id } : {}),\n\t\t\t\t...(args.limit !== undefined ? { limit: args.limit } : {}),\n\t\t\t},\n\t\t);\n\t\tconst entries = response.logs;\n\t\tconst logs = entries.map((e) => e.message).join('\\n');\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${entries.length} log line${entries.length === 1 ? '' : 's'} for deploy ${args.deploy_id}.`,\n\t\t\tdata: { logs, lineCount: entries.length, nextAfterId: response.nextAfterId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'diagnose_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t\"One-shot diagnostic bundle for a deploy: full deploy record + tail of build logs + tail of runtime logs starting at the deploy's start time. Lets you investigate any deploy failure mode (build-time failure, health-check timeout, post-deploy runtime crash) without juggling two log endpoints and two IDs.\",\n\t\t'',\n\t\t'When to use: a deploy is failed/superseded/stuck and you want the whole story in one call. For just the build output, use get_deploy_logs. For just runtime logs of the running service, use get_service_logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to diagnose.',\n\t\t' - build_log_lines (optional): how many trailing build log lines to include. Default 200, max 2000.',\n\t\t' - runtime_log_lines (optional): how many trailing runtime log lines (since the deploy started) to include. Default 100, max 1000. Set to 0 to skip runtime logs (saves a round-trip when investigating a pure build-time failure).',\n\t\t'',\n\t\t'Returns: { deploy, build: { logs, lineCount }, runtime: { logs, lineCount } } — runtime is omitted when runtime_log_lines is 0 or the deploy never started a container.',\n\t\t'',\n\t\t'Example: diagnose_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → all three sections in one call.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tbuild_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(2000)\n\t\t\t.optional()\n\t\t\t.describe('Trailing build log lines. Default 200, max 2000.'),\n\t\truntime_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Trailing runtime log lines since deploy start. Default 100, max 1000. 0 = skip.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst buildLimit = args.build_log_lines ?? 200;\n\t\tconst runtimeLimit = args.runtime_log_lines ?? 100;\n\n\t\t// Pull deploy + build logs in parallel — build logs don't depend on\n\t\t// the deploy record's metadata. Runtime logs need the deploy's\n\t\t// startedAt for the `since` window so they wait for the deploy fetch.\n\t\tconst [deployResp, buildResp] = await Promise.all([\n\t\t\tctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id),\n\t\t\tctx.hoststack.deploys.getLogs(teamId, args.service_id, args.deploy_id, {\n\t\t\t\tlimit: buildLimit,\n\t\t\t}),\n\t\t]);\n\n\t\tconst buildEntries = buildResp.logs;\n\t\tconst buildLogs = buildEntries.map((e) => e.message).join('\\n');\n\n\t\tconst deployShape = shapeDeploy(deployResp.deploy);\n\t\tconst deployStatus =\n\t\t\tdeployShape && 'status' in deployShape\n\t\t\t\t? (deployShape as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\tconst startedAtRaw =\n\t\t\tdeployShape && 'startedAt' in deployShape\n\t\t\t\t? (deployShape as { startedAt: unknown }).startedAt\n\t\t\t\t: undefined;\n\t\tconst startedAt = typeof startedAtRaw === 'string' ? startedAtRaw : undefined;\n\n\t\tconst data: Record<string, unknown> = {\n\t\t\tdeploy: deployShape,\n\t\t\tbuild: { logs: buildLogs, lineCount: buildEntries.length },\n\t\t};\n\n\t\t// Skip the runtime tail when caller asked us to OR when the deploy\n\t\t// never reached the container-start phase (build-time failure).\n\t\t// `startedAt` is set by the API when the deploy row is created, so\n\t\t// it's almost always present; the more reliable container-started\n\t\t// signal is whether status ever passed the build phase.\n\t\tconst hadContainer = deployStatus !== 'pending' && deployStatus !== 'building';\n\t\tif (runtimeLimit > 0 && hadContainer) {\n\t\t\tconst runtimeResp = await ctx.hoststack.services.getRuntimeLogs(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\t{\n\t\t\t\t\tlines: runtimeLimit,\n\t\t\t\t\t...(startedAt ? { since: startedAt } : {}),\n\t\t\t\t},\n\t\t\t);\n\t\t\tif ('count' in runtimeResp) {\n\t\t\t\t// getRuntimeLogs without countOnly should never return this\n\t\t\t\t// shape; defensive branch for future-proofing.\n\t\t\t\tdata['runtime'] = { logs: '', lineCount: runtimeResp.count };\n\t\t\t} else {\n\t\t\t\tconst runtimeEntries = Array.isArray(runtimeResp.logs)\n\t\t\t\t\t? runtimeResp.logs\n\t\t\t\t\t: typeof runtimeResp.logs === 'string'\n\t\t\t\t\t\t? runtimeResp.logs.split('\\n').map((line) => ({ message: line }))\n\t\t\t\t\t\t: [];\n\t\t\t\tconst runtimeText = runtimeEntries\n\t\t\t\t\t.map((e) =>\n\t\t\t\t\t\ttypeof e === 'object' && 'message' in e ? String(e.message) : String(e),\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n');\n\t\t\t\tdata['runtime'] = {\n\t\t\t\t\tlogs: runtimeText,\n\t\t\t\t\tlineCount: runtimeEntries.length,\n\t\t\t\t\tsinceWindow: startedAt ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Surface anything that might explain a failure — these field names\n\t\t// vary slightly across deploy phases (build, deploy, healthcheck) so\n\t\t// we look at the deploy shape directly rather than hard-coding.\n\t\tconst failureSummary = shape({\n\t\t\tstatus: deployStatus,\n\t\t\terrorMessage:\n\t\t\t\tdeployShape && 'errorMessage' in deployShape\n\t\t\t\t\t? (deployShape as { errorMessage: unknown }).errorMessage\n\t\t\t\t\t: undefined,\n\t\t});\n\t\tdata['summary'] = failureSummary;\n\n\t\treturn respond({\n\t\t\tsummary: `Diagnostic bundle for deploy ${args.deploy_id} (${deployStatus}): ${buildEntries.length} build log line${buildEntries.length === 1 ? '' : 's'}${runtimeLimit > 0 && hadContainer ? ', runtime log tail included' : ''}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport type { ApiClient } from '../api-client.js';\n\n/**\n * DNS-zone + record management. These tools wrap the same HTTP routes\n * the dashboard uses — `/api/dns-zones/:teamId/...` — so tenancy, audit\n * logging, PowerDNS sync, and idempotency behavior all match the\n * dashboard exactly. We never bypass the service layer.\n *\n * The HTTP-domain tools (list_domains, add_domain, verify_domain,\n * remove_domain) are about binding a hostname to a service for HTTP\n * routing + Let's Encrypt; they don't touch the underlying DNS zone.\n * Use these tools for actual record CRUD (TXT for verification, A/AAAA\n * for custom routing, MX for email, etc.).\n *\n * Source of truth for record types + validation:\n * packages/shared/src/schemas/dns.ts. Mirrored here so the model sees\n * the same enum without the MCP package depending on @hoststack/shared.\n */\nconst DNS_RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS', 'SRV', 'CAA', 'ALIAS'] as const;\n\ninterface ZoneResponse {\n\tpublicId: string;\n\tid: number;\n\tdomainName: string;\n\tstatus: string;\n}\n\ninterface RecordResponse {\n\tpublicId: string;\n\tid: number;\n\tzoneId: number;\n\ttype: string;\n\tname: string;\n\tvalue: string;\n}\n\ninterface ListZonesResponse {\n\tzones: ZoneResponse[];\n}\n\ninterface ListRecordsResponse {\n\trecords: RecordResponse[];\n}\n\ninterface SingleRecordResponse {\n\trecord: RecordResponse;\n}\n\n/**\n * Resolve `{ zone_id?, domain? }` to the zone's `publicId`. Accepts\n * either the zone publicId directly or a domain name (apex or\n * subdomain) and walks parent labels to find the longest-matching\n * hosted zone, same suffix-match semantics as\n * `DnsService.findHostedZoneForDomain` (apps/api/src/services/dns.service.ts:193).\n *\n * Returns the zone's publicId so the caller can address subsequent\n * routes by it, plus a human-readable hint about which zone matched.\n */\nasync function resolveZonePublicId(\n\tapi: ApiClient,\n\tteamId: number,\n\tinput: { zone_id?: string; domain?: string },\n): Promise<{ publicId: string; domainName: string }> {\n\tif (input.zone_id) {\n\t\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tconst match = zones.find((z) => z.publicId === input.zone_id);\n\t\tif (!match) {\n\t\t\tthrow new Error(`Zone ${input.zone_id} not found on this team.`);\n\t\t}\n\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t}\n\tif (!input.domain) {\n\t\tthrow new Error('Provide either zone_id or domain.');\n\t}\n\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\tconst fqdn = input.domain.toLowerCase().replace(/\\.$/, '');\n\tconst labels = fqdn.split('.');\n\tfor (let i = 0; i < labels.length - 1; i++) {\n\t\tconst candidate = labels.slice(i).join('.');\n\t\tconst match = zones.find((z) => z.domainName.toLowerCase() === candidate);\n\t\tif (match && match.status !== 'deleting') {\n\t\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t\t}\n\t}\n\tthrow new Error(\n\t\t`No hosted zone matches ${input.domain}. Create the zone in the dashboard (Domains → DNS) first, then retry.`,\n\t);\n}\n\ndefineTool({\n\tname: 'list_dns_zones',\n\tcategory: 'dns',\n\tdescription: [\n\t\t\"List authoritative DNS zones the team owns on HostStack's PowerDNS infrastructure. Each zone is the apex domain (e.g. example.com) under which DNS records live.\",\n\t\t'',\n\t\t'When to use: discover which apex domains support record management before calling create_dns_record / list_dns_records. The dashboard equivalent is Domains → DNS.',\n\t\t'',\n\t\t'Returns: { items: Zone[] } — each zone exposes publicId, domainName, status (\"active\" | \"syncing\" | \"failed\" | \"deleting\"), nsRecords (the nameservers the parent registry must delegate to), provider, createdAt.',\n\t\t'',\n\t\t'Example: list_dns_zones() → { items: [{ publicId: \"dnz_abc\", domainName: \"micci.dk\", status: \"active\", nsRecords: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"] }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tconst items = Array.isArray(response.zones) ? response.zones.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No DNS zones hosted on this team. Create one in the dashboard (Domains → DNS).'\n\t\t\t\t: `Found ${items.length} hosted DNS zone${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { items } });\n\t},\n});\n\ndefineTool({\n\tname: 'list_dns_records',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'List every active record in a hosted DNS zone. Records are addressed by their type + name + value tuple; PowerDNS gathers records with the same (name, type) into an RRset on the wire.',\n\t\t'',\n\t\t'When to use: audit what is currently published for a zone before editing, verify a recently-created record landed, or read existing TXT verification strings.',\n\t\t'',\n\t\t'Provide EITHER zone_id (the zone publicId, e.g. \"dnz_abc\") OR domain (an apex or subdomain — the longest-matching hosted apex wins; \"app.example.com\" matches a hosted \"example.com\" zone).',\n\t\t'',\n\t\t'Returns: { zone: { publicId, domainName }, items: Record[] } — each record includes publicId, type, name (\"@\" for apex, or label), value, ttl, priority?, status (\"active\" | \"syncing\" | \"failed\"), managedBy (\"user\" | \"hoststack\" | \"poststack\"), createdAt.',\n\t\t'',\n\t\t'Example: list_dns_records({ domain: \"micci.dk\" }) → { zone: { domainName: \"micci.dk\", … }, items: [{ type: \"TXT\", name: \"@\", value: \"google-site-verification=…\", ttl: 300, … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId (e.g. \"dnz_abc\").'),\n\t\tdomain: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Apex or subdomain — resolves to the longest-matching hosted zone.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.api, teamId, zoneInput);\n\t\tconst response = await ctx.api.get<ListRecordsResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t);\n\t\tconst items = Array.isArray(response.records) ? response.records.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `Zone ${zone.domainName} has no records yet.`\n\t\t\t\t: `Returned ${items.length} record${items.length === 1 ? '' : 's'} for ${zone.domainName}.`;\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: { zone: { publicId: zone.publicId, domainName: zone.domainName }, items },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Fetch a single DNS record by its publicId. Useful for confirming the record landed after create_dns_record / update_dns_record (status transitions from \"syncing\" to \"active\" once PowerDNS acknowledges).',\n\t\t'',\n\t\t'When to use: verify a record exists, inspect its current ttl/value, or check the sync status of an in-flight write.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\"). Returned by list_dns_records and create_dns_record.',\n\t\t'',\n\t\t'Returns: { record: Record }. Mirrors the shape returned by list_dns_records.',\n\t\t'',\n\t\t'Example: get_dns_record({ record_id: \"dnr_abc\" }) → { record: { type: \"TXT\", name: \"@\", value: \"…\", status: \"active\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Records have no direct GET-by-id route — walk the team's zones\n\t\t// and scan each for the matching publicId. Zones-per-team is tiny\n\t\t// (≤ a few dozen even on heavy users) so this is cheap.\n\t\tconst { zones } = await ctx.api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tfor (const zone of zones) {\n\t\t\tconst { records } = await ctx.api.get<ListRecordsResponse>(\n\t\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t\t);\n\t\t\tconst match = records.find((r) => r.publicId === args.record_id);\n\t\t\tif (match) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: `Record ${match.type} ${match.name} on ${zone.domainName}.`,\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\t\t\trecord: shape(match),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn respondError(`Record ${args.record_id} not found on any zone owned by this team.`);\n\t},\n});\n\ndefineTool({\n\tname: 'create_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Create a DNS record on a hosted zone. Writes to the DB first, then mirrors to PowerDNS — record returns with status=\"syncing\" until the provider acknowledges, then flips to \"active\". Re-creating the same (zone, type, name, value) tuple is idempotent and returns the existing row.',\n\t\t'',\n\t\t'When to use: add TXT verification records (Google Search Console, domain ownership challenges, DKIM), A/AAAA records pointing a subdomain at an IP, CNAME aliases, MX records, etc.',\n\t\t'',\n\t\t'Provide EITHER zone_id (zone publicId) OR domain (resolves via longest-suffix match against hosted zones).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"A\" | \"AAAA\" | \"CNAME\" | \"MX\" | \"TXT\" | \"NS\" | \"SRV\" | \"CAA\" | \"ALIAS\".',\n\t\t' - name: \"@\" for the zone apex, or a subdomain label (\"www\", \"api\", \"_acme-challenge\"). Do NOT include the zone suffix — the backend prepends it. \"bounce\" on zone \"micci.dk\" publishes \"bounce.micci.dk\"; passing \"bounce.micci.dk\" works (the backend strips the suffix) but is non-idiomatic.',\n\t\t' - content: the literal record value. For TXT pass the unquoted string — the PowerDNS layer handles quoting. For CNAME / MX / NS / SRV / ALIAS the target may be passed with or without a trailing dot; the adapter FQDN-izes it.',\n\t\t' - ttl (optional): 60–86400 seconds, default 3600.',\n\t\t' - priority (optional, required for MX and SRV): 0–65535.',\n\t\t'',\n\t\t'Returns: { record: Record } — see get_dns_record for the shape.',\n\t\t'',\n\t\t'Notes:',\n\t\t' - CNAME at the apex is rejected (RFC 1034 forbids CNAME coexisting with NS/SOA). Use type=\"ALIAS\" for apex aliasing.',\n\t\t'',\n\t\t'Example: create_dns_record({ domain: \"micci.dk\", type: \"TXT\", name: \"@\", content: \"google-site-verification=C0V0scK48g2…\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId.'),\n\t\tdomain: z.string().optional().describe('Apex or subdomain to resolve a hosted zone.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT values are unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\t// MX/SRV records carry priority on the wire; the shared upsert\n\t\t// schema enforces this with a superRefine. The MCP `input` shape\n\t\t// is a flat ZodRawShape so we can't express cross-field rules\n\t\t// there — surface a clean error from the handler instead of\n\t\t// letting the backend's 400 bubble up.\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.api, teamId, zoneInput);\n\n\t\tconst body: {\n\t\t\ttype: string;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.api.post<SingleRecordResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} ${args.name} on ${zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Update an existing DNS record by its publicId. Use this to change a TTL, swap a value, or move the record to a different (name, type) bucket. Full-record PUT semantics: every mutable field must be provided.',\n\t\t'',\n\t\t'When to use: rotate a DKIM key, repoint an A record at a new IP, bump TTL ahead of a planned change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t' - type, name, content: the new values (the route is full-replace, not patch — fetch with get_dns_record first if you only want to tweak one field).',\n\t\t' - ttl (optional), priority (optional, required for MX / SRV).',\n\t\t'',\n\t\t'Returns: { record: Record } reflecting the new state.',\n\t\t'',\n\t\t'Example: update_dns_record({ record_id: \"dnr_abc\", type: \"A\", name: \"api\", content: \"203.0.113.42\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.api, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\n\t\tconst body: {\n\t\t\ttype: string;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.api.put<SingleRecordResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${target.zone.publicId}/records/${args.record_id}`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Updated ${args.type} ${args.name} on ${target.zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Soft-delete a DNS record and remove it from PowerDNS. DESTRUCTIVE: resolvers stop returning the value as soon as the change propagates. Same code path as the dashboard delete button — the row is marked deleted and the RRset is re-pushed without it.',\n\t\t'',\n\t\t'When to use: retire a TXT verification record once the challenge has been confirmed; drop an A record for a decommissioned subdomain.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_dns_record({ record_id: \"dnr_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.api, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tawait ctx.api.delete<{ ok: true }>(\n\t\t\t`/api/dns-zones/${teamId}/${target.zone.publicId}/records/${args.record_id}`,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Deleted record ${args.record_id} from ${target.zone.domainName}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\n/**\n * The DNS HTTP routes nest records under their zone, so update/delete\n * by record publicId needs the owning zone's publicId too. Walk the\n * team's zones until we find a record match. Cheap — zone count is\n * bounded by team-tier limits.\n */\nasync function findRecordZone(\n\tapi: ApiClient,\n\tteamId: number,\n\trecordPublicId: string,\n): Promise<{ zone: ZoneResponse; record: RecordResponse } | null> {\n\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\tfor (const zone of zones) {\n\t\tconst { records } = await api.get<ListRecordsResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t);\n\t\tconst match = records.find((r) => r.publicId === recordPublicId);\n\t\tif (match) return { zone, record: match };\n\t}\n\treturn null;\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDomain, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_domains',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"List every custom domain attached to the team's services.\",\n\t\t'',\n\t\t'When to use: enumerate live domains, audit DNS verification status, or find which service a hostname resolves to before troubleshooting routing.',\n\t\t'',\n\t\t'Returns: { items: Domain[] } — id, publicId, hostname, serviceId, verified, dnsTargets, sslStatus, createdAt.',\n\t\t'',\n\t\t'Example: list_domains() → { items: [{ hostname: \"api.example.com\", verified: true, sslStatus: \"active\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.list(teamId);\n\t\tconst data = shapeList(response, 'domains', shapeDomain);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No custom domains configured.'\n\t\t\t\t: `Found ${data.items.length} custom domain${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'add_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Attach a custom hostname to the team — optionally pinned to a specific service. After adding, point your DNS at the targets returned and call verify_domain.',\n\t\t'',\n\t\t'When to use: the user wants to put a custom domain in front of a HostStack service (e.g. point api.example.com at svc_abc). The hostname must be unique across the team.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - hostname: the domain to add (e.g. \"api.example.com\").',\n\t\t' - service_id: publicId of the service to bind the domain to.',\n\t\t' - path_prefix (optional): when set, only requests under this URL prefix route to this service. Use for path-based fan-out (e.g. `/api` to an api service, `/` to a web service on the same hostname).',\n\t\t'',\n\t\t'Returns: { domain: Domain } — includes dnsTargets you must configure (CNAME / A records).',\n\t\t'',\n\t\t'Example: add_domain({ hostname: \"api.example.com\", service_id: \"svc_abc\" }) → { domain: { hostname: \"api.example.com\", dnsTargets: [...], verified: false, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\thostname: z\n\t\t\t.string()\n\t\t\t.min(3)\n\t\t\t.max(253)\n\t\t\t.describe('Fully-qualified hostname (e.g. api.example.com).'),\n\t\tservice_id: z.string().describe('Service publicId to bind to.'),\n\t\tpath_prefix: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Optional URL prefix for path-based routing (e.g. \"/api\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { domain: string; serviceId: string; pathPrefix?: string } = {\n\t\t\tdomain: args.hostname,\n\t\t\tserviceId: args.service_id,\n\t\t};\n\t\tif (args.path_prefix !== undefined) input.pathPrefix = args.path_prefix;\n\t\tconst response = await ctx.hoststack.domains.add(teamId, input);\n\t\tconst data = { domain: shapeDomain(response.domain) };\n\t\treturn respond({\n\t\t\tsummary: `Added domain ${args.hostname}. Configure DNS, then call verify_domain.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Trigger DNS verification for a previously-added domain. HostStack checks that the dnsTargets returned by add_domain are configured at the registrar.',\n\t\t'',\n\t\t'When to use: the user has set up DNS records and is ready to flip the domain live. Returns success when verification passes; the domain stays in pending state if DNS is still propagating.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains or add_domain).',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-call list_domains to inspect the updated verified flag and SSL status.',\n\t\t'',\n\t\t'Example: verify_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.verify(teamId, args.domain_id);\n\t\treturn respond({\n\t\t\tsummary: `Triggered DNS verification for ${args.domain_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'remove_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Detach a custom hostname from the team. DESTRUCTIVE: the domain stops routing immediately and any pinned service must fall back to its default hostname.',\n\t\t'',\n\t\t'When to use: the user wants to retire a custom domain. Confirm with the user first — the change is immediate and cannot be undone except by re-adding the domain and re-verifying DNS.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain to remove.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: remove_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.remove(teamId, args.domain_id);\n\t\treturn respond({ summary: `Removed domain ${args.domain_id}.`, data: { ok: true } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shapeEnvVar, shapeList } from '../lib/shape.js';\n\ninterface ExistingEnvVar {\n\tid: number;\n\tpublicId?: string;\n\tkey: string;\n\tisSecret: boolean;\n}\n\ndefineTool({\n\tname: 'list_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'List environment variables for a service. Secret values are masked server-side (\"••••••\"); only non-secret values come through in the clear.',\n\t\t'',\n\t\t'When to use: auditing what configuration a service has, finding the key for a value the user mentions by name, or confirming a variable was set after a write. Never assume the value field is plaintext for secret rows — it is masked.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: EnvVar[] } — id, publicId, key, value (masked if isSecret), isSecret, target, linkedDatabaseId, createdAt.',\n\t\t'',\n\t\t'Example: list_env_vars({ service_id: \"svc_abc\" }) → { items: [{ key: \"DATABASE_URL\", value: \"••••••\", isSecret: true }, { key: \"PORT\", value: \"3000\", isSecret: false }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'envVars', shapeEnvVar);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No env vars set on service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} env var${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'set_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Upsert a single environment variable on a service: creates the key if missing, updates the value if it already exists. Match is by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: rotate a secret, add a new config knob, or change a value the user dictated. The MCP layer looks up the existing var by key first; you do not need to know the env-var ID.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name (e.g. \"DATABASE_URL\").',\n\t\t' - value: new value (will be encrypted at rest if is_secret=true).',\n\t\t' - is_secret (optional): true marks the value as secret (masked on read). On create, defaults to true for safety. On update, omitting it leaves the existing flag untouched — pass it explicitly only when you want to change classification.',\n\t\t'',\n\t\t'Returns: { envVar: EnvVar, action: \"created\" | \"updated\" }.',\n\t\t'',\n\t\t'Example: set_env_var({ service_id: \"svc_abc\", key: \"DATABASE_URL\", value: \"postgres://…\", is_secret: true }) → { envVar: { key: \"DATABASE_URL\", value: \"••••••\", … }, action: \"updated\" }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key.'),\n\t\tvalue: z.string().describe('New value.'),\n\t\tis_secret: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Mark as secret (encrypted, masked on read). Default true.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\n\t\tif (match) {\n\t\t\t// On update, only forward `isSecret` when the caller passed it\n\t\t\t// explicitly. Defaulting to `true` here would silently flip\n\t\t\t// previously-plaintext keys (PORT, NODE_ENV, …) to secret on\n\t\t\t// every value change.\n\t\t\tconst updatePayload: { value: string; isSecret?: boolean } = { value: args.value };\n\t\t\tif (args.is_secret !== undefined) updatePayload.isSecret = args.is_secret;\n\t\t\tconst response = await ctx.hoststack.envVars.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tString(match.id),\n\t\t\t\tupdatePayload,\n\t\t\t);\n\t\t\tconst data = {\n\t\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\t\taction: 'updated' as const,\n\t\t\t};\n\t\t\treturn respond({ summary: `Updated ${args.key} on service ${args.service_id}.`, data });\n\t\t}\n\n\t\tconst response = await ctx.hoststack.envVars.create(teamId, args.service_id, {\n\t\t\tkey: args.key,\n\t\t\tvalue: args.value,\n\t\t\tisSecret: args.is_secret ?? true,\n\t\t});\n\t\tconst data = {\n\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\taction: 'created' as const,\n\t\t};\n\t\treturn respond({ summary: `Created ${args.key} on service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Remove an environment variable from a service by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: cleaning up unused config, or rotating away from a leaked secret. The MCP looks up the env-var ID from the key automatically.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name to delete.',\n\t\t'',\n\t\t'Returns: { ok: true } on success. Returns an error if the key was not found.',\n\t\t'',\n\t\t'Example: delete_env_var({ service_id: \"svc_abc\", key: \"OLD_FLAG\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key to delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\t\tif (!match) {\n\t\t\treturn respondError(`Env var \"${args.key}\" not found on service ${args.service_id}.`, {\n\t\t\t\tkey: args.key,\n\t\t\t\tservice_id: args.service_id,\n\t\t\t});\n\t\t}\n\t\tawait ctx.hoststack.envVars.delete(teamId, args.service_id, String(match.id));\n\t\treturn respond({\n\t\t\tsummary: `Deleted ${args.key} from service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'bulk_set_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Replace the entire env-var set on a service with the given list. Equivalent to deleting all current vars and creating the supplied ones — use with care.',\n\t\t'',\n\t\t'When to use: importing a .env file, mirroring config from a sibling service, or doing a clean reset. For incremental changes, use set_env_var per key — bulk_set is destructive for any key not in the supplied list.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - env_vars: array of { key, value, is_secret? }. is_secret defaults to true per row.',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-list with list_env_vars to confirm the new state.',\n\t\t'',\n\t\t'Example: bulk_set_env_vars({ service_id: \"svc_abc\", env_vars: [{ key: \"PORT\", value: \"3000\", is_secret: false }, { key: \"DATABASE_URL\", value: \"…\", is_secret: true }] }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tenv_vars: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tkey: z.string().min(1).max(128),\n\t\t\t\t\tvalue: z.string(),\n\t\t\t\t\tis_secret: z.boolean().optional(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(500)\n\t\t\t.describe('Array of env-var rows. Hard cap 500.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst payload = {\n\t\t\tvars: args.env_vars.map((v) => {\n\t\t\t\tconst row: { key: string; value: string; isSecret?: boolean } = {\n\t\t\t\t\tkey: v.key,\n\t\t\t\t\tvalue: v.value,\n\t\t\t\t};\n\t\t\t\tif (v.is_secret !== undefined) row.isSecret = v.is_secret;\n\t\t\t\treturn row;\n\t\t\t}),\n\t\t};\n\t\tawait ctx.hoststack.envVars.bulkSet(teamId, args.service_id, payload);\n\t\treturn respond({\n\t\t\tsummary: `Replaced env-var set on service ${args.service_id} with ${args.env_vars.length} entr${args.env_vars.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata: { ok: true, count: args.env_vars.length },\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * v66 P5: MCP tools for project environment management.\n *\n * Environments isolate services in the same project — staging vs\n * production, for example. Each project automatically gets a Production\n * environment; users can add staging/development/preview envs to run\n * sibling services side-by-side and promote builds between them.\n */\n\ndefineTool({\n\tname: 'list_environments',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'List every environment for a project (production / staging / development / preview).',\n\t\t'',\n\t\t'When to use: the user wants to see what envs exist before creating a service in one or promoting a deploy. Every project has at least Production.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { items: Environment[] } — id, publicId, name, type, isDefault, isProtected.',\n\t\t'',\n\t\t'Example: list_environments({ project_id: \"prj_abc\" }) → { items: [{ name: \"Production\", type: \"production\", isDefault: true }, { name: \"Staging\", type: \"staging\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.environments.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'environments', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No environments yet — every project should have Production by default.'\n\t\t\t\t: `Found ${data.items.length} environment${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Create a new environment in a project (e.g. Staging, QA, Preview).',\n\t\t'',\n\t\t'When to use: the user wants to add an env so they can run a sibling service alongside production for testing or staging before release.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - name: human-readable name (1–64 chars). Shown in the env switcher.',\n\t\t' - type: \"production\" | \"staging\" | \"development\" | \"preview\". Determines the hostname suffix on services in this env (production stays clean; others get -staging / -dev / -preview).',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env. Default false.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: create_environment({ project_id: \"prj_abc\", name: \"Staging\", type: \"staging\" }) → { environment: { id: 7, publicId: \"env_…\", name: \"Staging\", type: \"staging\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(64).describe('Environment name (1–64 chars).'),\n\t\ttype: z\n\t\t\t.enum(['production', 'staging', 'development', 'preview'])\n\t\t\t.describe('Environment type — drives the hostname suffix.'),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions. Default false.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname: string;\n\t\t\ttype: 'production' | 'staging' | 'development' | 'preview';\n\t\t\tisProtected?: boolean;\n\t\t} = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t};\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tconst response = await ctx.hoststack.environments.create(teamId, args.project_id, input);\n\t\tconst data = { environment: shape(response.environment) };\n\t\treturn respond({\n\t\t\tsummary: `Created environment \"${args.name}\" (${args.type}) in project ${args.project_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Delete an environment from a project.',\n\t\t'',\n\t\t'When to use: an env is no longer used (e.g. tearing down a feature branch staging). Blocked when the env still has live services or databases attached — destroy or move them first. The default env (usually Production) cannot be deleted.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { success: true } on success.',\n\t\t'',\n\t\t'Example: delete_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\" }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.environments.delete(teamId, args.project_id, args.environment_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted environment ${String(args.environment_id)} from project ${args.project_id}.`,\n\t\t\tdata: { success: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'promote_deploy',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Promote a built deploy from one environment to another (build-once, run-many).',\n\t\t'',\n\t\t'When to use: the user has tested a deploy in staging and wants to ship that exact image to production without rebuilding. Atomic and image-based — same docker image runs on the sibling service in the target env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: source service publicId (the one that owns the deploy).',\n\t\t' - deploy_id: source deploy publicId. Must have `dockerImageId` set (i.e. a successful build).',\n\t\t\" - target_environment_id: env publicId or numeric id to promote into. Must be in the same project. Cannot be the source service's own env.\",\n\t\t'',\n\t\t\"Behaviour: if a sibling service with the same name already exists in the target env, the new deploy lands on it. Otherwise the API auto-clones the source service's build/runtime config into the target env first. Env-specific config (env vars, secret files, volumes, IP allowlists, custom domains) is NOT copied — those are per-env by design.\",\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy on the target service.',\n\t\t'',\n\t\t'Example: promote_deploy({ service_id: \"svc_staging\", deploy_id: \"dpl_built\", target_environment_id: \"env_prod\" }) → { deploy: { id: 99, status: \"pending\", trigger: \"rollback\", dockerImageId: \"sha256:…\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Source service publicId.'),\n\t\tdeploy_id: z.string().describe('Source deploy publicId (must have a built image).'),\n\t\ttarget_environment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Target environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Resolve target env to numeric id; the API expects a number.\n\t\tconst targetEnvId = await ctx.hoststack.resolveId(args.target_environment_id, {\n\t\t\tkind: 'environment',\n\t\t\tteamId: await ctx.hoststack.resolveId(teamId, { kind: 'team' }),\n\t\t});\n\t\tconst response = await ctx.hoststack.deploys.promote(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\ttargetEnvId,\n\t\t);\n\t\tconst data = { deploy: shape(response.deploy) };\n\t\treturn respond({\n\t\t\tsummary: `Promoted deploy ${args.deploy_id} from service ${args.service_id} to env ${String(args.target_environment_id)}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeTeam, shapeUser } from '../lib/shape.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineTool({\n\tname: 'get_me',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Return the user and team identity bound to the current API key. Useful at the start of a conversation to confirm which account the agent is operating on, especially when a user has multiple HostStack environments.',\n\t\t'',\n\t\t'When to use: orient yourself at the start of a session, confirm which team the API key targets, or surface the email/team-name in a reply to the user.',\n\t\t'',\n\t\t'Returns: { user: { id, email, name, ... }, team?: { id, publicId, name, plan, ... } }.',\n\t\t'',\n\t\t'Example: get_me() → { user: { id: 1, email: \"ada@…\", … }, team: { id: 7, name: \"acme\", plan: \"pro\" } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst me = await ctx.api.get<MeResponse>('/api/auth/me');\n\t\tconst data: { user: ReturnType<typeof shapeUser>; team?: ReturnType<typeof shapeTeam> } = {\n\t\t\tuser: shapeUser(me.user),\n\t\t};\n\t\tif (me.team) data.team = shapeTeam(me.team);\n\t\tconst teamLabel =\n\t\t\tme.team && typeof me.team === 'object' && 'name' in me.team\n\t\t\t\t? ` on team ${(me.team as { name: string }).name}`\n\t\t\t\t: '';\n\t\tconst userEmail =\n\t\t\tme.user && typeof me.user === 'object' && 'email' in me.user\n\t\t\t\t? (me.user as { email: string }).email\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Authenticated as ${userEmail}${teamLabel}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\n/**\n * Source of truth: packages/shared/src/schemas/notification-channel.ts.\n * Kept in sync manually because the MCP package's Zod schemas are\n * what the model sees — pulling these from `@hoststack/shared` at\n * runtime would add a dependency that the published `@hoststack.dev/mcp`\n * shouldn't carry.\n */\nconst NOTIFICATION_EVENTS = [\n\t'deploy.started',\n\t'deploy.succeeded',\n\t'deploy.failed',\n\t'deploy.failed_consecutive',\n\t'service.created',\n\t'service.deleted',\n\t'service.suspended',\n\t'service.resumed',\n\t'service.restart_failed',\n\t'service.auto_suspended',\n\t'service.acme_cert_failed',\n\t'git.auth_failed',\n] as const;\n\ndefineTool({\n\tname: 'list_notification_channels',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List configured notification channels for the team (Slack/Discord webhooks + email recipients). Each channel has a list of events it subscribes to — fire only happens when an event in that list occurs.',\n\t\t'',\n\t\t\"When to use: setting up alerts ('which channels are notifying us on deploy failures?'), auditing why someone got/didn't get paged, or before subscribing a new event.\",\n\t\t'',\n\t\t'Returns: { items: Channel[] } — each channel includes id, type, name, webhookUrl (masked), active, events, createdAt.',\n\t\t'',\n\t\t\"Example: list_notification_channels() → { items: [{ id: 3, type: 'slack', name: 'eng-alerts', events: ['deploy.failed', 'git.auth_failed'], … }] }\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.listChannels(teamId);\n\t\tconst data = shapeList(response, 'channels', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No notification channels yet — create one with create_notification_channel.'\n\t\t\t\t: `Found ${data.items.length} notification channel${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Create a Slack, Discord, or email notification channel and subscribe it to a list of events.',\n\t\t'',\n\t\t'When to use: setting up alert delivery for a team — paging the on-call when a deploy fails, posting to #incidents when git auth breaks, emailing the owner on ACME renewal failure.',\n\t\t'',\n\t\t'For type=email, `webhook_url` is the recipient email address. For type=slack or type=discord, it is the incoming webhook URL. The URL is stored encrypted server-side and masked in list responses.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"slack\" | \"discord\" | \"email\".',\n\t\t' - name: human-readable label (1–128 chars).',\n\t\t' - webhook_url: Slack/Discord webhook URL OR email address.',\n\t\t' - events: list of event names to subscribe to. Pass an empty list to create a channel that fires for nothing (manual subscribe later with update_notification_channel).',\n\t\t'',\n\t\t'Valid events: deploy.started, deploy.succeeded, deploy.failed, deploy.failed_consecutive, service.created, service.deleted, service.suspended, service.resumed, service.restart_failed, service.auto_suspended, service.acme_cert_failed, git.auth_failed.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: create_notification_channel({ type: 'slack', name: 'eng-alerts', webhook_url: 'https://hooks.slack.com/…', events: ['deploy.failed', 'git.auth_failed', 'service.restart_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\ttype: z.enum(['slack', 'discord', 'email']).describe('Channel type.'),\n\t\tname: z.string().min(1).max(128).describe('Human-readable label.'),\n\t\twebhook_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.describe('Slack/Discord webhook URL or email address (when type=email).'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.describe(\n\t\t\t\t'List of events the channel subscribes to. Empty list = subscribe to nothing.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.createChannel(teamId, {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\twebhookUrl: args.webhook_url,\n\t\t\tevents: args.events,\n\t\t});\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} channel \"${args.name}\" subscribed to ${args.events.length} event${args.events.length === 1 ? '' : 's'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Update a notification channel: rename, enable/disable, or change its event subscription list. The webhook URL and type are immutable — create a new channel if those need to change.',\n\t\t'',\n\t\t'When to use: silencing a noisy channel (active=false), adding a new event to an existing list, or relabeling.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t' - name (optional): new label.',\n\t\t' - active (optional): false to silence, true to re-enable.',\n\t\t' - events (optional): replace the full subscription list. Passing [] silences all events without disabling the channel.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: update_notification_channel({ channel_id: 3, events: ['deploy.failed', 'service.restart_failed', 'git.auth_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric channel id from list_notification_channels.'),\n\t\tname: z.string().min(1).max(128).optional().describe('New label.'),\n\t\tactive: z.boolean().optional().describe('false silences without deleting.'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.optional()\n\t\t\t.describe('Replaces the full subscription list.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst update: { name?: string; active?: boolean; events?: readonly string[] } = {};\n\t\tif (args.name !== undefined) update.name = args.name;\n\t\tif (args.active !== undefined) update.active = args.active;\n\t\tif (args.events !== undefined) update.events = args.events;\n\n\t\tif (Object.keys(update).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\t// Cast: SDK's events type is the typed enum array; passing through\n\t\t// from the Zod-validated args is safe at the type level even if\n\t\t// TS can't bridge the readonly tuple to the SDK signature.\n\t\tconst response = await ctx.hoststack.notifications.updateChannel(\n\t\t\tteamId,\n\t\t\targs.channel_id,\n\t\t\tupdate as Parameters<typeof ctx.hoststack.notifications.updateChannel>[2],\n\t\t);\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Updated notification channel #${args.channel_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Permanently delete a notification channel. Use update_notification_channel({ active: false }) to silence without deleting if you might want it back later.',\n\t\t'',\n\t\t'When to use: a channel is no longer needed (decommissioned Slack workspace, team member left, webhook leaked) and you want it gone from the team config entirely.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_notification_channel({ channel_id: 3 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.notifications.deleteChannel(teamId, args.channel_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted notification channel #${args.channel_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'test_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Fire a synthetic test event to the channel so the user can confirm the webhook is wired up correctly. The webhook receives an unmistakeable \"test message\" payload.',\n\t\t'',\n\t\t\"When to use: after creating a channel, or when alerts stop arriving — quickest way to tell whether the issue is on HostStack's side or the channel's.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { success: boolean, error?: string } — false + error string when the webhook returned non-2xx.',\n\t\t'',\n\t\t'Example: test_notification_channel({ channel_id: 3 }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.testChannel(teamId, args.channel_id);\n\t\tconst data = shape(response);\n\t\tconst okFlag =\n\t\t\tdata && typeof data === 'object' && 'success' in data\n\t\t\t\t? (data as { success: boolean }).success\n\t\t\t\t: false;\n\t\treturn respond({\n\t\t\tsummary: okFlag\n\t\t\t\t? `Test event delivered to channel #${args.channel_id}.`\n\t\t\t\t: `Test event FAILED for channel #${args.channel_id}: ${(data as { error?: string }).error ?? 'unknown error'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeList, shapeProject } from '../lib/shape.js';\n\n// HostStack region IDs (matches @hoststack/shared REGION_IDS and the SDK's\n// internal RegionId). The SDK narrows region to this union but doesn't export\n// the type name, so we mirror the literals here — structurally identical, so\n// it stays assignable to the SDK's create-project param.\nconst REGION_IDS = ['eu-central-1', 'eu-central-2', 'eu-west-1', 'us-east-1'] as const;\ntype RegionId = (typeof REGION_IDS)[number];\n\ndefineTool({\n\tname: 'list_projects',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'List every project in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs an overview of what projects exist before drilling into services, deploys, or databases. Use this as the first step when the user mentions a project by name and you need to resolve its ID.',\n\t\t'',\n\t\t'Returns: { items: Project[] } — each project includes id, publicId, name, description, createdAt.',\n\t\t'',\n\t\t'Example: list_projects() → { items: [{ id: 12, publicId: \"prj_…\", name: \"billing-api\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.list(teamId);\n\t\tconst data = shapeList(response, 'projects', shapeProject);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No projects yet — create one in the dashboard or via create_project.'\n\t\t\t\t: `Found ${data.items.length} project${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Create a new project (logical grouping of services + databases).',\n\t\t'',\n\t\t\"When to use: the user wants to set up a new app or environment in HostStack. Projects are a free organisational layer — they don't cost anything until you add services or databases inside them.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name: human-readable project name (1–60 chars).',\n\t\t' - description (optional): short blurb shown in the dashboard.',\n\t\t' - region (optional): \"eu-central-1\" (Falkenstein) | \"eu-central-2\" (Nuremberg) | \"eu-west-1\" (Helsinki) | \"us-east-1\" (Ashburn). Defaults to eu-central-2.',\n\t\t'',\n\t\t'Returns: { project: Project } — includes the new id and publicId.',\n\t\t'',\n\t\t'Example: create_project({ name: \"billing-api\", description: \"Stripe webhooks\", region: \"eu-central-1\" }) → { project: { id: 12, publicId: \"prj_…\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z.string().min(1).max(60).describe('Project name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('Short description (≤500 chars).'),\n\t\tregion: z\n\t\t\t.enum(REGION_IDS)\n\t\t\t.optional()\n\t\t\t.describe('Region: eu-central-1 | eu-central-2 | eu-west-1 | us-east-1.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; description?: string; region?: RegionId } = { name: args.name };\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tif (args.region !== undefined) input.region = args.region;\n\t\tconst response = await ctx.hoststack.projects.create(teamId, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst publicId =\n\t\t\tdata.project && 'publicId' in data.project\n\t\t\t\t? (data.project as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created project \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Rename a project or update its description.',\n\t\t'',\n\t\t'When to use: the user wants to fix a typo in a project name, attach a clearer description, or align the dashboard label with their internal naming.',\n\t\t'',\n\t\t'Inputs (all optional, at least one required):',\n\t\t' - project_id: publicId of the project (required).',\n\t\t' - name: new name (1–60 chars).',\n\t\t' - description: new description (≤500 chars).',\n\t\t'',\n\t\t'Returns: { project: Project } — the updated record.',\n\t\t'',\n\t\t'Example: update_project({ project_id: \"prj_abc\", name: \"billing-prod\" }) → { project: { name: \"billing-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(60).optional().describe('New name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('New description (≤500 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif (args.name === undefined && args.description === undefined) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No fields to update — pass `name` and/or `description`.',\n\t\t\t\tdata: {},\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; description?: string } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tconst response = await ctx.hoststack.projects.update(teamId, args.project_id, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\treturn respond({ summary: `Updated project ${args.project_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Fetch a single project by ID. Includes name, description, and timestamps.',\n\t\t'',\n\t\t'When to use: confirming a project exists before creating resources inside it, or pulling the canonical name/description for a reply to the user.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: publicId of the project (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { project: Project }.',\n\t\t'',\n\t\t'Example: get_project({ project_id: \"prj_abc\" }) → { project: { id: 12, name: \"billing\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId (e.g. prj_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.get(teamId, args.project_id);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst name =\n\t\t\tdata.project && 'name' in data.project\n\t\t\t\t? (data.project as { name: string }).name\n\t\t\t\t: args.project_id;\n\t\treturn respond({ summary: `Project \"${name}\".`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeService } from '../lib/shape.js';\n\n// Keep in sync with the `dev-environment` preset in\n// packages/shared/src/templates.ts (the dashboard wizard's source of truth).\n// The MCP package only depends on the SDK, so these constants are duplicated\n// here intentionally rather than imported from @hoststack/shared.\nconst DEV_ENV_IMAGE = 'hoststack/dev-env:claude';\nconst DEV_ENV_VOLUME = { name: 'workspace', mountPath: '/workspace', sizeGb: 10 };\n\nconst SERVICE_TYPES = [\n\t'web_service',\n\t'private_service',\n\t'worker',\n\t'cron_job',\n\t'static_site',\n] as const;\nconst SERVICE_PLANS = [\n\t'pico',\n\t'nano',\n\t'micro',\n\t'starter',\n\t'standard',\n\t'pro_standard',\n\t'pro_large',\n] as const;\n\ndefineTool({\n\tname: 'list_services',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List services (web, worker, cron, private, static) in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs to find a service by name, check what is deployed, or pick a target for a follow-up tool (logs, deploys, env vars). This is the canonical way to resolve a publicId from a human-friendly name.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - project_id: narrow to one project (numeric id or publicId \"prj_…\").',\n\t\t' - environment_id: narrow to one environment (numeric id or publicId \"env_…\").',\n\t\t' - status: \"active\" | \"deploying\" | \"suspended\" | \"failed\" | \"not_deployed\".',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t'',\n\t\t'Returns: { items: Service[] } — each service includes id, publicId, name, type, status, projectId, repoUrl, branch, runtime, createdAt.',\n\t\t'',\n\t\t'Example: list_services({ status: \"failed\" }) → only services that need attention.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Project filter — numeric id or publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment filter — numeric id or publicId.'),\n\t\tstatus: z\n\t\t\t.enum(['active', 'deploying', 'suspended', 'failed', 'not_deployed'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by current runtime status.'),\n\t\ttype: z\n\t\t\t.enum(['web_service', 'private_service', 'worker', 'cron_job', 'static_site'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by service type.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst filters: Parameters<typeof ctx.hoststack.services.list>[1] = {};\n\t\tif (args.project_id !== undefined) {\n\t\t\t// SDK accepts publicId or numeric id; let resolveId handle it.\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\t\tkind: 'project',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.projectId = resolved;\n\t\t}\n\t\tif (args.environment_id !== undefined) {\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.environmentId = resolved;\n\t\t}\n\t\tif (args.status) filters.status = args.status;\n\t\tif (args.type) filters.type = args.type;\n\n\t\tconst response = await ctx.hoststack.services.list(teamId, filters);\n\t\tconst data = shapeList(response, 'services', shapeService);\n\t\tconst filterDesc = [\n\t\t\targs.project_id !== undefined ? `project=${args.project_id}` : null,\n\t\t\targs.environment_id !== undefined ? `env=${args.environment_id}` : null,\n\t\t\targs.status ? `status=${args.status}` : null,\n\t\t\targs.type ? `type=${args.type}` : null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? filterDesc\n\t\t\t\t\t? `No services match the given filters (${filterDesc}).`\n\t\t\t\t\t: 'No services yet — create one with create_service (or create_dev_environment for an AI dev box), or via the dashboard.'\n\t\t\t\t: `Found ${data.items.length} service${data.items.length === 1 ? '' : 's'}${filterDesc ? ` matching ${filterDesc}` : ''}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Create a new service in a project. Source is either a connected git repo (github_repo_id) OR a pre-built docker_image — never both.',\n\t\t'',\n\t\t'When to use: the user wants to deploy something new. For a one-command AI dev environment specifically, prefer create_dev_environment (it also attaches the /workspace volume and sets the MCP keys).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: service name (1–100 chars).',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - docker_image (optional): pre-built image ref to deploy instead of building from source.',\n\t\t' - github_repo_id (optional): connect a previously-linked GitHub repo by numeric id.',\n\t\t' - branch (optional): git branch (default \"main\").',\n\t\t' - install_command / build_command / start_command (optional): build/run shell commands. start_command is required for web/private services without a docker_image.',\n\t\t' - cron_schedule (optional): cron expression — required for cron_job.',\n\t\t' - publish_path (optional): static-site output dir (e.g. \"dist\").',\n\t\t' - runtime (optional): \"node\" | \"bun\" | \"python\" | … (auto-detected from a repo when omitted).',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - auto_deploy (optional, default true): trigger the first deploy immediately when a source is present.',\n\t\t'',\n\t\t'Returns: { service: Service, deployId: number | null }.',\n\t\t'',\n\t\t'Example: create_service({ project_id: \"prj_abc\", name: \"api\", type: \"web_service\", github_repo_id: 42 }) → { service: { publicId: \"svc_…\" }, deployId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z.string().min(1).max(100).describe('Service name (1–100 chars).'),\n\t\ttype: z.enum(SERVICE_TYPES).describe('Service type.'),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Pre-built image ref. Mutually exclusive with github_repo_id.'),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe('Linked GitHub repo numeric id. Mutually exclusive with docker_image.'),\n\t\tbranch: z.string().max(200).optional().describe('Git branch (default \"main\").'),\n\t\tinstall_command: z.string().max(1000).optional().describe('Install shell command.'),\n\t\tbuild_command: z.string().max(1000).optional().describe('Build shell command.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Start shell command (required for web/private services without an image).'),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Cron expression — required for cron_job.'),\n\t\tpublish_path: z.string().max(500).optional().describe('Static-site output dir.'),\n\t\truntime: z.string().max(50).optional().describe('Runtime hint (node/bun/python/…).'),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Bind to a specific environment; defaults to Production.'),\n\t\tauto_deploy: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Trigger the first deploy immediately (default true).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\n\t\tconst input: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.docker_image !== undefined) input.dockerImage = args.docker_image;\n\t\tif (args.github_repo_id !== undefined) input.githubRepoId = args.github_repo_id;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tif (args.install_command !== undefined) input.installCommand = args.install_command;\n\t\tif (args.build_command !== undefined) input.buildCommand = args.build_command;\n\t\tif (args.start_command !== undefined) input.startCommand = args.start_command;\n\t\tif (args.cron_schedule !== undefined) input.cronSchedule = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) input.publishPath = args.publish_path;\n\t\tif (args.runtime !== undefined) input.runtime = args.runtime;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.auto_deploy !== undefined) input.autoDeploy = args.auto_deploy;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\n\t\tconst response = await ctx.hoststack.services.create(teamId, input);\n\t\tconst data = {\n\t\t\tservice: shapeService(response.service),\n\t\t\tdeployId: (response as { deployId?: number | null }).deployId ?? null,\n\t\t};\n\t\tconst publicId =\n\t\t\tdata.service && 'publicId' in data.service\n\t\t\t\t? (data.service as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created service \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an AI dev environment in one call: a private service from the agentic dev-env image (Claude Code + Codex + OpenCode + MCPs preinstalled), with a persistent /workspace volume and the MCP API keys set, then fire the first deploy.',\n\t\t'',\n\t\t'When to use: the user wants a cloud terminal / dev box they can drive coding agents in (from a desk or a phone). This mirrors the dashboard\\'s \"AI Dev Environment\" wizard preset — create (deploy deferred) → set keys → attach /workspace → deploy, so the first container boots with its volume and keys already in place.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name (optional): service name (default \"dev-environment\").',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - disk_gb (optional): /workspace volume size in GB (default 10, 1–100).',\n\t\t' - hoststack_api_key (optional): sets HOSTSTACK_API_KEY so the hoststack MCP works inside the container.',\n\t\t' - poststack_api_key (optional): sets POSTSTACK_API_KEY so the poststack MCP works inside the container.',\n\t\t'',\n\t\t'Returns: { service: Service, volumeAttached: boolean, deployId: number | null }. Open the Terminal tab on the service (dashboard or phone) once it is running; log in once with `claude /login` inside the container.',\n\t\t'',\n\t\t'Example: create_dev_environment({ project_id: \"prj_abc\", name: \"scratch\", hoststack_api_key: \"hs_live_…\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Service name (default \"dev-environment\").'),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tdisk_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('/workspace volume size in GB (default 10).'),\n\t\thoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for HOSTSTACK_API_KEY (enables the hoststack MCP in-container).'),\n\t\tpoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for POSTSTACK_API_KEY (enables the poststack MCP in-container).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst name = args.name ?? 'dev-environment';\n\t\tconst sizeGb = args.disk_gb ?? DEV_ENV_VOLUME.sizeGb;\n\n\t\t// Defer the first deploy (autoDeploy:false) so we can set the MCP keys\n\t\t// and attach /workspace BEFORE the container boots — same ordering as\n\t\t// the dashboard wizard's use-new-service-form.\n\t\tconst createInput: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname,\n\t\t\ttype: 'private_service',\n\t\t\tprojectId,\n\t\t\tdockerImage: DEV_ENV_IMAGE,\n\t\t\tautoDeploy: false,\n\t\t};\n\t\tif (args.plan !== undefined) createInput.plan = args.plan;\n\t\tconst created = await ctx.hoststack.services.create(teamId, createInput);\n\t\tconst service = created.service;\n\n\t\tconst envVars: { key: string; value: string; isSecret: boolean }[] = [];\n\t\tif (args.hoststack_api_key)\n\t\t\tenvVars.push({\n\t\t\t\tkey: 'HOSTSTACK_API_KEY',\n\t\t\t\tvalue: args.hoststack_api_key,\n\t\t\t\tisSecret: true,\n\t\t\t});\n\t\tif (args.poststack_api_key)\n\t\t\tenvVars.push({\n\t\t\t\tkey: 'POSTSTACK_API_KEY',\n\t\t\t\tvalue: args.poststack_api_key,\n\t\t\t\tisSecret: true,\n\t\t\t});\n\t\tif (envVars.length > 0) {\n\t\t\tawait ctx.hoststack.envVars.bulkSet(teamId, service.id, { vars: envVars });\n\t\t}\n\n\t\tlet volumeAttached = false;\n\t\ttry {\n\t\t\tawait ctx.hoststack.volumes.create(teamId, service.id, {\n\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\tsizeGb,\n\t\t\t});\n\t\t\tvolumeAttached = true;\n\t\t} catch {\n\t\t\t// Surface but don't abort — the env still boots, just without the\n\t\t\t// persistent workspace. Caller sees volumeAttached:false.\n\t\t}\n\n\t\tconst deploy = await ctx.hoststack.deploys.trigger(teamId, service.id);\n\t\tconst deployId = deploy.deploy?.id ?? null;\n\n\t\treturn respond({\n\t\t\tsummary: `Created AI dev environment \"${name}\" (${service.publicId})${volumeAttached ? ' with a /workspace volume' : ''} — deploying. Open its Terminal tab once running.`,\n\t\t\tdata: { service: shapeService(service), volumeAttached, deployId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a single service by ID with its current status AND its service_config row (resources, health-check tuning, scaling, restart policy).',\n\t\t'',\n\t\t'When to use: drilling into a service after list_services, checking deploy/runtime status, grabbing the repo+branch before triggering a deploy, or inspecting the health-check / autoscale knobs before tweaking them via update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\").',\n\t\t'',\n\t\t'Returns: { service: Service, config: ServiceConfig } — service has type/status/runtime/repoUrl/branch/autoDeploy/region/plan/timestamps; config has memoryMb, cpuShares, diskSizeGb, port, protocol, healthCheckEnabled, healthCheckInterval, healthCheckTimeout, healthCheckGracePeriodSec, restartPolicy, preDeployCommand, min/maxInstances, scale thresholds.',\n\t\t'',\n\t\t'Example: get_service({ service_id: \"svc_abc\" }) → { service: { type: \"web\", status: \"running\", … }, config: { healthCheckGracePeriodSec: 120, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Bundle service + config so a single call answers both \"how is it\n\t\t// running?\" and \"how is it tuned?\". Previously callers had to follow\n\t\t// up with a separate read for any of the health-check / scaling /\n\t\t// resource knobs, which lived only on the service_config row.\n\t\tconst [serviceResponse, configResponse] = await Promise.all([\n\t\t\tctx.hoststack.services.get(teamId, args.service_id),\n\t\t\tctx.hoststack.services.getConfig(teamId, args.service_id),\n\t\t]);\n\t\tconst data = {\n\t\t\tservice: shapeService(serviceResponse.service),\n\t\t\tconfig: shape(configResponse.config),\n\t\t};\n\t\tconst status =\n\t\t\tdata.service && 'status' in data.service\n\t\t\t\t? (data.service as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Service ${args.service_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch the latest CPU, memory, network, and request-rate metrics for a service.',\n\t\t'',\n\t\t'When to use: a service is reportedly slow, you suspect resource pressure, or you want a quick health snapshot before scaling. Returns a single point-in-time sample. For a time series — \"is memory growing?\", \"did CPU spike during that deploy?\" — use get_service_metrics_history instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { metrics: { cpu, memory, network, requests } } — values are normalised utilisation/throughput numbers.',\n\t\t'',\n\t\t'Example: get_service_metrics({ service_id: \"svc_abc\" }) → { metrics: { cpu: 0.42, memory: 0.71, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.getMetrics(teamId, args.service_id);\n\t\tconst data = { metrics: shape(response.metrics) };\n\t\treturn respond({ summary: `Metrics snapshot for service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics_history',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a metrics time series (CPU, memory, network, disk) for a service.',\n\t\t'',\n\t\t'When to use: answering \"is memory growing over the last hour?\", \"did CPU spike during deploy X?\", or \"what does normal look like for this service?\". Pairs well with get_deploy to align spikes against deploy times.',\n\t\t'',\n\t\t'Inputs (all optional — omit both for the trailing hour):',\n\t\t' - service_id: publicId of the service.',\n\t\t' - from: ISO-8601 lower bound OR relative offset like \"-15m\" / \"-2h\" / \"-7d\".',\n\t\t' - to: ISO-8601 upper bound (or relative offset). Defaults to now.',\n\t\t'',\n\t\t'Resolution: ≤7d → raw samples (~minute granularity), ≤30d → hourly pre-aggregates, >30d → daily. Up to ~500 points returned.',\n\t\t'',\n\t\t'Returns: { history: Array<{ timestamp, cpuPercent, memoryUsedMb, memoryLimitMb, networkRxBytes, networkTxBytes, diskUsedMb }> }.',\n\t\t'',\n\t\t'Example: get_service_metrics_history({ service_id: \"svc_abc\", from: \"-1h\" }) → 60-ish points for the last hour.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tfrom: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 lower bound or relative offset (e.g. \"-1h\", \"-2d\").'),\n\t\tto: z.string().optional().describe('ISO-8601 upper bound; defaults to now.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Translate the same `-Nm/h/d` relative shorthand we accept in logs\n\t\t// so callers don't have to learn a second convention. The metrics\n\t\t// history endpoint expects ISO timestamps server-side; we resolve\n\t\t// relative offsets here for symmetry with get_service_logs.\n\t\tconst opts: { from?: string; to?: string } = {};\n\t\tconst resolveRel = (raw: string): string => {\n\t\t\tconst rel = /^-(\\d+)(s|m|h|d)$/.exec(raw.trim());\n\t\t\tif (!rel) return raw;\n\t\t\tconst amount = Number.parseInt(rel[1] as string, 10);\n\t\t\tconst unit = rel[2] as 's' | 'm' | 'h' | 'd';\n\t\t\tconst ms =\n\t\t\t\tunit === 's'\n\t\t\t\t\t? amount * 1000\n\t\t\t\t\t: unit === 'm'\n\t\t\t\t\t\t? amount * 60_000\n\t\t\t\t\t\t: unit === 'h'\n\t\t\t\t\t\t\t? amount * 3_600_000\n\t\t\t\t\t\t\t: amount * 86_400_000;\n\t\t\treturn new Date(Date.now() - ms).toISOString();\n\t\t};\n\t\tif (args.from) opts.from = resolveRel(args.from);\n\t\tif (args.to) opts.to = resolveRel(args.to);\n\n\t\tconst response = await ctx.hoststack.services.getMetricsHistory(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\topts,\n\t\t);\n\t\tconst points = Array.isArray(response.history) ? response.history.length : 0;\n\t\treturn respond({\n\t\t\tsummary: `Returned ${points} metric point${points === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { history: response.history },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Rename a service. Only the human-readable name changes — the publicId is permanent.',\n\t\t'',\n\t\t'When to use: the user wants to relabel a service in the dashboard. For deeper config edits (build command, branch, scale, plan), use update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - name: new name (1–60 chars).',\n\t\t'',\n\t\t'Returns: { service: Service }.',\n\t\t'',\n\t\t'Example: update_service({ service_id: \"svc_abc\", name: \"api-prod\" }) → { service: { name: \"api-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z.string().min(1).max(60).describe('New service name (1–60 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.update(teamId, args.service_id, {\n\t\t\tname: args.name,\n\t\t});\n\t\tconst data = { service: shapeService(response.service) };\n\t\treturn respond({ summary: `Renamed service ${args.service_id} to \"${args.name}\".`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_service_config',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Update build/runtime configuration for a service. All fields optional — pass only what you want to change.',\n\t\t'',\n\t\t'When to use: the user wants to tweak how a service builds, runs, scales, or health-checks. Build/runtime fields (branch, install/build/start command, root, dockerfile) take effect on the next deploy — call trigger_deploy after if you need them applied immediately. Instance_count, resource and health-check changes rescale or rewire without a redeploy.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - install_command, build_command, start_command (optional): shell commands. Pass null to clear.',\n\t\t' - branch (optional): git branch to track.',\n\t\t' - root_directory (optional): build context root inside the repo.',\n\t\t' - dockerfile_path (optional): path to Dockerfile relative to root_directory. Pass null to clear.',\n\t\t' - auto_deploy (optional): boolean — auto-deploy on git push.',\n\t\t' - health_check_path (optional): HTTP path the platform GETs to verify liveness (e.g. \"/health\"). Pass null for TCP-only check.',\n\t\t' - health_check_enabled (optional): boolean — toggle health checking on/off.',\n\t\t' - health_check_interval (optional): integer 5–300 seconds — how often the check runs.',\n\t\t' - health_check_timeout (optional): integer 1–60 seconds — single-attempt timeout.',\n\t\t' - health_check_grace_period_sec (optional): integer 1–1800 seconds — startup tolerance before failures count. RAISE THIS (e.g. 180) when the agent reports \"Health check timed out\" on a cold-boot app (Bun + Vite SSR typically need 90–180s).',\n\t\t' - memory_mb (optional): integer 128–16384 — container memory cap.',\n\t\t' - cpu_shares (optional): integer 128–4096 — relative CPU weight.',\n\t\t' - disk_size_gb (optional): integer 1–100 — ephemeral disk cap.',\n\t\t' - port (optional): integer 1–65535 — container port the platform forwards traffic to.',\n\t\t' - protocol (optional): \"http\" | \"tcp\".',\n\t\t' - restart_policy (optional): \"always\" | \"on-failure\" | \"no\".',\n\t\t' - pre_deploy_command (optional): shell command run before the new release accepts traffic (typical use: migrations).',\n\t\t' - instance_count (optional): integer 1–50 — pin both min and max instances to this value.',\n\t\t' - min_instances, max_instances (optional): integers — autoscale bounds. Use instead of instance_count when you want a range.',\n\t\t' - scale_cpu_threshold, scale_memory_threshold (optional): integer 10–100 — autoscale trigger percentage.',\n\t\t' - log_filter_rules (optional): list of { pattern, action } rules applied to runtime logs at query time. Pattern matches the message by case-insensitive substring; action is \"drop\" (filter out) or \"downgrade\" (flip stderr → stdout so it stops looking like an error). Pass [] to clear all rules. Capped at 50 rules.',\n\t\t'',\n\t\t'Returns: { service?: Service, config?: ServiceConfig } — whichever rows were touched.',\n\t\t'',\n\t\t'Example: update_service_config({ service_id: \"svc_abc\", health_check_grace_period_sec: 180 }) → { config: { healthCheckGracePeriodSec: 180, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tinstall_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Install shell command. Null clears.'),\n\t\tbuild_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Build shell command. Null clears.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Start shell command. Null clears.'),\n\t\tbranch: z.string().optional().describe('Git branch to track.'),\n\t\troot_directory: z.string().optional().describe('Build context root.'),\n\t\tdockerfile_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Path to Dockerfile relative to root. Null clears.'),\n\t\tauto_deploy: z.boolean().optional().describe('Auto-deploy on push.'),\n\t\thealth_check_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('HTTP health-check path (e.g. \"/health\"). Null = TCP-only check.'),\n\t\thealth_check_enabled: z.boolean().optional().describe('Toggle health checking on/off.'),\n\t\thealth_check_interval: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(5)\n\t\t\t.max(300)\n\t\t\t.optional()\n\t\t\t.describe('How often the check runs, in seconds (5–300).'),\n\t\thealth_check_timeout: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(60)\n\t\t\t.optional()\n\t\t\t.describe('Single-attempt timeout in seconds (1–60).'),\n\t\thealth_check_grace_period_sec: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1800)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Startup grace period in seconds (1–1800). Raise this if the app needs more time to boot before health checks start counting failures.',\n\t\t\t),\n\t\tmemory_mb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(16384)\n\t\t\t.optional()\n\t\t\t.describe('Container memory cap in MB (128–16384).'),\n\t\tcpu_shares: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(4096)\n\t\t\t.optional()\n\t\t\t.describe('Relative CPU weight (128–4096).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Ephemeral disk size in GB (1–100).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe('Container port the platform forwards traffic to.'),\n\t\tprotocol: z.enum(['http', 'tcp']).optional().describe('Traffic protocol.'),\n\t\trestart_policy: z\n\t\t\t.enum(['always', 'on-failure', 'no'])\n\t\t\t.optional()\n\t\t\t.describe('Docker restart policy.'),\n\t\tpre_deploy_command: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Shell command run before the new release accepts traffic.'),\n\t\tinstance_count: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Pin min and max instances to this value (1–50).'),\n\t\tmin_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale lower bound. Use with max_instances for a range.'),\n\t\tmax_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale upper bound. Use with min_instances for a range.'),\n\t\tscale_cpu_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale CPU trigger percentage (10–100).'),\n\t\tscale_memory_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale memory trigger percentage (10–100).'),\n\t\tlog_filter_rules: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpattern: z.string().min(1).max(200),\n\t\t\t\t\taction: z.enum(['drop', 'downgrade']),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Runtime-log filter rules. Empty array [] clears all rules. Each pattern is case-insensitive substring match against the message.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\n\t\t// services-row fields (PATCH /services/:tid/:sid). Note: nullable\n\t\t// fields take `null` to clear, distinct from `undefined` which means\n\t\t// \"leave unchanged\" — passing `null` through to the underlying API\n\t\t// is what flips the column to NULL on the row.\n\t\tconst serviceUpdate: Record<string, unknown> = {};\n\t\tif (args.install_command !== undefined)\n\t\t\tserviceUpdate['installCommand'] = args.install_command;\n\t\tif (args.build_command !== undefined) serviceUpdate['buildCommand'] = args.build_command;\n\t\tif (args.start_command !== undefined) serviceUpdate['startCommand'] = args.start_command;\n\t\tif (args.dockerfile_path !== undefined)\n\t\t\tserviceUpdate['dockerfilePath'] = args.dockerfile_path;\n\t\tif (args.branch !== undefined) serviceUpdate['branch'] = args.branch;\n\t\tif (args.root_directory !== undefined) serviceUpdate['rootDirectory'] = args.root_directory;\n\t\tif (args.auto_deploy !== undefined) serviceUpdate['autoDeploy'] = args.auto_deploy;\n\t\tif (args.health_check_path !== undefined)\n\t\t\tserviceUpdate['healthCheckPath'] = args.health_check_path;\n\n\t\t// service_config-row fields (PATCH /services/:tid/:sid/config). The\n\t\t// schema expects min_instances/max_instances; \"pin to N\" means setting\n\t\t// both to the same value.\n\t\tconst configUpdate: Record<string, unknown> = {};\n\t\tif (args.health_check_enabled !== undefined)\n\t\t\tconfigUpdate['healthCheckEnabled'] = args.health_check_enabled;\n\t\tif (args.health_check_interval !== undefined)\n\t\t\tconfigUpdate['healthCheckInterval'] = args.health_check_interval;\n\t\tif (args.health_check_timeout !== undefined)\n\t\t\tconfigUpdate['healthCheckTimeout'] = args.health_check_timeout;\n\t\tif (args.health_check_grace_period_sec !== undefined)\n\t\t\tconfigUpdate['healthCheckGracePeriodSec'] = args.health_check_grace_period_sec;\n\t\tif (args.memory_mb !== undefined) configUpdate['memoryMb'] = args.memory_mb;\n\t\tif (args.cpu_shares !== undefined) configUpdate['cpuShares'] = args.cpu_shares;\n\t\tif (args.disk_size_gb !== undefined) configUpdate['diskSizeGb'] = args.disk_size_gb;\n\t\tif (args.port !== undefined) configUpdate['port'] = args.port;\n\t\tif (args.protocol !== undefined) configUpdate['protocol'] = args.protocol;\n\t\tif (args.restart_policy !== undefined) configUpdate['restartPolicy'] = args.restart_policy;\n\t\tif (args.pre_deploy_command !== undefined)\n\t\t\tconfigUpdate['preDeployCommand'] = args.pre_deploy_command;\n\t\tif (args.instance_count !== undefined) {\n\t\t\tconfigUpdate['minInstances'] = args.instance_count;\n\t\t\tconfigUpdate['maxInstances'] = args.instance_count;\n\t\t}\n\t\tif (args.min_instances !== undefined) configUpdate['minInstances'] = args.min_instances;\n\t\tif (args.max_instances !== undefined) configUpdate['maxInstances'] = args.max_instances;\n\t\tif (args.scale_cpu_threshold !== undefined)\n\t\t\tconfigUpdate['scaleCpuThreshold'] = args.scale_cpu_threshold;\n\t\tif (args.scale_memory_threshold !== undefined)\n\t\t\tconfigUpdate['scaleMemoryThreshold'] = args.scale_memory_threshold;\n\t\tif (args.log_filter_rules !== undefined) {\n\t\t\t// Empty array is a meaningful value (clears all rules);\n\t\t\t// undefined leaves rules unchanged. Don't short-circuit.\n\t\t\tconfigUpdate['logFilterRules'] = args.log_filter_rules;\n\t\t}\n\n\t\tif (Object.keys(serviceUpdate).length === 0 && Object.keys(configUpdate).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\n\t\tconst data: Record<string, unknown> = {};\n\t\tconst touchedFields: string[] = [];\n\t\tif (Object.keys(serviceUpdate).length > 0) {\n\t\t\tconst serviceResponse = await ctx.hoststack.services.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tserviceUpdate,\n\t\t\t);\n\t\t\tdata['service'] = shape(serviceResponse.service);\n\t\t\ttouchedFields.push(...Object.keys(serviceUpdate));\n\t\t}\n\t\tif (Object.keys(configUpdate).length > 0) {\n\t\t\tconst configResponse = await ctx.hoststack.services.updateConfig(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tconfigUpdate,\n\t\t\t);\n\t\t\tdata['config'] = shape(configResponse.config);\n\t\t\ttouchedFields.push(...Object.keys(configUpdate));\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary: `Updated ${touchedFields.join(', ')} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Suspend a service: stop all running instances and pause auto-deploys. The service stays configured and can be resumed later with resume_service.',\n\t\t'',\n\t\t'When to use: the user wants to temporarily stop traffic and billing without deleting the service (for example: dev environment overnight, debugging).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.suspend(teamId, args.service_id);\n\t\treturn respond({ summary: `Suspended service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'resume_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resume a previously suspended service: start instances back up and re-enable auto-deploys.',\n\t\t'',\n\t\t'When to use: the user wants to bring a suspended service back online. Pair with suspend_service.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: resume_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.resume(teamId, args.service_id);\n\t\treturn respond({ summary: `Resumed service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs',\n\tcategory: 'logs',\n\tdescription: [\n\t\t\"Fetch a snapshot of the running container's recent runtime logs (stdout/stderr). For *deploy* logs (build output), use get_deploy_logs instead.\",\n\t\t'',\n\t\t'When to use: a service is misbehaving in production and you need to read what it is currently logging. The tool returns a snapshot — there is no streaming over MCP. Re-call to get newer entries.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - lines (optional): tail size (default 200, max 1000).',\n\t\t' - since/until (optional): ISO-8601 timestamp OR a relative offset like \"-5m\", \"-1h\", \"-2d\".',\n\t\t' - stream (optional): \"stdout\" | \"stderr\". Omit to combine.',\n\t\t' - level (optional): real log level — trace/debug/info/warn/error/fatal. For structured JSON logs (pino, bunyan, OpenTelemetry severity) this filters on the parsed inner level field. For plain text logs it falls back to a stream-alias hint (info/debug → stdout, warn/error/fatal → stderr).',\n\t\t' - search (optional): case-insensitive substring grep, ≤100 chars.',\n\t\t' - count_only (optional): when true, returns { count } only — much cheaper for \"how many error lines in last 5m\" polling.',\n\t\t'',\n\t\t'Returns: { logs: LogEntry[] | string } when count_only is false. Each entry has { timestamp, level?, stream, message }. `level` is the parsed inner level when the message is a structured JSON envelope (pino numeric or string), and undefined for plain-text logs. `stream` is always one of stdout/stderr. Or { count: number } when count_only is true.',\n\t\t'',\n\t\t'Example: get_service_logs({ service_id: \"svc_abc\", search: \"OOM\", since: \"-15m\" }) → { logs: [...] }.',\n\t\t'',\n\t\t'More examples:',\n\t\t' - Last 50 stderr lines from the past hour: get_service_logs({ service_id: \"svc_abc\", lines: 50, stream: \"stderr\", since: \"-1h\" })',\n\t\t' - Just count error lines without fetching them: get_service_logs({ service_id: \"svc_abc\", level: \"error\", since: \"-5m\", count_only: true }) → { count: 47 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tlines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Tail size; default 200, hard cap 1000.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Filter by structured JSON log level (pino/bunyan/severity). Falls back to a stream-alias hint for plain-text logs (info/debug→stdout, warn/error/fatal→stderr).',\n\t\t\t),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only { count } — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts: {\n\t\t\tlines?: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = {\n\t\t\tlines: args.lines ?? 200,\n\t\t};\n\t\tif (args.since) opts.since = args.since;\n\t\tif (args.until) opts.until = args.until;\n\t\tif (args.stream) opts.stream = args.stream;\n\t\tif (args.level) opts.level = args.level;\n\t\tif (args.search) opts.search = args.search;\n\t\tif (args.count_only) opts.countOnly = args.count_only;\n\n\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, args.service_id, opts);\n\t\tif ('count' in response) {\n\t\t\treturn respond({\n\t\t\t\tsummary: `${response.count} matching log line${response.count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\t\tdata: { count: response.count },\n\t\t\t});\n\t\t}\n\t\tconst count = Array.isArray(response.logs)\n\t\t\t? response.logs.length\n\t\t\t: typeof response.logs === 'string'\n\t\t\t\t? response.logs.split('\\n').length\n\t\t\t\t: 0;\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${count} log line${count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { logs: response.logs },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs_bulk',\n\tcategory: 'logs',\n\tdescription: [\n\t\t'Fetch runtime logs for multiple services in parallel and return one response keyed by service_id. Single round-trip from the MCP client perspective.',\n\t\t'',\n\t\t'When to use: investigating something that spans a project — \"what does every service in the auth-fleet say about the OOM at 14:00?\", \"did anything log an error in the last 5 minutes across these 4 services?\". Pair with list_services (filter by project/status) to get the service_ids list first.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_ids: list of service publicIds (svc_…). Hard cap 10 to bound work — for larger fleets, partition the call.',\n\t\t' - lines_per_service (optional): tail size per service (default 100, max 500). Smaller default than get_service_logs since you are fanning out.',\n\t\t' - since/until (optional): same shape as get_service_logs (ISO-8601 or \"-5m\", \"-1h\").',\n\t\t' - stream (optional): \"stdout\" | \"stderr\".',\n\t\t' - level (optional): structured-log level filter (trace/debug/info/warn/error/fatal).',\n\t\t' - search (optional): case-insensitive substring filter applied to every service.',\n\t\t' - count_only (optional): when true, return { results: { [service_id]: { count } } } — much cheaper for \"how many error lines per service in the last 5m\" surveys.',\n\t\t'',\n\t\t'Returns: { results: { [service_id]: { logs?, count?, error? } } } — per-service result, with `error` populated when one service failed (the rest still succeed).',\n\t\t'',\n\t\t'Example: get_service_logs_bulk({ service_ids: [\"svc_api\", \"svc_worker\"], level: \"error\", since: \"-15m\", count_only: true }) → { results: { svc_api: { count: 0 }, svc_worker: { count: 12 } } }.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_ids: z\n\t\t\t.array(z.string())\n\t\t\t.min(1)\n\t\t\t.max(10)\n\t\t\t.describe('Service publicIds (1–10). Hard cap 10 to bound parallel work.'),\n\t\tlines_per_service: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Tail size per service; default 100, hard cap 500.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe('Structured log level filter (same as get_service_logs).'),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only counts per service — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst linesPerService = args.lines_per_service ?? 100;\n\n\t\t// Build a single options object reused across all fan-outs so we\n\t\t// don't reconstruct identical params in a loop.\n\t\tconst baseOpts: {\n\t\t\tlines: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = { lines: linesPerService };\n\t\tif (args.since) baseOpts.since = args.since;\n\t\tif (args.until) baseOpts.until = args.until;\n\t\tif (args.stream) baseOpts.stream = args.stream;\n\t\tif (args.level) baseOpts.level = args.level;\n\t\tif (args.search) baseOpts.search = args.search;\n\t\tif (args.count_only) baseOpts.countOnly = args.count_only;\n\n\t\t// Promise.allSettled so one failing service (e.g. deleted between\n\t\t// list_services and this call) doesn't void the rest.\n\t\tconst settled = await Promise.allSettled(\n\t\t\targs.service_ids.map(async (sid) => {\n\t\t\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, sid, baseOpts);\n\t\t\t\treturn { sid, response };\n\t\t\t}),\n\t\t);\n\n\t\tconst results: Record<string, unknown> = {};\n\t\tlet okCount = 0;\n\t\tlet errorCount = 0;\n\t\tfor (let i = 0; i < settled.length; i += 1) {\n\t\t\tconst outcome = settled[i] as PromiseSettledResult<{\n\t\t\t\tsid: string;\n\t\t\t\tresponse: { logs?: unknown; count?: number };\n\t\t\t}>;\n\t\t\tconst sid = args.service_ids[i] as string;\n\t\t\tif (outcome.status === 'fulfilled') {\n\t\t\t\tconst { response } = outcome.value;\n\t\t\t\tif ('count' in response) {\n\t\t\t\t\tresults[sid] = { count: response.count };\n\t\t\t\t} else {\n\t\t\t\t\tresults[sid] = { logs: response.logs };\n\t\t\t\t}\n\t\t\t\tokCount += 1;\n\t\t\t} else {\n\t\t\t\tconst reason = outcome.reason as { message?: string } | string;\n\t\t\t\tresults[sid] = {\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof reason === 'string' ? reason : (reason?.message ?? 'Unknown error'),\n\t\t\t\t};\n\t\t\t\terrorCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tconst summary =\n\t\t\terrorCount === 0\n\t\t\t\t? `Fetched logs from ${okCount} service${okCount === 1 ? '' : 's'}.`\n\t\t\t\t: `Fetched logs from ${okCount} of ${args.service_ids.length} service${args.service_ids.length === 1 ? '' : 's'} (${errorCount} failed — see per-service .error field).`;\n\t\treturn respond({ summary, data: { results } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeVolume } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_volumes',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'List persistent disks attached to a service. Volumes mount writable storage into the container at the path you choose, surviving redeploys and restarts.',\n\t\t'',\n\t\t'When to use: auditing what disks are attached, checking sizes before adding more, or confirming a volume took effect after creation. Note: storage is metered for billing — use this to spot oversized disks.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: Volume[] } — each entry has id, publicId, name, mountPath, sizeGb, status (pending|active|deleting), createdAt, updatedAt.',\n\t\t'',\n\t\t'Example: list_volumes({ service_id: \"svc_abc\" }) → { items: [{ name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.volumes.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'volumes', shapeVolume);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No volumes attached to service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} volume${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Attach a new persistent disk to a service. The disk gets provisioned on the host and bind-mounted into the container at `mount_path` on the next deploy. Use this when an app needs writable persistent storage — uploads, caches, SQLite databases, anything that must survive container restarts.',\n\t\t'',\n\t\t'When to use: the user mentions storing files, uploads, or any data that needs to outlive a container restart. For purely ephemeral scratch space, point the app at /tmp instead (already tmpfs-mounted, free, no provisioning needed) — see HOSTSTACK_TMP_DIR.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to attach to.',\n\t\t' - name: lowercase alphanumeric + hyphens, ≤64 chars (used as the docker volume identifier — change with care once data is written).',\n\t\t' - mount_path: in-container absolute path (e.g. \"/var/data\").',\n\t\t' - size_gb: optional, 1–100, default 1. Counts against your plan storage quota and is metered for billing.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the created record.',\n\t\t'',\n\t\t'Example: create_volume({ service_id: \"svc_abc\", name: \"data\", mount_path: \"/var/data\", size_gb: 10 }) → { volume: { name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(64)\n\t\t\t.regex(/^[a-z0-9-]+$/)\n\t\t\t.describe('Volume name (lowercase alphanumeric + hyphens).'),\n\t\tmount_path: z\n\t\t\t.string()\n\t\t\t.startsWith('/')\n\t\t\t.max(500)\n\t\t\t.describe('In-container mount path (absolute).'),\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Disk size in GB (default 1, max 100).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; mountPath: string; sizeGb?: number } = {\n\t\t\tname: args.name,\n\t\t\tmountPath: args.mount_path,\n\t\t};\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tconst response = await ctx.hoststack.volumes.create(teamId, args.service_id, input);\n\t\tconst data = { volume: shape(response.volume) };\n\t\treturn respond({\n\t\t\tsummary: `Attached volume \"${args.name}\" (${args.size_gb ?? 1}GB) at ${args.mount_path} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t\"Update a volume's mountPath or sizeGb. Resizes take effect on the next deploy; shrinking is rejected at the storage layer (filesystems can't safely shrink under live data).\",\n\t\t'',\n\t\t'When to use: the user wants to grow the disk for an existing volume, or move where it mounts inside the container.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume to update.',\n\t\t' - mount_path (optional): new in-container mount path.',\n\t\t' - size_gb (optional): new size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the updated record.',\n\t\t'',\n\t\t'Example: update_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\", size_gb: 20 }) → { volume: { sizeGb: 20, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId (e.g. vol_…).'),\n\t\tmount_path: z.string().startsWith('/').max(500).optional().describe('New mount path.'),\n\t\tsize_gb: z.number().int().min(1).max(100).optional().describe('New size in GB.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { mountPath?: string; sizeGb?: number } = {};\n\t\tif (args.mount_path !== undefined) input.mountPath = args.mount_path;\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.volumes.update(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.volume_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { volume: shape(response.volume) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on volume ${args.volume_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Detach and deprovision a volume. The underlying disk is destroyed — back up any data first. Async: marks the row deleting, the host agent finalises removal once it acks. A periodic reaper retries if the agent was offline at delete time.',\n\t\t'',\n\t\t\"When to use: the user explicitly says to remove a volume, or you're cleaning up after retiring a service. Confirm before calling on production data.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.volumes.delete(teamId, args.service_id, args.volume_id);\n\t\treturn respond({\n\t\t\tsummary: `Detached volume ${args.volume_id} from service ${args.service_id} (deprovisioning).`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n"],"mappings":"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","names":["apiKey","baseUrl","server","args","server","server","args","z","z","args","z","z","args","z","z","args","z","z","args","z","z","args","z","zones","z","args","z","z","args","z","z","args","response","data","z","z","args","z","z","args","z","z","args","z","z","args","z","z","args","baseUrl","server"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/server-factory.ts","../src/api-client.ts","../src/prompts/registry.ts","../src/resources/registry.ts","../src/registry.ts","../src/prompts/deploy-prompts.ts","../src/lib/shape.ts","../src/resources/hoststack-resources.ts","../src/tools/activity-log.ts","../src/lib/respond.ts","../src/tools/alerts.ts","../src/tools/cron.ts","../src/tools/databases.ts","../src/tools/deploys.ts","../src/tools/dns-records.ts","../src/tools/domains.ts","../src/tools/env-vars.ts","../src/tools/environments.ts","../src/tools/meta.ts","../src/tools/notifications.ts","../src/tools/projects.ts","../src/tools/services.ts","../src/tools/volumes.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\nimport { createMcpServer, type ToolCallEvent } from './server-factory.js';\n\n// ─── --print-config <client> ─────────────────────────────────────────\n// Generate a client-specific JSON config snippet so the user can copy/paste\n// into Claude Desktop / Cursor without hand-writing the env block. Runs\n// before the API key check so users without a key set can still discover\n// the right config shape.\n\nconst args = process.argv.slice(2);\nconst printIdx = args.indexOf('--print-config');\nif (printIdx !== -1) {\n\tconst target = args[printIdx + 1];\n\tconst validTargets = new Set(['claude-desktop', 'cursor', 'claude-code']);\n\tif (!target || !validTargets.has(target)) {\n\t\tconsole.error(\n\t\t\t`Usage: hoststack-mcp --print-config <${[...validTargets].join('|')}>\\n` +\n\t\t\t\t`(--print-config emits a config snippet to stdout; you still need to put your API key into it.)`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\tconst apiKeyPlaceholder = process.env['HOSTSTACK_API_KEY'] ?? 'hs_live_your_api_key_here';\n\n\tif (target === 'claude-desktop' || target === 'cursor') {\n\t\tconst config = {\n\t\t\tmcpServers: {\n\t\t\t\thoststack: {\n\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\targs: ['-y', '@hoststack.dev/mcp'],\n\t\t\t\t\tenv: {\n\t\t\t\t\t\tHOSTSTACK_API_KEY: apiKeyPlaceholder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tconst payload = target === 'cursor' ? config.mcpServers : config;\n\t\tprocess.stdout.write(JSON.stringify(payload, null, '\\t') + '\\n');\n\t\tprocess.exit(0);\n\t}\n\n\tif (target === 'claude-code') {\n\t\tprocess.stdout.write(\n\t\t\t`claude mcp add hoststack --env HOSTSTACK_API_KEY=${apiKeyPlaceholder} -- npx -y @hoststack.dev/mcp\\n`,\n\t\t);\n\t\tprocess.exit(0);\n\t}\n}\n\nconst apiKey = process.env['HOSTSTACK_API_KEY'];\nif (!apiKey) {\n\tconsole.error('HOSTSTACK_API_KEY environment variable is required');\n\tprocess.exit(1);\n}\n\nconst baseUrl = process.env['HOSTSTACK_BASE_URL'] ?? 'https://hoststack.dev';\n\n// stdio mode: NDJSON to stderr so the user can `2> mcp-trace.log` if they\n// want a local trace, without ever colliding with the JSON-RPC body on\n// stdout. Disabled when HOSTSTACK_MCP_TRACE is unset to keep the default\n// quiet for Claude Desktop / Cursor.\nconst traceEnabled = process.env['HOSTSTACK_MCP_TRACE'] === '1';\nconst onToolCall = traceEnabled\n\t? (event: ToolCallEvent) => {\n\t\t\tprocess.stderr.write(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tts: event.startedAt.toISOString(),\n\t\t\t\t\ttool: event.tool,\n\t\t\t\t\tok: event.ok,\n\t\t\t\t\tduration_ms: event.durationMs,\n\t\t\t\t\terror_code: event.errorCode,\n\t\t\t\t\ttransport: 'stdio',\n\t\t\t\t}) + '\\n',\n\t\t\t);\n\t\t}\n\t: undefined;\n\nconst server = createMcpServer({\n\tapiKey,\n\tbaseUrl,\n\t...(onToolCall ? { onToolCall } : {}),\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { HostStack } from '@hoststack.dev/sdk';\n\nimport { ApiClient } from './api-client.js';\nimport { attachPrompts } from './prompts/registry.js';\nimport { attachResources } from './resources/registry.js';\nimport { attachTools, type ToolCallSink, type ToolContext } from './registry.js';\n\nexport type { ToolCallEvent, ToolCallSink } from './registry.js';\n\n// Importing each tool/prompt/resource module runs its top-level define*()\n// calls and populates the shared registries. ESM caches modules, so each\n// registry is built exactly once per process.\nimport './prompts/deploy-prompts.js';\nimport './resources/hoststack-resources.js';\nimport './tools/activity-log.js';\nimport './tools/alerts.js';\nimport './tools/cron.js';\nimport './tools/databases.js';\nimport './tools/deploys.js';\nimport './tools/dns-records.js';\nimport './tools/domains.js';\nimport './tools/env-vars.js';\nimport './tools/environments.js';\nimport './tools/meta.js';\nimport './tools/notifications.js';\nimport './tools/projects.js';\nimport './tools/services.js';\nimport './tools/volumes.js';\n\nexport interface CreateServerOptions {\n\t/** HostStack API key (hs_live_… or hs_test_…). Required. */\n\tapiKey: string;\n\t/** Base URL for the HostStack API. Defaults to https://hoststack.dev */\n\tbaseUrl?: string;\n\t/**\n\t * Server identity advertised in the MCP `initialize` response. Defaults\n\t * to `{ name: \"hoststack\", version: PACKAGE_VERSION }`.\n\t */\n\tserverInfo?: { name: string; version: string };\n\t/**\n\t * Optional telemetry sink invoked once per tool call (success or failure).\n\t * Runs on the microtask queue; sink errors are swallowed so tool execution\n\t * never blocks on telemetry.\n\t */\n\tonToolCall?: ToolCallSink;\n}\n\nexport const PACKAGE_NAME = 'hoststack';\nexport const PACKAGE_VERSION = '0.9.1';\n\ninterface MeResponse {\n\tuser: { id: number };\n\tteam?: { id: number };\n}\n\n/**\n * Build a fully-wired MCP server. Used by both the stdio CLI entry-point and\n * the HTTP transport mounted by the API server. A new server (and its\n * underlying SDK + ApiClient) is created per call, so the HTTP transport can\n * scope each connection to a single API key without cross-contamination.\n */\nexport function createMcpServer(options: CreateServerOptions): McpServer {\n\tconst baseUrl = (options.baseUrl ?? 'https://hoststack.dev').replace(/\\/$/, '');\n\tconst hoststack = new HostStack({ apiKey: options.apiKey, baseUrl });\n\tconst api = new ApiClient(options.apiKey, baseUrl);\n\n\tconst server = new McpServer({\n\t\tname: options.serverInfo?.name ?? PACKAGE_NAME,\n\t\tversion: options.serverInfo?.version ?? PACKAGE_VERSION,\n\t});\n\n\t// Cache the team lookup. The first tool call pays one /auth/me round-trip;\n\t// every subsequent call reuses the cached promise. We store the in-flight\n\t// promise (not the resolved value) so concurrent first-calls don't fan out\n\t// into duplicate /auth/me requests.\n\tlet teamIdPromise: Promise<number> | null = null;\n\tconst resolveTeamId = (): Promise<number> => {\n\t\tif (!teamIdPromise) {\n\t\t\tteamIdPromise = api.get<MeResponse>('/api/auth/me').then((me) => {\n\t\t\t\tif (!me.team?.id) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'No team bound to API key. Generate a team-scoped key in the dashboard.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn me.team.id;\n\t\t\t});\n\t\t\t// If the lookup fails, clear the cache so a retry can succeed once\n\t\t\t// the user fixes their key. Without this, every subsequent tool\n\t\t\t// call would re-throw the same cached rejection.\n\t\t\tteamIdPromise.catch(() => {\n\t\t\t\tteamIdPromise = null;\n\t\t\t});\n\t\t}\n\t\treturn teamIdPromise;\n\t};\n\n\tconst ctx: ToolContext = { hoststack, api, resolveTeamId };\n\tattachTools(server, ctx, options.onToolCall);\n\tattachPrompts(server, ctx);\n\tattachResources(server, ctx);\n\n\treturn server;\n}\n","/**\n * Lightweight API client for HostStack endpoints not yet covered by the SDK\n * (logs snapshots, activity log, runtime metrics, /me lookup). Uses the same\n * auth and base URL as the SDK instance.\n */\nexport class ApiClient {\n\tconstructor(\n\t\tprivate readonly apiKey: string,\n\t\tprivate readonly baseUrl: string,\n\t) {}\n\n\tprivate get headers(): Record<string, string> {\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t'Content-Type': 'application/json',\n\t\t};\n\t}\n\n\tasync get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n\t\tconst url = new URL(`${this.baseUrl}${path}`);\n\t\tif (params) {\n\t\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\t\tif (value !== undefined) url.searchParams.set(key, String(value));\n\t\t\t}\n\t\t}\n\t\tconst res = await fetch(url.toString(), { headers: this.headers });\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\tconst init: RequestInit = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.headers,\n\t\t};\n\t\tif (body !== undefined) init.body = JSON.stringify(body);\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, init);\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync patch<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync put<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PUT',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync delete<T>(path: string): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.headers,\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tprivate async handle<T>(res: Response): Promise<T> {\n\t\tif (!res.ok) {\n\t\t\tconst error = (await res.json().catch(() => ({ error: res.statusText }))) as {\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tthrow new Error(error.error ?? `API error: ${res.status}`);\n\t\t}\n\t\tif (res.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn res.json() as Promise<T>;\n\t}\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from '../api-client.js';\n\nexport interface PromptContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalPromptDefinition {\n\tname: string;\n\tdescription: string;\n\targs: ZodRawShape;\n\thandler: (\n\t\targs: Record<string, string | undefined>,\n\t\tctx: PromptContext,\n\t) => Promise<GetPromptResult>;\n}\n\nexport type PromptDefinition = Readonly<InternalPromptDefinition>;\n\nconst prompts: InternalPromptDefinition[] = [];\n\n/**\n * Register a single prompt. The generic over `TArgs` lets the handler receive\n * precisely-typed args while the registry stores the widened shape — same\n * trick as `defineTool` to keep the compiler's instantiation budget in check.\n */\nexport function definePrompt<TArgs extends ZodRawShape>(def: {\n\tname: string;\n\tdescription: string;\n\targs: TArgs;\n\thandler: (args: z.infer<z.ZodObject<TArgs>>, ctx: PromptContext) => Promise<GetPromptResult>;\n}): void {\n\tprompts.push({\n\t\tname: def.name,\n\t\tdescription: def.description,\n\t\targs: def.args,\n\t\thandler: def.handler as InternalPromptDefinition['handler'],\n\t});\n}\n\nexport function listPromptDefinitions(): ReadonlyArray<PromptDefinition> {\n\treturn prompts;\n}\n\n/** Iterate the registry and wire each prompt into the MCP server. */\nexport function attachPrompts(server: McpServer, ctx: PromptContext): void {\n\ttype PromptMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\targsSchema: ZodRawShape,\n\t\tcb: (args: Record<string, string | undefined>) => Promise<GetPromptResult>,\n\t) => unknown;\n\n\tconst register = server.prompt.bind(server) as unknown as PromptMethod;\n\n\tfor (const def of prompts) {\n\t\tregister(def.name, def.description, def.args, (args) => def.handler(args, ctx));\n\t}\n}\n\n/**\n * Helper to build a single user-role text message — the most common shape for\n * these prompts (we're guiding the agent, not modeling assistant responses).\n */\nexport function userMessage(text: string): GetPromptResult['messages'][number] {\n\treturn { role: 'user', content: { type: 'text', text } };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\n\nimport type { ApiClient } from '../api-client.js';\n\ninterface ResourceContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface StaticResourceDef {\n\tkind: 'static';\n\tname: string;\n\turi: string;\n\tdescription: string;\n\tmimeType?: string;\n\tread: (uri: URL, ctx: ResourceContext) => Promise<ReadResourceResult>;\n}\n\nexport type ResourceDefinition = Readonly<StaticResourceDef>;\n\nconst resources: StaticResourceDef[] = [];\n\nexport function defineResource(def: StaticResourceDef): void {\n\tresources.push(def);\n}\n\nexport function listResourceDefinitions(): ReadonlyArray<ResourceDefinition> {\n\treturn resources;\n}\n\nexport function attachResources(server: McpServer, ctx: ResourceContext): void {\n\tfor (const def of resources) {\n\t\tserver.resource(\n\t\t\tdef.name,\n\t\t\tdef.uri,\n\t\t\t{\n\t\t\t\tdescription: def.description,\n\t\t\t\t...(def.mimeType ? { mimeType: def.mimeType } : {}),\n\t\t\t},\n\t\t\t(uri) => def.read(uri, ctx),\n\t\t);\n\t}\n}\n\n/**\n * Pack a JSON value into the resource read-result envelope. The MCP spec\n * requires `text` for application/json resources — we serialise here so each\n * resource handler stays a one-liner.\n */\nexport function jsonResource(uri: URL, data: unknown): ReadResourceResult {\n\treturn {\n\t\tcontents: [\n\t\t\t{\n\t\t\t\turi: uri.toString(),\n\t\t\t\tmimeType: 'application/json',\n\t\t\t\ttext: JSON.stringify(data, null, 2),\n\t\t\t},\n\t\t],\n\t};\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from './api-client.js';\n\nexport type ToolCategory =\n\t| 'projects'\n\t| 'services'\n\t| 'deploys'\n\t| 'databases'\n\t| 'domains'\n\t| 'dns'\n\t| 'volumes'\n\t| 'env-vars'\n\t| 'environments'\n\t| 'cron'\n\t| 'logs'\n\t| 'activity-log'\n\t| 'alerts'\n\t| 'meta';\n\nexport interface ToolContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\t/**\n\t * Lazy resolver for the team ID bound to the API key. The agent never has\n\t * to pass `teamId` to a tool — every SDK route is team-scoped, so the MCP\n\t * looks it up once via `/api/auth/me` and caches it for the rest of the\n\t * server's lifetime. Throws if the API key is invalid or unbound.\n\t */\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalToolDefinition {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: ZodRawShape;\n\thandler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;\n}\n\nexport type ToolDefinition = Readonly<InternalToolDefinition>;\n\nconst tools: InternalToolDefinition[] = [];\n\n/**\n * Register a single tool definition. The generic over `TInput` lets the\n * handler receive precisely-typed arguments while the registry stores the\n * widened shape.\n */\nexport function defineTool<TInput extends ZodRawShape>(def: {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: TInput;\n\thandler: (args: z.infer<z.ZodObject<TInput>>, ctx: ToolContext) => Promise<CallToolResult>;\n}): void {\n\ttools.push({\n\t\tname: def.name,\n\t\tcategory: def.category,\n\t\tdescription: def.description,\n\t\tinput: def.input,\n\t\thandler: def.handler as InternalToolDefinition['handler'],\n\t});\n}\n\n/** Snapshot the currently-registered tool definitions (for tests / docs). */\nexport function listToolDefinitions(): ReadonlyArray<ToolDefinition> {\n\treturn tools;\n}\n\n/**\n * Telemetry event emitted once per tool invocation. The MCP package never\n * persists these — it just hands them to the optional `onToolCall` sink so\n * the host (HTTP transport, CLI, tests) can decide where they go.\n *\n * Privacy: `inputHash` is a hash over the JSON-stringified args; we never\n * expose the args themselves so PII from tool inputs (env-var values, domain\n * names) never reaches a sink.\n */\nexport interface ToolCallEvent {\n\ttool: string;\n\tdurationMs: number;\n\tok: boolean;\n\terrorCode: string | null;\n\tinputHash: string | null;\n\tstartedAt: Date;\n}\n\nexport type ToolCallSink = (event: ToolCallEvent) => void | Promise<void>;\n\n/** Iterate the registry and wire each tool into the MCP server. */\nexport function attachTools(server: McpServer, ctx: ToolContext, sink?: ToolCallSink): void {\n\ttype ToolMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\tschema: ZodRawShape,\n\t\tcb: (args: Record<string, unknown>) => Promise<CallToolResult>,\n\t) => unknown;\n\n\tconst register = server.tool.bind(server) as unknown as ToolMethod;\n\n\tfor (const def of tools) {\n\t\tregister(def.name, def.description, def.input, async (args) => {\n\t\t\tconst startedAt = new Date();\n\t\t\tconst start = performance.now();\n\t\t\tlet ok = true;\n\t\t\tlet errorCode: string | null = null;\n\t\t\ttry {\n\t\t\t\tconst result = await def.handler(args, ctx);\n\t\t\t\tif (result.isError) {\n\t\t\t\t\tok = false;\n\t\t\t\t\terrorCode = 'tool_error';\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tok = false;\n\t\t\t\terrorCode =\n\t\t\t\t\terr instanceof Error ? (err.name || 'Error').slice(0, 64) : 'unknown_error';\n\t\t\t\tthrow err;\n\t\t\t} finally {\n\t\t\t\tif (sink) {\n\t\t\t\t\tconst durationMs = Math.round(performance.now() - start);\n\t\t\t\t\tconst event: ToolCallEvent = {\n\t\t\t\t\t\ttool: def.name,\n\t\t\t\t\t\tdurationMs,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\tinputHash: hashArgs(args),\n\t\t\t\t\t\tstartedAt,\n\t\t\t\t\t};\n\t\t\t\t\tPromise.resolve()\n\t\t\t\t\t\t.then(() => sink(event))\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction hashArgs(args: Record<string, unknown>): string | null {\n\ttry {\n\t\tconst json = JSON.stringify(args);\n\t\tconst bunGlobal = (globalThis as { Bun?: { hash?: (data: string) => bigint } }).Bun;\n\t\tif (bunGlobal?.hash) {\n\t\t\treturn bunGlobal.hash(json).toString(16);\n\t\t}\n\t\treturn `len:${json.length}`;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import { z } from 'zod';\n\nimport { definePrompt, userMessage } from './registry.js';\n\ndefinePrompt({\n\tname: 'diagnose_failed_deploy',\n\tdescription:\n\t\t'Walk the agent through diagnosing a failed deploy: pull the latest deploys for the named service, find the most recent failure, fetch its build/deploy logs, and propose a fix. Stops at suggestion — does not retrigger automatically.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async ({ service_id }) => {\n\t\tconst text = `Goal: diagnose the most recent failed deploy on service ${service_id} and propose a fix.\n\nPlan (use these tools in order):\n1. \\`list_deploys({ service_id: \"${service_id}\" })\\` — pull recent deploys, newest first. Identify the most recent deploy with status \\`failed\\` (or \\`cancelled\\` if the user wants to investigate that too). Capture its publicId, branch, and commitSha.\n2. \\`get_deploy_logs({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — read the full build output. Scan for: stack traces, \"ERROR\" / \"error:\" lines, exit codes, missing env vars, OOM-killed signals, network failures pulling dependencies.\n3. \\`get_service({ service_id: \"${service_id}\" })\\` — confirm the service's runtime, plan, and whether autoDeploy is on. Plan tier matters because OOMs at small tiers point at \\`maxMemoryMb\\`.\n4. (Optional) \\`list_env_vars({ service_id: \"${service_id}\" })\\` — only if the build error mentions a missing variable. Don't fetch otherwise; values are masked anyway.\n\nThen write a short diagnosis for the user with three sections:\n- **Root cause** — one sentence pointing at the actual failure line.\n- **Evidence** — 3–6 line excerpt from the logs that proves the diagnosis.\n- **Suggested fix** — concrete next steps, including which tool to call (e.g. \"set MISSING_KEY with set_env_var, then trigger_deploy\"). Do NOT call trigger_deploy automatically; the user decides.\n\nIf the deploy logs are empty or only contain queued/pending markers, surface that — the build never started, which is its own class of bug (likely a builder-pool or quota issue).`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'deploy_status_check',\n\tdescription:\n\t\t\"Quick orientation prompt: who am I, what's running, and is anything currently deploying? Useful at the start of a session.\",\n\targs: {},\n\thandler: async () => {\n\t\tconst text = `Goal: produce a 30-second status check on this HostStack team.\n\nPlan:\n1. \\`get_me()\\` — confirm which user + team the API key belongs to.\n2. \\`list_services()\\` — every service with current status (running, suspended, building, deploying, failed). Note any service in a non-running state.\n3. For each service whose status is \\`building\\` or \\`deploying\\`, call \\`list_deploys({ service_id })\\` and surface the latest deploy's commitMessage + author.\n4. \\`list_activity_log({ per_page: 10 })\\` — last 10 audit events to spot recent changes (deploys triggered, env vars edited, services restarted).\n\nWrite a status report shaped like:\n\n> **Team**: <name>\n> **Services**: <n> total — <breakdown by status>\n> **In flight**: <list of building/deploying services with commit info, or \"none\">\n> **Recent activity**: <3-5 most relevant events from the audit log, newest first>\n\nKeep it tight (under 200 words). The user can drill in with follow-up tools.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'rotate_secret',\n\tdescription:\n\t\t'Guide the agent through rotating an env-var secret on a service. Lists current vars to confirm the target key exists, asks the user for the new value, sets it, and offers to trigger a deploy.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().describe('Env-var key to rotate (e.g. DATABASE_URL).'),\n\t},\n\thandler: async ({ service_id, key }) => {\n\t\tconst text = `Goal: rotate the secret \\`${key}\\` on service ${service_id}.\n\nPlan:\n1. \\`list_env_vars({ service_id: \"${service_id}\" })\\` — confirm \\`${key}\\` exists. Note its \\`isSecret\\` flag (it should be true; if not, flag this — you're about to mark it secret on rotation, which is a behaviour change).\n2. **Ask the user for the new value before calling any write tool.** The agent must NOT invent or generate the secret; it has to come from the user's clipboard / vault. Phrase the ask explicitly: \"Paste the new value for ${key} (it will be encrypted at rest and masked everywhere except the running container).\"\n3. Once the user provides the value:\n - \\`set_env_var({ service_id: \"${service_id}\", key: \"${key}\", value: \"<new value>\", is_secret: true })\\` — upserts by key.\n4. After the write succeeds, ask the user whether to redeploy (so the new value lands in the running container). If yes:\n - \\`trigger_deploy({ service_id: \"${service_id}\" })\\` — kicks off a build with the rotated secret.\n - \\`get_deploy({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — poll until status is \\`live\\` or \\`failed\\`. On failure, hand off to \\`diagnose_failed_deploy\\`.\n\nSafety rails:\n- Never echo the new value back in your reply; the user should verify it from their own source of truth, not from your context window.\n- If the user says \"generate a new one\", suggest \\`openssl rand -hex 32\\` (or similar) for them to run locally and paste in. Do NOT generate it for them — secrets that pass through an LLM context are no longer secrets.\n- If \\`set_env_var\\` returned \\`action: \"created\"\\` instead of \\`\"updated\"\\`, the key didn't exist before. Pause and confirm with the user — they may have typo'd the key name.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n","/**\n * Per-resource shape pickers. Each function takes a raw API/SDK response and\n * returns the agent-friendly subset: drops internal foreign keys (teamId,\n * userId, ...) and null-valued columns, leaves ISO-8601 timestamps as-is.\n *\n * Pickers must be permissive about input shape — different SDK methods may\n * return slightly different fields (e.g. list vs get) and the API may grow\n * fields over time. We never throw on missing fields.\n */\n\nconst INTERNAL_FIELDS = new Set([\n\t'teamId',\n\t'team_id',\n\t'userId',\n\t'user_id',\n\t'accountId',\n\t'account_id',\n\t'tenantId',\n\t'tenant_id',\n\t'createdById',\n\t'updatedById',\n]);\n\ntype Obj = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is Obj {\n\treturn value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction dropNullsAndInternals(obj: Obj): Obj {\n\tconst out: Obj = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (INTERNAL_FIELDS.has(key)) continue;\n\t\tif (value === null || value === undefined) continue;\n\t\tout[key] = value;\n\t}\n\treturn out;\n}\n\n/** Generic shape — drop internals, drop nulls, keep everything else. */\nexport function shape(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(shape);\n\tif (isObject(value)) return dropNullsAndInternals(value);\n\treturn value;\n}\n\nfunction shapeAll<T>(arr: unknown, shaper: (item: unknown) => T): T[] {\n\tif (!Array.isArray(arr)) return [];\n\treturn arr.map(shaper);\n}\n\n/**\n * Unwrap a HostStack list response. The API returns shapes like\n * `{ projects: [...] }`, `{ services: [...] }`, etc. — the key varies by\n * resource. Pass the key explicitly so the wrapper stays type-loose.\n */\nexport function shapeList<T>(\n\tresponse: unknown,\n\tkey: string,\n\tshaper: (item: unknown) => T,\n): { items: T[] } {\n\tif (!isObject(response)) return { items: [] };\n\treturn { items: shapeAll(response[key], shaper) };\n}\n\n// ──────────────────────────────────────────────────────────────────────\n// Per-resource pickers — typed convenience wrappers around shape().\n// All return a plain Obj (never null), keeping the registry handlers simple.\n// ──────────────────────────────────────────────────────────────────────\n\nexport function shapeProject(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeService(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDeploy(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDatabase(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDomain(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeEnvVar(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeCronExecution(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeActivity(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeUser(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeTeam(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeVolume(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n","import { shapeProject, shapeService, shapeTeam, shapeUser } from '../lib/shape.js';\nimport { defineResource, jsonResource } from './registry.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineResource({\n\tkind: 'static',\n\tname: 'team',\n\turi: 'hoststack://team',\n\tdescription:\n\t\t'Authenticated team identity + a quick orientation snapshot. Includes the team record, the user behind the API key, and counts of projects and services so an agent can decide whether to fan out into list_projects / list_services without an extra round-trip.',\n\tmimeType: 'application/json',\n\tread: async (uri, { hoststack, api, resolveTeamId }) => {\n\t\tconst teamId = await resolveTeamId();\n\n\t\t// Resource handlers must not throw on transient downstream failures —\n\t\t// clients call resources/read for orientation and a network blip\n\t\t// shouldn't poison the entire session. Tolerate partial failure and\n\t\t// surface what we can.\n\t\tconst [me, projectsResult, servicesResult] = await Promise.all([\n\t\t\tapi.get<MeResponse>('/api/auth/me').catch(() => null),\n\t\t\thoststack.projects.list(teamId).catch(() => ({ projects: [] as unknown[] })),\n\t\t\thoststack.services.list(teamId).catch(() => ({ services: [] as unknown[] })),\n\t\t]);\n\n\t\tconst projects = (projectsResult.projects as unknown[]).slice(0, 10).map(shapeProject);\n\t\tconst services = (servicesResult.services as unknown[]).slice(0, 10).map(shapeService);\n\n\t\treturn jsonResource(uri, {\n\t\t\tuser: me?.user ? shapeUser(me.user) : null,\n\t\t\tteam: me?.team ? shapeTeam(me.team) : null,\n\t\t\tproject_count: (projectsResult.projects as unknown[]).length,\n\t\t\tservice_count: (servicesResult.services as unknown[]).length,\n\t\t\t// First 10 of each so the resource stays small while still useful\n\t\t\t// for orientation. Use list_projects / list_services for the full set.\n\t\t\tprojects_preview: projects,\n\t\t\tservices_preview: services,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeActivity } from '../lib/shape.js';\n\ninterface ActivityLogResponse {\n\tdata?: unknown[];\n\t// Older API style: pagination meta as a single nested object. Some\n\t// callers still serve this shape, so keep accepting it.\n\tmeta?: unknown;\n\t// Current API style: pagination meta fields are spread flat on the\n\t// response root (`{ data, page, perPage, total, totalPages }`).\n\tpage?: number;\n\tperPage?: number;\n\ttotal?: number;\n\ttotalPages?: number;\n}\n\ndefineTool({\n\tname: 'list_activity_log',\n\tcategory: 'activity-log',\n\tdescription: [\n\t\t'List the team activity audit log: who did what, when. Backed by /api/activity-log/:teamId.',\n\t\t'',\n\t\t'When to use: investigate why a service was changed unexpectedly (\"who deleted that env var?\"), correlate a deploy with a user, or pull recent admin events for a status update.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - page: 1-based page index (default 1).',\n\t\t' - per_page: items per page (default 25, hard cap 100).',\n\t\t' - action: filter to a specific action (e.g. \"service.created\", \"deploy.triggered\").',\n\t\t' - resource_type: filter by resource type (e.g. \"service\", \"deploy\", \"domain\").',\n\t\t' - user_id: filter by acting user numeric ID.',\n\t\t' - since/until: ISO-8601 timestamp OR a relative offset like \"-15m\" / \"-2h\" / \"-7d\" — narrow to a time window (handy for correlating a deploy with a log spike).',\n\t\t'',\n\t\t'Returns: { items: ActivityLogEntry[], meta: { page, perPage, total, totalPages } } — each entry has id, action, resourceType, resourceId, actorEmail, ipAddress, createdAt, and a context payload. Use meta.totalPages to tell whether you have more.',\n\t\t'',\n\t\t'Example: list_activity_log({ resource_type: \"deploy\", per_page: 10 }) → { items: [{ action: \"deploy.triggered\", actorEmail: \"ada@…\", … }], meta: { total: 47, page: 1 } }',\n\t].join('\\n'),\n\tinput: {\n\t\tpage: z.number().int().positive().optional().describe('Page index, 1-based.'),\n\t\tper_page: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Items per page, hard cap 100.'),\n\t\taction: z.string().optional().describe('Action filter, e.g. \"service.created\".'),\n\t\tresource_type: z.string().optional().describe('Resource type filter.'),\n\t\tuser_id: z.number().int().positive().optional().describe('Numeric acting-user filter.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-15m\", \"-2h\", \"-7d\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.page !== undefined) params['page'] = String(args.page);\n\t\tif (args.per_page !== undefined) params['perPage'] = String(args.per_page);\n\t\tif (args.action !== undefined) params['action'] = args.action;\n\t\tif (args.resource_type !== undefined) params['resourceType'] = args.resource_type;\n\t\tif (args.user_id !== undefined) params['userId'] = String(args.user_id);\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\n\t\tconst response = await ctx.api.get<ActivityLogResponse>(\n\t\t\t`/api/activity-log/${teamId}`,\n\t\t\tparams,\n\t\t);\n\t\tconst items = Array.isArray(response.data) ? response.data.map(shapeActivity) : [];\n\t\tconst data: { items: unknown[]; meta?: unknown } = { items };\n\t\t// The API spreads pagination at the top level (`{ data, page, perPage,\n\t\t// total, totalPages }`). Older variants nested it under `meta`.\n\t\t// Surface a single `meta` object regardless so MCP callers don't have\n\t\t// to learn two shapes.\n\t\tif (response.meta !== undefined) {\n\t\t\tdata.meta = shape(response.meta);\n\t\t} else if (\n\t\t\tresponse.page !== undefined ||\n\t\t\tresponse.perPage !== undefined ||\n\t\t\tresponse.total !== undefined ||\n\t\t\tresponse.totalPages !== undefined\n\t\t) {\n\t\t\tdata.meta = shape({\n\t\t\t\tpage: response.page,\n\t\t\t\tperPage: response.perPage,\n\t\t\t\ttotal: response.total,\n\t\t\t\ttotalPages: response.totalPages,\n\t\t\t});\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\titems.length === 0\n\t\t\t\t\t? 'No activity log entries match the given filters.'\n\t\t\t\t\t: `Returned ${items.length} activity log entr${items.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import type { CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';\n\ninterface RespondInput {\n\tsummary: string;\n\tdata?: unknown;\n}\n\nexport const SUMMARY_MAX_LEN = 500;\n\n/**\n * Build an MCP tool response with a 1–3 line plain-text summary, a fenced\n * JSON block for human readability, and a structuredContent object that\n * agents can parse without re-tokenising the JSON text.\n */\nexport function respond({ summary, data }: RespondInput): CallToolResult {\n\tconst trimmed = summary.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('respond(): summary must be non-empty');\n\t}\n\tif (trimmed.length > SUMMARY_MAX_LEN) {\n\t\tthrow new Error(`respond(): summary too long (${trimmed.length} > ${SUMMARY_MAX_LEN})`);\n\t}\n\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: trimmed }];\n\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\n\tconst result: CallToolResult = { content: blocks };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nexport function respondError(message: string, data?: unknown): CallToolResult {\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: `Error: ${message}` }];\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\tconst result: CallToolResult = { content: blocks, isError: true };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nfunction toStructuredContent(data: unknown): Record<string, unknown> {\n\tif (data !== null && typeof data === 'object' && !Array.isArray(data)) {\n\t\treturn data as Record<string, unknown>;\n\t}\n\treturn { result: data };\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface AlertsResponse {\n\talerts: unknown[];\n\taggregated: boolean;\n}\n\ndefineTool({\n\tname: 'list_alerts',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List recent alert-shaped events for the team: deploy failures, git auth losses, service health failures, auto-restarts, ACME cert failures, resource alerts (high CPU), database backup/restore failures, registrant verification lapses.',\n\t\t'',\n\t\t\"When to use: triage 'what's currently broken or recently broke for this team'. Pairs with list_activity_log for the full audit feed; this tool is the alert-shaped subset.\",\n\t\t'',\n\t\t'By default events are AGGREGATED by (action, resourceId) so flapping events collapse to one row with a fire count + first/last timestamps — e.g. \"service.auto_restarted on service 31, 8 times in the last hour, last at 14:22\". Pass aggregate=false to see every raw row.',\n\t\t'',\n\t\t'ACTIVE vs HISTORICAL (v89): each aggregated entry now has an `active` boolean and a `lastResolvedAt` timestamp. `active=true` means the most recent occurrence has NOT been resolved — the alert is still on fire. The default `active=true` filter hides resolved/historical alerts so triage starts with \"what is broken right now\". Pass `active=false` to include resolved entries too (useful for post-mortems). Resolution is automatic for some alert kinds — e.g. `deploy.failed_consecutive` is cleared when the next deploy of the same service goes live.',\n\t\t'',\n\t\t'Database backup_failed entries (v89) include `containerStatus` / `containerHealth` / `containerExitCode` in lastMetadata when the agent could resolve the container at failure time, so you can tell \"Redis is overloaded\" apart from \"Redis is restarting\" without a second tool call.',\n\t\t'',\n\t\t'Deploy.failed_consecutive entries (v89) carry the offending `commitHash` in lastMetadata, and the streak now dedupes per (service, commitHash) — one critical per bad commit, not one every 6 retries.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - since: ISO-8601 timestamp OR relative offset like \"-1h\" / \"-2d\". Default: -24h.',\n\t\t' - until: ISO-8601 upper bound (ignored when aggregating — aggregated view always extends to now).',\n\t\t' - limit: max rows (default 100, hard cap 500).',\n\t\t' - aggregate: true (default) collapses by (action, resourceId); false returns raw rows.',\n\t\t' - active: true (default) shows only alerts that are still on fire (resolved_at IS NULL on the most recent row). false includes resolved historical alerts.',\n\t\t'',\n\t\t'Returns: { alerts: Array, aggregated: boolean, activeOnly: boolean }. Each aggregated entry includes action, resourceType, resourceId, severity, count, firstFiredAt, lastFiredAt, lastResolvedAt (nullable), active (boolean), lastMetadata. Each raw entry adds resolvedAt (nullable).',\n\t\t'',\n\t\t\"Example: list_alerts({ since: '-1h' }) → { alerts: [{ action: 'deploy.failed_consecutive', resourceId: 31, severity: 'critical', active: true, count: 3, lastFiredAt: '…', lastResolvedAt: null, lastMetadata: { commitHash: 'abc1234' }, … }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-1h\", \"-2d\"). Default: -24h.'),\n\t\tuntil: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 upper bound. Only honored when aggregate=false.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Max rows (default 100, hard cap 500).'),\n\t\taggregate: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Collapse by (action, resourceId). Default true.'),\n\t\tactive: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Only return alerts still on fire (resolved_at IS NULL on the most recent row). Default true. Pass false to include resolved historical alerts.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\t\tif (args.limit !== undefined) params['limit'] = String(args.limit);\n\t\t// API expects aggregate=1|0; default behavior on the API side is\n\t\t// aggregate=true. Pass explicit \"0\" only when caller opts out so\n\t\t// we don't ship spurious query params.\n\t\tif (args.aggregate === false) params['aggregate'] = '0';\n\t\t// v89: same convention for active=1|0. Default true on the API\n\t\t// side, so we only ship `active=0` when the caller opts in to\n\t\t// historical entries.\n\t\tif (args.active === false) params['active'] = '0';\n\n\t\tconst response = await ctx.api.get<AlertsResponse>(`/api/alerts/${teamId}`, params);\n\t\tconst items = Array.isArray(response.alerts) ? response.alerts.map(shape) : [];\n\t\tconst aggregated = Boolean(response.aggregated);\n\t\tconst activeOnly = args.active !== false;\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? activeOnly\n\t\t\t\t\t? 'No active alerts in the requested window — everything is operating normally.'\n\t\t\t\t\t: 'No alerts in the requested window (active or resolved).'\n\t\t\t\t: aggregated\n\t\t\t\t\t? `Returned ${items.length} alert group${items.length === 1 ? '' : 's'}${activeOnly ? ' (active only — pass active=false for resolved history)' : ''} (flapping events collapsed).`\n\t\t\t\t\t: `Returned ${items.length} raw alert event${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { alerts: items, aggregated, activeOnly } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeCronExecution, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_cron_executions',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'List recent execution records for a cron service (newest first).',\n\t\t'',\n\t\t'When to use: investigating whether a scheduled job ran on time, finding the most recent failure, or auditing cron run-times. Only works on services of type \"cron\"; for web services this returns an error.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - limit (optional): how many executions to fetch (default 50, max enforced server-side).',\n\t\t'',\n\t\t'Returns: { items: CronExecution[] } — id, publicId, status (succeeded|failed|running|queued), startedAt, finishedAt, exitCode, triggeredBy.',\n\t\t'',\n\t\t'Example: list_cron_executions({ service_id: \"svc_cron\" }) → { items: [{ status: \"succeeded\", exitCode: 0, … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Max executions to return (default 50).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts = args.limit !== undefined ? { limit: args.limit } : undefined;\n\t\tconst response = await ctx.hoststack.cron.list(teamId, args.service_id, opts);\n\t\tconst data = shapeList(response, 'executions', shapeCronExecution);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No cron executions yet for ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} cron execution${data.items.length === 1 ? '' : 's'} for ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_cron_execution',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'Fetch a single cron execution record. Includes status, exit code, and timing.',\n\t\t'',\n\t\t'When to use: drilling into a specific run after list_cron_executions surfaced a failure, or correlating an execution timestamp with logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - execution_id: publicId of the execution record.',\n\t\t'',\n\t\t'Returns: { execution: CronExecution }.',\n\t\t'',\n\t\t'Example: get_cron_execution({ service_id: \"svc_cron\", execution_id: \"exe_xyz\" }) → { execution: { status: \"failed\", exitCode: 1, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\texecution_id: z.string().describe('Execution publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.cron.get(teamId, args.service_id, args.execution_id);\n\t\tconst data = { execution: shapeCronExecution(response.execution) };\n\t\tconst status =\n\t\t\tdata.execution && 'status' in data.execution\n\t\t\t\t? (data.execution as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Execution ${args.execution_id} ${status}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDatabase, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_databases',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'List managed databases (Postgres, Redis, MySQL, MariaDB, MongoDB, Meilisearch, NATS) inside a project.',\n\t\t'',\n\t\t'When to use: an agent needs to know what data stores exist in a project before connecting a service or running a migration. Pair with list_projects to discover project IDs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project ID (from list_projects).',\n\t\t'',\n\t\t'Returns: { items: Database[] } — id, publicId, name, type, status, version, projectId, createdAt.',\n\t\t'',\n\t\t'Example: list_databases({ project_id: 12 }) → { items: [{ publicId: \"db_…\", type: \"postgres\", status: \"running\", … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric project ID (from list_projects).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'databases', shapeDatabase);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No databases in project ${args.project_id}.`\n\t\t\t\t: `Found ${data.items.length} database${data.items.length === 1 ? '' : 's'} in project ${args.project_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Update a managed database — rename, change plan tier (memory/CPU), or grow disk size.',\n\t\t'',\n\t\t'When to use: the user wants to resize a database (disk or plan), or rename it. Disk can only grow — shrinking is rejected because the underlying filesystem would orphan data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t' - name (optional): new database name.',\n\t\t' - plan (optional): \"free\" | \"micro\" | \"starter\" | \"standard\" | \"pro\" — changes memory tier.',\n\t\t' - disk_size_gb (optional): new disk size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { database: Database } — the updated record.',\n\t\t'',\n\t\t'Example: update_database({ database_id: \"db_xyz\", disk_size_gb: 50 }) → { database: { diskSizeGb: 50, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t\tname: z.string().min(1).max(100).optional().describe('New database name.'),\n\t\tplan: z\n\t\t\t.enum(['free', 'micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1024)\n\t\t\t.optional()\n\t\t\t.describe('New disk size in GB. Must be ≥ current.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname?: string;\n\t\t\tplan?: 'free' | 'micro' | 'starter' | 'standard' | 'pro';\n\t\t\tdiskSizeGb?: number;\n\t\t} = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.disk_size_gb !== undefined) input.diskSizeGb = args.disk_size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.databases.update(teamId, args.database_id, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on database ${args.database_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Fetch a single managed database by ID. Includes status, type, version, and project link.',\n\t\t'',\n\t\t'When to use: confirming the version of a database before generating connection strings, or checking the status of a recently created database. To retrieve the actual connection credentials, use the dashboard — credentials are intentionally not exposed via this MCP tool.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { database: Database }.',\n\t\t'',\n\t\t'Example: get_database({ database_id: \"db_xyz\" }) → { database: { type: \"postgres\", version: \"16\", status: \"running\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.get(teamId, args.database_id);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst type =\n\t\t\tdata.database && 'type' in data.database\n\t\t\t\t? (data.database as { type: string }).type\n\t\t\t\t: 'unknown';\n\t\tconst status =\n\t\t\tdata.database && 'status' in data.database\n\t\t\t\t? (data.database as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Database ${args.database_id} (${type}) is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'query_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Run a single READ-ONLY SQL statement against a managed Postgres database.',\n\t\t'',\n\t\t'When to use: ad-hoc data inspection — checking row counts, sampling rows, debugging a constraint. For schema changes or seed data, ship a Drizzle migration instead.',\n\t\t'',\n\t\t'Safety model (server-side, not bypassable):',\n\t\t' - Wrapped in `BEGIN TRANSACTION READ ONLY` — INSERT/UPDATE/DELETE/DDL fail with a clear \"cannot execute … in a read-only transaction\" error.',\n\t\t' - 30s statement_timeout — runaway queries are killed.',\n\t\t' - 1000-row cap; result CSV is also size-capped at 1 MiB. Both surface via `truncated: true`.',\n\t\t' - Postgres engine only (v1). Other engines return a 400.',\n\t\t' - Single statement only; no embedded `;` and no psql meta-commands (`\\\\…`).',\n\t\t' - Every call is audit-logged (success or failure) as `database.query` so an admin can answer \"who queried this DB, when, with what SQL\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (e.g. \"db_…\"). Resolved via the SDK\\'s publicId helper.',\n\t\t' - sql: a single SELECT/WITH/SHOW/EXPLAIN statement, no trailing `;`. Max 16 KiB.',\n\t\t'',\n\t\t'Returns: { columns: string[], rows: string[][], rowCount: number, truncated: boolean, durationMs: number }. Values come back as strings (psql --csv); cast on the caller side when needed.',\n\t\t'',\n\t\t'Example:',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT count(*) FROM users\" }) → { columns: [\"count\"], rows: [[\"1234\"]], … }',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT id, email FROM users WHERE created_at > now() - interval \\'1 day\\' LIMIT 50\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc).'),\n\t\tsql: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(16_384)\n\t\t\t.describe(\n\t\t\t\t'A single READ-ONLY SQL statement. No trailing `;`, no embedded `;`, no psql meta-commands.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.query(teamId, args.database_id, args.sql);\n\t\tconst summary = result.truncated\n\t\t\t? `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} (truncated; raise LIMIT if needed) in ${result.durationMs}ms.`\n\t\t\t: `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} in ${result.durationMs}ms.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_to_ha',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Trigger a single-node → HA migration on a managed Postgres database. The agent runs pg_dump → bootstrap 3-node Patroni cluster (1 leader + 2 sync replicas + HAProxy) → pg_restore. Customer connections to this database are briefly read-only during the cutover; the standalone container stays running (read-only) for 24 h as a rollback target before automatic decommission.',\n\t\t'',\n\t\t'Requires: PATRONI_ENABLED env on the HostStack deployment + `teams.ha_beta = true` on the calling team. Returns 403 otherwise. Returns 400 if the database is already HA, not Postgres, not on the standard or pro tier, or has a pg_database_size over 1 GB (in-memory archive transfer cap).',\n\t\t'',\n\t\t'When to use: a customer needs failover protection on an existing standalone Postgres. For new databases on an HA-eligible team, just call create_database — the cluster path is automatic.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database to upgrade.',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `pg_engine_type === \"patroni\"` and `status === \"available\"` to confirm completion. The Cluster tab on the dashboard mirrors the same data.',\n\t\t'',\n\t\t'Example: upgrade_database_to_ha({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade to HA.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeToHa(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `HA migration started for ${args.database_id}. Poll get_database until pg_engine_type=patroni to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_version',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Upgrade a standalone managed database (postgres or redis) to a newer engine version in place. The agent dumps the live data, recreates the container at the target version under the same DNS name, and restores — so connection URLs stay valid and services do NOT need a redeploy. The database is briefly unavailable (seconds) during the cut-over; on any failure the original container is rolled back automatically.',\n\t\t'',\n\t\t'`version` must be a supported, strictly-newer version for the engine (postgres: 18/17/16/15, redis: 8/7/6). Downgrades are rejected. Returns 400 for unsupported engines (mysql/mariadb/mongodb upgrades are not implemented yet), for HA (Patroni) clusters, or if the database is not currently `available`.',\n\t\t'',\n\t\t'When to use: a customer wants to move to a newer Postgres/Redis major version. Check get_database first — the `version` field shows the current version; compare against the engine latest.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres/redis database to upgrade.',\n\t\t' - version: target engine version (e.g. \"18\" for postgres, \"8\" for redis).',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `version` matches the target and `status === \"available\"` to confirm.',\n\t\t'',\n\t\t'Example: upgrade_database_version({ database_id: \"db_abc\", version: \"18\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade.'),\n\t\tversion: z\n\t\t\t.string()\n\t\t\t.describe('Target engine version, e.g. \"18\" for postgres or \"8\" for redis.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeVersion(teamId, args.database_id, args.version);\n\t\treturn respond({\n\t\t\tsummary: `Version upgrade to v${args.version} started for ${args.database_id}. Poll get_database until version=${args.version} and status=available to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_database_cluster',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Get the live + retired cluster members and failover history for a Patroni HA Postgres database.',\n\t\t'',\n\t\t'When to use: confirm an HA cluster is healthy (1 leader + 2 sync replicas + 1 proxy + 1 etcd live) or investigate a failover that fired. For standalone databases this returns 400 — call get_database first if you need to branch.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (must be HA).',\n\t\t'',\n\t\t\"Returns: { members: { id, memberRole: 'etcd' | 'primary-candidate' | 'replica' | 'proxy', containerId, workerHostId, joinedAt, leftAt }[], failovers: { id, createdAt, oldLeader, newLeader }[] }.\",\n\t\t'',\n\t\t'Example: get_database_cluster({ database_id: \"db_abc\" }) → { members: [...5 live rows], failovers: [] }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId for the HA cluster.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.getCluster(teamId, args.database_id);\n\t\tconst live = result.members.filter((m) => m.leftAt === null);\n\t\tconst summary = `${live.length} live members (${live.map((m) => m.memberRole).join(', ')}); ${result.failovers.length} historical failovers.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeDeploy, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_deploys',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'List deploys for a service in reverse-chronological order (newest first).',\n\t\t'',\n\t\t'When to use: investigating a recent deploy outcome, finding the last successful deploy ID, comparing run-times, or just checking deploy history before triggering a new one.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\"). Use list_services to find it.',\n\t\t'',\n\t\t'Returns: { items: Deploy[] } — each deploy includes id, publicId, status (pending|building|deploying|live|failed|cancelled), commitSha, commitMessage, branch, triggeredBy, startedAt, finishedAt, imageBuildMs (docker build / image-pull only; null on skip-build redeploys — v89), containerBootMs (deploying → live wall-clock; null on builds that failed before container start — v89), buildDurationMs (legacy alias of imageBuildMs kept for back-compat), totalDurationMs (full deploy wall-clock = finishedAt − startedAt). Use imageBuildMs + containerBootMs together to tell \"build is slow\" apart from \"boot is slow\".',\n\t\t'',\n\t\t'Example: list_deploys({ service_id: \"svc_abc\" }) → { items: [{ publicId: \"dpl_…\", status: \"live\", commitMessage: \"Fix login\", … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.list(teamId, args.service_id);\n\t\t// Deploys list is paginated and returns `{ data, page, ... }` — not the\n\t\t// bare `{ deploys: [] }` shape every other list endpoint uses.\n\t\tconst data = shapeList(response, 'data', shapeDeploy);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No deploys yet for service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} deploy${data.items.length === 1 ? '' : 's'} for service ${args.service_id} (page ${response.page} of ${response.totalPages}, ${response.total} total).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch a single deploy by ID, including its current status, commit metadata, and timestamps.',\n\t\t'',\n\t\t\"When to use: drilling into the details of a specific deploy after list_deploys, polling a build's status, or grabbing the commit SHA so you can correlate logs with code.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy (e.g. \"dpl_…\").',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — full deploy record (status, commitSha, commitMessage, branch, imageBuildMs + containerBootMs (v89 split timings; legacy buildDurationMs preserved as alias), totalDurationMs, finishedAt, etc).',\n\t\t'',\n\t\t'Example: get_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { deploy: { status: \"live\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst status =\n\t\t\tdata.deploy && typeof data.deploy === 'object' && 'status' in data.deploy\n\t\t\t\t? (data.deploy as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Deploy ${args.deploy_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'trigger_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Kick off a new deploy. Defaults to the latest commit on the service branch; pass commit_hash or branch to deploy something else.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to deploy (\"ship it\", \"redeploy\", \"kick off a new build\"). For services with auto-deploy enabled, a fresh git push already triggers a deploy automatically — only call this for manual triggers or after a config change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - commit_hash (optional): build a specific commit instead of HEAD on the configured branch.',\n\t\t' - branch (optional): build the tip of a non-default branch.',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy record. Status will start as \"pending\" then transition through \"building\" → \"deploying\" → \"live\" or \"failed\".',\n\t\t'',\n\t\t'Example: trigger_deploy({ service_id: \"svc_abc\" }) → { deploy: { publicId: \"dpl_…\", status: \"pending\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tcommit_hash: z\n\t\t\t.string()\n\t\t\t.max(40)\n\t\t\t.optional()\n\t\t\t.describe('Specific commit to build (default: branch HEAD).'),\n\t\tbranch: z\n\t\t\t.string()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Branch to build (default: service configured branch).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { commitHash?: string; branch?: string } = {};\n\t\tif (args.commit_hash !== undefined) input.commitHash = args.commit_hash;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tconst response = await ctx.hoststack.deploys.trigger(teamId, args.service_id, input);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst publicId =\n\t\t\tdata.deploy && 'publicId' in data.deploy\n\t\t\t\t? (data.deploy as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({\n\t\t\tsummary: `Triggered deploy ${publicId} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'cancel_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Cancel a running deploy. Stops the build/deploy pipeline mid-flight; the previously live revision keeps serving traffic.',\n\t\t'',\n\t\t'When to use: the user notices a bad commit was pushed and wants to abort before it lands, or a build is hanging. Has no effect on already-finished deploys.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to cancel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: cancel_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.deploys.cancel(teamId, args.service_id, args.deploy_id);\n\t\treturn respond({\n\t\t\tsummary: `Cancelled deploy ${args.deploy_id} on service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy_logs',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch the build/deploy logs for a single deploy. Returns the entire log buffer the agent can then search for errors.',\n\t\t'',\n\t\t'When to use: a deploy failed and you need to read the build output to diagnose. Pair with get_deploy to find a failed deploy_id, then call this. For runtime (post-deploy) logs of the running container, use get_service_logs instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy.',\n\t\t'',\n\t\t'Returns: { logs: string, lineCount: number, nextAfterId: number | null } — flattened build output joined with newlines, line count, and a cursor for pagination (pass back as after_id to fetch the next page).',\n\t\t'',\n\t\t'Example: get_deploy_logs({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { logs: \"Building…\\\\nStep 1/8…\\\\n…\", lineCount: 42, nextAfterId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tafter_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.optional()\n\t\t\t.describe('Pagination cursor from a previous response’s nextAfterId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(5000)\n\t\t\t.optional()\n\t\t\t.describe('Max log entries to fetch (default 500, hard cap 5000).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.getLogs(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\t{\n\t\t\t\t...(args.after_id !== undefined ? { afterId: args.after_id } : {}),\n\t\t\t\t...(args.limit !== undefined ? { limit: args.limit } : {}),\n\t\t\t},\n\t\t);\n\t\tconst entries = response.logs;\n\t\tconst logs = entries.map((e) => e.message).join('\\n');\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${entries.length} log line${entries.length === 1 ? '' : 's'} for deploy ${args.deploy_id}.`,\n\t\t\tdata: { logs, lineCount: entries.length, nextAfterId: response.nextAfterId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'diagnose_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t\"One-shot diagnostic bundle for a deploy: full deploy record + tail of build logs + tail of runtime logs starting at the deploy's start time. Lets you investigate any deploy failure mode (build-time failure, health-check timeout, post-deploy runtime crash) without juggling two log endpoints and two IDs.\",\n\t\t'',\n\t\t'When to use: a deploy is failed/superseded/stuck and you want the whole story in one call. For just the build output, use get_deploy_logs. For just runtime logs of the running service, use get_service_logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to diagnose.',\n\t\t' - build_log_lines (optional): how many trailing build log lines to include. Default 200, max 2000.',\n\t\t' - runtime_log_lines (optional): how many trailing runtime log lines (since the deploy started) to include. Default 100, max 1000. Set to 0 to skip runtime logs (saves a round-trip when investigating a pure build-time failure).',\n\t\t'',\n\t\t'Returns: { deploy, build: { logs, lineCount }, runtime: { logs, lineCount } } — runtime is omitted when runtime_log_lines is 0 or the deploy never started a container.',\n\t\t'',\n\t\t'Example: diagnose_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → all three sections in one call.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tbuild_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(2000)\n\t\t\t.optional()\n\t\t\t.describe('Trailing build log lines. Default 200, max 2000.'),\n\t\truntime_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Trailing runtime log lines since deploy start. Default 100, max 1000. 0 = skip.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst buildLimit = args.build_log_lines ?? 200;\n\t\tconst runtimeLimit = args.runtime_log_lines ?? 100;\n\n\t\t// Pull deploy + build logs in parallel — build logs don't depend on\n\t\t// the deploy record's metadata. Runtime logs need the deploy's\n\t\t// startedAt for the `since` window so they wait for the deploy fetch.\n\t\tconst [deployResp, buildResp] = await Promise.all([\n\t\t\tctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id),\n\t\t\tctx.hoststack.deploys.getLogs(teamId, args.service_id, args.deploy_id, {\n\t\t\t\tlimit: buildLimit,\n\t\t\t}),\n\t\t]);\n\n\t\tconst buildEntries = buildResp.logs;\n\t\tconst buildLogs = buildEntries.map((e) => e.message).join('\\n');\n\n\t\tconst deployShape = shapeDeploy(deployResp.deploy);\n\t\tconst deployStatus =\n\t\t\tdeployShape && 'status' in deployShape\n\t\t\t\t? (deployShape as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\tconst startedAtRaw =\n\t\t\tdeployShape && 'startedAt' in deployShape\n\t\t\t\t? (deployShape as { startedAt: unknown }).startedAt\n\t\t\t\t: undefined;\n\t\tconst startedAt = typeof startedAtRaw === 'string' ? startedAtRaw : undefined;\n\n\t\tconst data: Record<string, unknown> = {\n\t\t\tdeploy: deployShape,\n\t\t\tbuild: { logs: buildLogs, lineCount: buildEntries.length },\n\t\t};\n\n\t\t// Skip the runtime tail when caller asked us to OR when the deploy\n\t\t// never reached the container-start phase (build-time failure).\n\t\t// `startedAt` is set by the API when the deploy row is created, so\n\t\t// it's almost always present; the more reliable container-started\n\t\t// signal is whether status ever passed the build phase.\n\t\tconst hadContainer = deployStatus !== 'pending' && deployStatus !== 'building';\n\t\tif (runtimeLimit > 0 && hadContainer) {\n\t\t\tconst runtimeResp = await ctx.hoststack.services.getRuntimeLogs(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\t{\n\t\t\t\t\tlines: runtimeLimit,\n\t\t\t\t\t...(startedAt ? { since: startedAt } : {}),\n\t\t\t\t},\n\t\t\t);\n\t\t\tif ('count' in runtimeResp) {\n\t\t\t\t// getRuntimeLogs without countOnly should never return this\n\t\t\t\t// shape; defensive branch for future-proofing.\n\t\t\t\tdata['runtime'] = { logs: '', lineCount: runtimeResp.count };\n\t\t\t} else {\n\t\t\t\tconst runtimeEntries = Array.isArray(runtimeResp.logs)\n\t\t\t\t\t? runtimeResp.logs\n\t\t\t\t\t: typeof runtimeResp.logs === 'string'\n\t\t\t\t\t\t? runtimeResp.logs.split('\\n').map((line) => ({ message: line }))\n\t\t\t\t\t\t: [];\n\t\t\t\tconst runtimeText = runtimeEntries\n\t\t\t\t\t.map((e) =>\n\t\t\t\t\t\ttypeof e === 'object' && 'message' in e ? String(e.message) : String(e),\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n');\n\t\t\t\tdata['runtime'] = {\n\t\t\t\t\tlogs: runtimeText,\n\t\t\t\t\tlineCount: runtimeEntries.length,\n\t\t\t\t\tsinceWindow: startedAt ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Surface anything that might explain a failure — these field names\n\t\t// vary slightly across deploy phases (build, deploy, healthcheck) so\n\t\t// we look at the deploy shape directly rather than hard-coding.\n\t\tconst failureSummary = shape({\n\t\t\tstatus: deployStatus,\n\t\t\terrorMessage:\n\t\t\t\tdeployShape && 'errorMessage' in deployShape\n\t\t\t\t\t? (deployShape as { errorMessage: unknown }).errorMessage\n\t\t\t\t\t: undefined,\n\t\t});\n\t\tdata['summary'] = failureSummary;\n\n\t\treturn respond({\n\t\t\tsummary: `Diagnostic bundle for deploy ${args.deploy_id} (${deployStatus}): ${buildEntries.length} build log line${buildEntries.length === 1 ? '' : 's'}${runtimeLimit > 0 && hadContainer ? ', runtime log tail included' : ''}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport type { ApiClient } from '../api-client.js';\n\n/**\n * DNS-zone + record management. These tools wrap the same HTTP routes\n * the dashboard uses — `/api/dns-zones/:teamId/...` — so tenancy, audit\n * logging, PowerDNS sync, and idempotency behavior all match the\n * dashboard exactly. We never bypass the service layer.\n *\n * The HTTP-domain tools (list_domains, add_domain, verify_domain,\n * remove_domain) are about binding a hostname to a service for HTTP\n * routing + Let's Encrypt; they don't touch the underlying DNS zone.\n * Use these tools for actual record CRUD (TXT for verification, A/AAAA\n * for custom routing, MX for email, etc.).\n *\n * Source of truth for record types + validation:\n * packages/shared/src/schemas/dns.ts. Mirrored here so the model sees\n * the same enum without the MCP package depending on @hoststack/shared.\n */\nconst DNS_RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS', 'SRV', 'CAA', 'ALIAS'] as const;\n\ninterface ZoneResponse {\n\tpublicId: string;\n\tid: number;\n\tdomainName: string;\n\tstatus: string;\n}\n\ninterface RecordResponse {\n\tpublicId: string;\n\tid: number;\n\tzoneId: number;\n\ttype: string;\n\tname: string;\n\tvalue: string;\n}\n\ninterface ListZonesResponse {\n\tzones: ZoneResponse[];\n}\n\ninterface ListRecordsResponse {\n\trecords: RecordResponse[];\n}\n\ninterface SingleRecordResponse {\n\trecord: RecordResponse;\n}\n\n/**\n * Resolve `{ zone_id?, domain? }` to the zone's `publicId`. Accepts\n * either the zone publicId directly or a domain name (apex or\n * subdomain) and walks parent labels to find the longest-matching\n * hosted zone, same suffix-match semantics as\n * `DnsService.findHostedZoneForDomain` (apps/api/src/services/dns.service.ts:193).\n *\n * Returns the zone's publicId so the caller can address subsequent\n * routes by it, plus a human-readable hint about which zone matched.\n */\nasync function resolveZonePublicId(\n\tapi: ApiClient,\n\tteamId: number,\n\tinput: { zone_id?: string; domain?: string },\n): Promise<{ publicId: string; domainName: string }> {\n\tif (input.zone_id) {\n\t\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tconst match = zones.find((z) => z.publicId === input.zone_id);\n\t\tif (!match) {\n\t\t\tthrow new Error(`Zone ${input.zone_id} not found on this team.`);\n\t\t}\n\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t}\n\tif (!input.domain) {\n\t\tthrow new Error('Provide either zone_id or domain.');\n\t}\n\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\tconst fqdn = input.domain.toLowerCase().replace(/\\.$/, '');\n\tconst labels = fqdn.split('.');\n\tfor (let i = 0; i < labels.length - 1; i++) {\n\t\tconst candidate = labels.slice(i).join('.');\n\t\tconst match = zones.find((z) => z.domainName.toLowerCase() === candidate);\n\t\tif (match && match.status !== 'deleting') {\n\t\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t\t}\n\t}\n\tthrow new Error(\n\t\t`No hosted zone matches ${input.domain}. Create the zone in the dashboard (Domains → DNS) first, then retry.`,\n\t);\n}\n\ndefineTool({\n\tname: 'list_dns_zones',\n\tcategory: 'dns',\n\tdescription: [\n\t\t\"List authoritative DNS zones the team owns on HostStack's PowerDNS infrastructure. Each zone is the apex domain (e.g. example.com) under which DNS records live.\",\n\t\t'',\n\t\t'When to use: discover which apex domains support record management before calling create_dns_record / list_dns_records. The dashboard equivalent is Domains → DNS.',\n\t\t'',\n\t\t'Returns: { items: Zone[] } — each zone exposes publicId, domainName, status (\"active\" | \"syncing\" | \"failed\" | \"deleting\"), nsRecords (the nameservers the parent registry must delegate to), provider, createdAt.',\n\t\t'',\n\t\t'Example: list_dns_zones() → { items: [{ publicId: \"dnz_abc\", domainName: \"micci.dk\", status: \"active\", nsRecords: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"] }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tconst items = Array.isArray(response.zones) ? response.zones.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No DNS zones hosted on this team. Create one in the dashboard (Domains → DNS).'\n\t\t\t\t: `Found ${items.length} hosted DNS zone${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { items } });\n\t},\n});\n\ndefineTool({\n\tname: 'list_dns_records',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'List every active record in a hosted DNS zone. Records are addressed by their type + name + value tuple; PowerDNS gathers records with the same (name, type) into an RRset on the wire.',\n\t\t'',\n\t\t'When to use: audit what is currently published for a zone before editing, verify a recently-created record landed, or read existing TXT verification strings.',\n\t\t'',\n\t\t'Provide EITHER zone_id (the zone publicId, e.g. \"dnz_abc\") OR domain (an apex or subdomain — the longest-matching hosted apex wins; \"app.example.com\" matches a hosted \"example.com\" zone).',\n\t\t'',\n\t\t'Returns: { zone: { publicId, domainName }, items: Record[] } — each record includes publicId, type, name (\"@\" for apex, or label), value, ttl, priority?, status (\"active\" | \"syncing\" | \"failed\"), managedBy (\"user\" | \"hoststack\" | \"poststack\"), createdAt.',\n\t\t'',\n\t\t'Example: list_dns_records({ domain: \"micci.dk\" }) → { zone: { domainName: \"micci.dk\", … }, items: [{ type: \"TXT\", name: \"@\", value: \"google-site-verification=…\", ttl: 300, … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId (e.g. \"dnz_abc\").'),\n\t\tdomain: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Apex or subdomain — resolves to the longest-matching hosted zone.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.api, teamId, zoneInput);\n\t\tconst response = await ctx.api.get<ListRecordsResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t);\n\t\tconst items = Array.isArray(response.records) ? response.records.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `Zone ${zone.domainName} has no records yet.`\n\t\t\t\t: `Returned ${items.length} record${items.length === 1 ? '' : 's'} for ${zone.domainName}.`;\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: { zone: { publicId: zone.publicId, domainName: zone.domainName }, items },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Fetch a single DNS record by its publicId. Useful for confirming the record landed after create_dns_record / update_dns_record (status transitions from \"syncing\" to \"active\" once PowerDNS acknowledges).',\n\t\t'',\n\t\t'When to use: verify a record exists, inspect its current ttl/value, or check the sync status of an in-flight write.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\"). Returned by list_dns_records and create_dns_record.',\n\t\t'',\n\t\t'Returns: { record: Record }. Mirrors the shape returned by list_dns_records.',\n\t\t'',\n\t\t'Example: get_dns_record({ record_id: \"dnr_abc\" }) → { record: { type: \"TXT\", name: \"@\", value: \"…\", status: \"active\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Records have no direct GET-by-id route — walk the team's zones\n\t\t// and scan each for the matching publicId. Zones-per-team is tiny\n\t\t// (≤ a few dozen even on heavy users) so this is cheap.\n\t\tconst { zones } = await ctx.api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\t\tfor (const zone of zones) {\n\t\t\tconst { records } = await ctx.api.get<ListRecordsResponse>(\n\t\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t\t);\n\t\t\tconst match = records.find((r) => r.publicId === args.record_id);\n\t\t\tif (match) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: `Record ${match.type} ${match.name} on ${zone.domainName}.`,\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\t\t\trecord: shape(match),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn respondError(`Record ${args.record_id} not found on any zone owned by this team.`);\n\t},\n});\n\ndefineTool({\n\tname: 'create_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Create a DNS record on a hosted zone. Writes to the DB first, then mirrors to PowerDNS — record returns with status=\"syncing\" until the provider acknowledges, then flips to \"active\". Re-creating the same (zone, type, name, value) tuple is idempotent and returns the existing row.',\n\t\t'',\n\t\t'When to use: add TXT verification records (Google Search Console, domain ownership challenges, DKIM), A/AAAA records pointing a subdomain at an IP, CNAME aliases, MX records, etc.',\n\t\t'',\n\t\t'Provide EITHER zone_id (zone publicId) OR domain (resolves via longest-suffix match against hosted zones).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"A\" | \"AAAA\" | \"CNAME\" | \"MX\" | \"TXT\" | \"NS\" | \"SRV\" | \"CAA\" | \"ALIAS\".',\n\t\t' - name: \"@\" for the zone apex, or a subdomain label (\"www\", \"api\", \"_acme-challenge\"). Do NOT include the zone suffix — the backend prepends it. \"bounce\" on zone \"micci.dk\" publishes \"bounce.micci.dk\"; passing \"bounce.micci.dk\" works (the backend strips the suffix) but is non-idiomatic.',\n\t\t' - content: the literal record value. For TXT pass the unquoted string — the PowerDNS layer handles quoting. For CNAME / MX / NS / SRV / ALIAS the target may be passed with or without a trailing dot; the adapter FQDN-izes it.',\n\t\t' - ttl (optional): 60–86400 seconds, default 3600.',\n\t\t' - priority (optional, required for MX and SRV): 0–65535.',\n\t\t'',\n\t\t'Returns: { record: Record } — see get_dns_record for the shape.',\n\t\t'',\n\t\t'Notes:',\n\t\t' - CNAME at the apex is rejected (RFC 1034 forbids CNAME coexisting with NS/SOA). Use type=\"ALIAS\" for apex aliasing.',\n\t\t'',\n\t\t'Example: create_dns_record({ domain: \"micci.dk\", type: \"TXT\", name: \"@\", content: \"google-site-verification=C0V0scK48g2…\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId.'),\n\t\tdomain: z.string().optional().describe('Apex or subdomain to resolve a hosted zone.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT values are unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\t// MX/SRV records carry priority on the wire; the shared upsert\n\t\t// schema enforces this with a superRefine. The MCP `input` shape\n\t\t// is a flat ZodRawShape so we can't express cross-field rules\n\t\t// there — surface a clean error from the handler instead of\n\t\t// letting the backend's 400 bubble up.\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.api, teamId, zoneInput);\n\n\t\tconst body: {\n\t\t\ttype: string;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.api.post<SingleRecordResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} ${args.name} on ${zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Update an existing DNS record by its publicId. Use this to change a TTL, swap a value, or move the record to a different (name, type) bucket. Full-record PUT semantics: every mutable field must be provided.',\n\t\t'',\n\t\t'When to use: rotate a DKIM key, repoint an A record at a new IP, bump TTL ahead of a planned change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t' - type, name, content: the new values (the route is full-replace, not patch — fetch with get_dns_record first if you only want to tweak one field).',\n\t\t' - ttl (optional), priority (optional, required for MX / SRV).',\n\t\t'',\n\t\t'Returns: { record: Record } reflecting the new state.',\n\t\t'',\n\t\t'Example: update_dns_record({ record_id: \"dnr_abc\", type: \"A\", name: \"api\", content: \"203.0.113.42\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.api, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\n\t\tconst body: {\n\t\t\ttype: string;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.api.put<SingleRecordResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${target.zone.publicId}/records/${args.record_id}`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Updated ${args.type} ${args.name} on ${target.zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Soft-delete a DNS record and remove it from PowerDNS. DESTRUCTIVE: resolvers stop returning the value as soon as the change propagates. Same code path as the dashboard delete button — the row is marked deleted and the RRset is re-pushed without it.',\n\t\t'',\n\t\t'When to use: retire a TXT verification record once the challenge has been confirmed; drop an A record for a decommissioned subdomain.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_dns_record({ record_id: \"dnr_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.api, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tawait ctx.api.delete<{ ok: true }>(\n\t\t\t`/api/dns-zones/${teamId}/${target.zone.publicId}/records/${args.record_id}`,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Deleted record ${args.record_id} from ${target.zone.domainName}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\n/**\n * The DNS HTTP routes nest records under their zone, so update/delete\n * by record publicId needs the owning zone's publicId too. Walk the\n * team's zones until we find a record match. Cheap — zone count is\n * bounded by team-tier limits.\n */\nasync function findRecordZone(\n\tapi: ApiClient,\n\tteamId: number,\n\trecordPublicId: string,\n): Promise<{ zone: ZoneResponse; record: RecordResponse } | null> {\n\tconst { zones } = await api.get<ListZonesResponse>(`/api/dns-zones/${teamId}`);\n\tfor (const zone of zones) {\n\t\tconst { records } = await api.get<ListRecordsResponse>(\n\t\t\t`/api/dns-zones/${teamId}/${zone.publicId}/records`,\n\t\t);\n\t\tconst match = records.find((r) => r.publicId === recordPublicId);\n\t\tif (match) return { zone, record: match };\n\t}\n\treturn null;\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDomain, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_domains',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"List every custom domain attached to the team's services.\",\n\t\t'',\n\t\t'When to use: enumerate live domains, audit DNS verification status, or find which service a hostname resolves to before troubleshooting routing.',\n\t\t'',\n\t\t'Returns: { items: Domain[] } — id, publicId, hostname, serviceId, verified, dnsTargets, sslStatus, createdAt.',\n\t\t'',\n\t\t'Example: list_domains() → { items: [{ hostname: \"api.example.com\", verified: true, sslStatus: \"active\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.list(teamId);\n\t\tconst data = shapeList(response, 'domains', shapeDomain);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No custom domains configured.'\n\t\t\t\t: `Found ${data.items.length} custom domain${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'add_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Attach a custom hostname to the team — optionally pinned to a specific service. After adding, point your DNS at the targets returned and call verify_domain.',\n\t\t'',\n\t\t'When to use: the user wants to put a custom domain in front of a HostStack service (e.g. point api.example.com at svc_abc). The hostname must be unique across the team.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - hostname: the domain to add (e.g. \"api.example.com\").',\n\t\t' - service_id: publicId of the service to bind the domain to.',\n\t\t' - path_prefix (optional): when set, only requests under this URL prefix route to this service. Use for path-based fan-out (e.g. `/api` to an api service, `/` to a web service on the same hostname).',\n\t\t'',\n\t\t'Returns: { domain: Domain } — includes dnsTargets you must configure (CNAME / A records).',\n\t\t'',\n\t\t'Example: add_domain({ hostname: \"api.example.com\", service_id: \"svc_abc\" }) → { domain: { hostname: \"api.example.com\", dnsTargets: [...], verified: false, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\thostname: z\n\t\t\t.string()\n\t\t\t.min(3)\n\t\t\t.max(253)\n\t\t\t.describe('Fully-qualified hostname (e.g. api.example.com).'),\n\t\tservice_id: z.string().describe('Service publicId to bind to.'),\n\t\tpath_prefix: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Optional URL prefix for path-based routing (e.g. \"/api\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { domain: string; serviceId: string; pathPrefix?: string } = {\n\t\t\tdomain: args.hostname,\n\t\t\tserviceId: args.service_id,\n\t\t};\n\t\tif (args.path_prefix !== undefined) input.pathPrefix = args.path_prefix;\n\t\tconst response = await ctx.hoststack.domains.add(teamId, input);\n\t\tconst data = { domain: shapeDomain(response.domain) };\n\t\treturn respond({\n\t\t\tsummary: `Added domain ${args.hostname}. Configure DNS, then call verify_domain.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Trigger DNS verification for a previously-added domain. HostStack checks that the dnsTargets returned by add_domain are configured at the registrar.',\n\t\t'',\n\t\t'When to use: the user has set up DNS records and is ready to flip the domain live. Returns success when verification passes; the domain stays in pending state if DNS is still propagating.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains or add_domain).',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-call list_domains to inspect the updated verified flag and SSL status.',\n\t\t'',\n\t\t'Example: verify_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.verify(teamId, args.domain_id);\n\t\treturn respond({\n\t\t\tsummary: `Triggered DNS verification for ${args.domain_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'remove_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Detach a custom hostname from the team. DESTRUCTIVE: the domain stops routing immediately and any pinned service must fall back to its default hostname.',\n\t\t'',\n\t\t'When to use: the user wants to retire a custom domain. Confirm with the user first — the change is immediate and cannot be undone except by re-adding the domain and re-verifying DNS.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain to remove.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: remove_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.remove(teamId, args.domain_id);\n\t\treturn respond({ summary: `Removed domain ${args.domain_id}.`, data: { ok: true } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shapeEnvVar, shapeList } from '../lib/shape.js';\n\ninterface ExistingEnvVar {\n\tid: number;\n\tpublicId?: string;\n\tkey: string;\n\tisSecret: boolean;\n}\n\ndefineTool({\n\tname: 'list_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'List environment variables for a service. Secret values are masked server-side (\"••••••\"); only non-secret values come through in the clear.',\n\t\t'',\n\t\t'When to use: auditing what configuration a service has, finding the key for a value the user mentions by name, or confirming a variable was set after a write. Never assume the value field is plaintext for secret rows — it is masked.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: EnvVar[] } — id, publicId, key, value (masked if isSecret), isSecret, target, linkedDatabaseId, createdAt.',\n\t\t'',\n\t\t'Example: list_env_vars({ service_id: \"svc_abc\" }) → { items: [{ key: \"DATABASE_URL\", value: \"••••••\", isSecret: true }, { key: \"PORT\", value: \"3000\", isSecret: false }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'envVars', shapeEnvVar);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No env vars set on service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} env var${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'set_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Upsert a single environment variable on a service: creates the key if missing, updates the value if it already exists. Match is by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: rotate a secret, add a new config knob, or change a value the user dictated. The MCP layer looks up the existing var by key first; you do not need to know the env-var ID.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name (e.g. \"DATABASE_URL\").',\n\t\t' - value: new value (will be encrypted at rest if is_secret=true).',\n\t\t' - is_secret (optional): true marks the value as secret (masked on read). On create, defaults to true for safety. On update, omitting it leaves the existing flag untouched — pass it explicitly only when you want to change classification.',\n\t\t'',\n\t\t'Returns: { envVar: EnvVar, action: \"created\" | \"updated\" }.',\n\t\t'',\n\t\t'Example: set_env_var({ service_id: \"svc_abc\", key: \"DATABASE_URL\", value: \"postgres://…\", is_secret: true }) → { envVar: { key: \"DATABASE_URL\", value: \"••••••\", … }, action: \"updated\" }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key.'),\n\t\tvalue: z.string().describe('New value.'),\n\t\tis_secret: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Mark as secret (encrypted, masked on read). Default true.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\n\t\tif (match) {\n\t\t\t// On update, only forward `isSecret` when the caller passed it\n\t\t\t// explicitly. Defaulting to `true` here would silently flip\n\t\t\t// previously-plaintext keys (PORT, NODE_ENV, …) to secret on\n\t\t\t// every value change.\n\t\t\tconst updatePayload: { value: string; isSecret?: boolean } = { value: args.value };\n\t\t\tif (args.is_secret !== undefined) updatePayload.isSecret = args.is_secret;\n\t\t\tconst response = await ctx.hoststack.envVars.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tString(match.id),\n\t\t\t\tupdatePayload,\n\t\t\t);\n\t\t\tconst data = {\n\t\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\t\taction: 'updated' as const,\n\t\t\t};\n\t\t\treturn respond({ summary: `Updated ${args.key} on service ${args.service_id}.`, data });\n\t\t}\n\n\t\tconst response = await ctx.hoststack.envVars.create(teamId, args.service_id, {\n\t\t\tkey: args.key,\n\t\t\tvalue: args.value,\n\t\t\tisSecret: args.is_secret ?? true,\n\t\t});\n\t\tconst data = {\n\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\taction: 'created' as const,\n\t\t};\n\t\treturn respond({ summary: `Created ${args.key} on service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Remove an environment variable from a service by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: cleaning up unused config, or rotating away from a leaked secret. The MCP looks up the env-var ID from the key automatically.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name to delete.',\n\t\t'',\n\t\t'Returns: { ok: true } on success. Returns an error if the key was not found.',\n\t\t'',\n\t\t'Example: delete_env_var({ service_id: \"svc_abc\", key: \"OLD_FLAG\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key to delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\t\tif (!match) {\n\t\t\treturn respondError(`Env var \"${args.key}\" not found on service ${args.service_id}.`, {\n\t\t\t\tkey: args.key,\n\t\t\t\tservice_id: args.service_id,\n\t\t\t});\n\t\t}\n\t\tawait ctx.hoststack.envVars.delete(teamId, args.service_id, String(match.id));\n\t\treturn respond({\n\t\t\tsummary: `Deleted ${args.key} from service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'bulk_set_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Replace the entire env-var set on a service with the given list. Equivalent to deleting all current vars and creating the supplied ones — use with care.',\n\t\t'',\n\t\t'When to use: importing a .env file, mirroring config from a sibling service, or doing a clean reset. For incremental changes, use set_env_var per key — bulk_set is destructive for any key not in the supplied list.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - env_vars: array of { key, value, is_secret? }. is_secret defaults to true per row.',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-list with list_env_vars to confirm the new state.',\n\t\t'',\n\t\t'Example: bulk_set_env_vars({ service_id: \"svc_abc\", env_vars: [{ key: \"PORT\", value: \"3000\", is_secret: false }, { key: \"DATABASE_URL\", value: \"…\", is_secret: true }] }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tenv_vars: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tkey: z.string().min(1).max(128),\n\t\t\t\t\tvalue: z.string(),\n\t\t\t\t\tis_secret: z.boolean().optional(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(500)\n\t\t\t.describe('Array of env-var rows. Hard cap 500.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst payload = {\n\t\t\tvars: args.env_vars.map((v) => {\n\t\t\t\tconst row: { key: string; value: string; isSecret?: boolean } = {\n\t\t\t\t\tkey: v.key,\n\t\t\t\t\tvalue: v.value,\n\t\t\t\t};\n\t\t\t\tif (v.is_secret !== undefined) row.isSecret = v.is_secret;\n\t\t\t\treturn row;\n\t\t\t}),\n\t\t};\n\t\tawait ctx.hoststack.envVars.bulkSet(teamId, args.service_id, payload);\n\t\treturn respond({\n\t\t\tsummary: `Replaced env-var set on service ${args.service_id} with ${args.env_vars.length} entr${args.env_vars.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata: { ok: true, count: args.env_vars.length },\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * v66 P5: MCP tools for project environment management.\n *\n * Environments isolate services in the same project — staging vs\n * production, for example. Each project automatically gets a Production\n * environment; users can add staging/development/preview envs to run\n * sibling services side-by-side and promote builds between them.\n */\n\ndefineTool({\n\tname: 'list_environments',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'List every environment for a project (production / staging / development / preview).',\n\t\t'',\n\t\t'When to use: the user wants to see what envs exist before creating a service in one or promoting a deploy. Every project has at least Production.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { items: Environment[] } — id, publicId, name, type, isDefault, isProtected.',\n\t\t'',\n\t\t'Example: list_environments({ project_id: \"prj_abc\" }) → { items: [{ name: \"Production\", type: \"production\", isDefault: true }, { name: \"Staging\", type: \"staging\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.environments.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'environments', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No environments yet — every project should have Production by default.'\n\t\t\t\t: `Found ${data.items.length} environment${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Create a new environment in a project (e.g. Staging, QA, Preview).',\n\t\t'',\n\t\t'When to use: the user wants to add an env so they can run a sibling service alongside production for testing or staging before release.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - name: human-readable name (1–64 chars). Shown in the env switcher.',\n\t\t' - type: \"production\" | \"staging\" | \"development\" | \"preview\". Determines the hostname suffix on services in this env (production stays clean; others get -staging / -dev / -preview).',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env. Default false.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: create_environment({ project_id: \"prj_abc\", name: \"Staging\", type: \"staging\" }) → { environment: { id: 7, publicId: \"env_…\", name: \"Staging\", type: \"staging\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(64).describe('Environment name (1–64 chars).'),\n\t\ttype: z\n\t\t\t.enum(['production', 'staging', 'development', 'preview'])\n\t\t\t.describe('Environment type — drives the hostname suffix.'),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions. Default false.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname: string;\n\t\t\ttype: 'production' | 'staging' | 'development' | 'preview';\n\t\t\tisProtected?: boolean;\n\t\t} = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t};\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tconst response = await ctx.hoststack.environments.create(teamId, args.project_id, input);\n\t\tconst data = { environment: shape(response.environment) };\n\t\treturn respond({\n\t\t\tsummary: `Created environment \"${args.name}\" (${args.type}) in project ${args.project_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Delete an environment from a project.',\n\t\t'',\n\t\t'When to use: an env is no longer used (e.g. tearing down a feature branch staging). Blocked when the env still has live services or databases attached — destroy or move them first. The default env (usually Production) cannot be deleted.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { success: true } on success.',\n\t\t'',\n\t\t'Example: delete_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\" }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.environments.delete(teamId, args.project_id, args.environment_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted environment ${String(args.environment_id)} from project ${args.project_id}.`,\n\t\t\tdata: { success: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'promote_deploy',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Promote a built deploy from one environment to another (build-once, run-many).',\n\t\t'',\n\t\t'When to use: the user has tested a deploy in staging and wants to ship that exact image to production without rebuilding. Atomic and image-based — same docker image runs on the sibling service in the target env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: source service publicId (the one that owns the deploy).',\n\t\t' - deploy_id: source deploy publicId. Must have `dockerImageId` set (i.e. a successful build).',\n\t\t\" - target_environment_id: env publicId or numeric id to promote into. Must be in the same project. Cannot be the source service's own env.\",\n\t\t'',\n\t\t\"Behaviour: if a sibling service with the same name already exists in the target env, the new deploy lands on it. Otherwise the API auto-clones the source service's build/runtime config into the target env first. Env-specific config (env vars, secret files, volumes, IP allowlists, custom domains) is NOT copied — those are per-env by design.\",\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy on the target service.',\n\t\t'',\n\t\t'Example: promote_deploy({ service_id: \"svc_staging\", deploy_id: \"dpl_built\", target_environment_id: \"env_prod\" }) → { deploy: { id: 99, status: \"pending\", trigger: \"rollback\", dockerImageId: \"sha256:…\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Source service publicId.'),\n\t\tdeploy_id: z.string().describe('Source deploy publicId (must have a built image).'),\n\t\ttarget_environment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Target environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Resolve target env to numeric id; the API expects a number.\n\t\tconst targetEnvId = await ctx.hoststack.resolveId(args.target_environment_id, {\n\t\t\tkind: 'environment',\n\t\t\tteamId: await ctx.hoststack.resolveId(teamId, { kind: 'team' }),\n\t\t});\n\t\tconst response = await ctx.hoststack.deploys.promote(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\ttargetEnvId,\n\t\t);\n\t\tconst data = { deploy: shape(response.deploy) };\n\t\treturn respond({\n\t\t\tsummary: `Promoted deploy ${args.deploy_id} from service ${args.service_id} to env ${String(args.target_environment_id)}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeTeam, shapeUser } from '../lib/shape.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineTool({\n\tname: 'get_me',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Return the user and team identity bound to the current API key. Useful at the start of a conversation to confirm which account the agent is operating on, especially when a user has multiple HostStack environments.',\n\t\t'',\n\t\t'When to use: orient yourself at the start of a session, confirm which team the API key targets, or surface the email/team-name in a reply to the user.',\n\t\t'',\n\t\t'Returns: { user: { id, email, name, ... }, team?: { id, publicId, name, plan, ... } }.',\n\t\t'',\n\t\t'Example: get_me() → { user: { id: 1, email: \"ada@…\", … }, team: { id: 7, name: \"acme\", plan: \"pro\" } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst me = await ctx.api.get<MeResponse>('/api/auth/me');\n\t\tconst data: { user: ReturnType<typeof shapeUser>; team?: ReturnType<typeof shapeTeam> } = {\n\t\t\tuser: shapeUser(me.user),\n\t\t};\n\t\tif (me.team) data.team = shapeTeam(me.team);\n\t\tconst teamLabel =\n\t\t\tme.team && typeof me.team === 'object' && 'name' in me.team\n\t\t\t\t? ` on team ${(me.team as { name: string }).name}`\n\t\t\t\t: '';\n\t\tconst userEmail =\n\t\t\tme.user && typeof me.user === 'object' && 'email' in me.user\n\t\t\t\t? (me.user as { email: string }).email\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Authenticated as ${userEmail}${teamLabel}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\n/**\n * Source of truth: packages/shared/src/schemas/notification-channel.ts.\n * Kept in sync manually because the MCP package's Zod schemas are\n * what the model sees — pulling these from `@hoststack/shared` at\n * runtime would add a dependency that the published `@hoststack.dev/mcp`\n * shouldn't carry.\n */\nconst NOTIFICATION_EVENTS = [\n\t'deploy.started',\n\t'deploy.succeeded',\n\t'deploy.failed',\n\t'deploy.failed_consecutive',\n\t'service.created',\n\t'service.deleted',\n\t'service.suspended',\n\t'service.resumed',\n\t'service.restart_failed',\n\t'service.auto_suspended',\n\t'service.acme_cert_failed',\n\t'git.auth_failed',\n] as const;\n\ndefineTool({\n\tname: 'list_notification_channels',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List configured notification channels for the team (Slack/Discord webhooks + email recipients). Each channel has a list of events it subscribes to — fire only happens when an event in that list occurs.',\n\t\t'',\n\t\t\"When to use: setting up alerts ('which channels are notifying us on deploy failures?'), auditing why someone got/didn't get paged, or before subscribing a new event.\",\n\t\t'',\n\t\t'Returns: { items: Channel[] } — each channel includes id, type, name, webhookUrl (masked), active, events, createdAt.',\n\t\t'',\n\t\t\"Example: list_notification_channels() → { items: [{ id: 3, type: 'slack', name: 'eng-alerts', events: ['deploy.failed', 'git.auth_failed'], … }] }\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.listChannels(teamId);\n\t\tconst data = shapeList(response, 'channels', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No notification channels yet — create one with create_notification_channel.'\n\t\t\t\t: `Found ${data.items.length} notification channel${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Create a Slack, Discord, or email notification channel and subscribe it to a list of events.',\n\t\t'',\n\t\t'When to use: setting up alert delivery for a team — paging the on-call when a deploy fails, posting to #incidents when git auth breaks, emailing the owner on ACME renewal failure.',\n\t\t'',\n\t\t'For type=email, `webhook_url` is the recipient email address. For type=slack or type=discord, it is the incoming webhook URL. The URL is stored encrypted server-side and masked in list responses.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"slack\" | \"discord\" | \"email\".',\n\t\t' - name: human-readable label (1–128 chars).',\n\t\t' - webhook_url: Slack/Discord webhook URL OR email address.',\n\t\t' - events: list of event names to subscribe to. Pass an empty list to create a channel that fires for nothing (manual subscribe later with update_notification_channel).',\n\t\t'',\n\t\t'Valid events: deploy.started, deploy.succeeded, deploy.failed, deploy.failed_consecutive, service.created, service.deleted, service.suspended, service.resumed, service.restart_failed, service.auto_suspended, service.acme_cert_failed, git.auth_failed.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: create_notification_channel({ type: 'slack', name: 'eng-alerts', webhook_url: 'https://hooks.slack.com/…', events: ['deploy.failed', 'git.auth_failed', 'service.restart_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\ttype: z.enum(['slack', 'discord', 'email']).describe('Channel type.'),\n\t\tname: z.string().min(1).max(128).describe('Human-readable label.'),\n\t\twebhook_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.describe('Slack/Discord webhook URL or email address (when type=email).'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.describe(\n\t\t\t\t'List of events the channel subscribes to. Empty list = subscribe to nothing.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.createChannel(teamId, {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\twebhookUrl: args.webhook_url,\n\t\t\tevents: args.events,\n\t\t});\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} channel \"${args.name}\" subscribed to ${args.events.length} event${args.events.length === 1 ? '' : 's'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Update a notification channel: rename, enable/disable, or change its event subscription list. The webhook URL and type are immutable — create a new channel if those need to change.',\n\t\t'',\n\t\t'When to use: silencing a noisy channel (active=false), adding a new event to an existing list, or relabeling.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t' - name (optional): new label.',\n\t\t' - active (optional): false to silence, true to re-enable.',\n\t\t' - events (optional): replace the full subscription list. Passing [] silences all events without disabling the channel.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: update_notification_channel({ channel_id: 3, events: ['deploy.failed', 'service.restart_failed', 'git.auth_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric channel id from list_notification_channels.'),\n\t\tname: z.string().min(1).max(128).optional().describe('New label.'),\n\t\tactive: z.boolean().optional().describe('false silences without deleting.'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.optional()\n\t\t\t.describe('Replaces the full subscription list.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst update: { name?: string; active?: boolean; events?: readonly string[] } = {};\n\t\tif (args.name !== undefined) update.name = args.name;\n\t\tif (args.active !== undefined) update.active = args.active;\n\t\tif (args.events !== undefined) update.events = args.events;\n\n\t\tif (Object.keys(update).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\t// Cast: SDK's events type is the typed enum array; passing through\n\t\t// from the Zod-validated args is safe at the type level even if\n\t\t// TS can't bridge the readonly tuple to the SDK signature.\n\t\tconst response = await ctx.hoststack.notifications.updateChannel(\n\t\t\tteamId,\n\t\t\targs.channel_id,\n\t\t\tupdate as Parameters<typeof ctx.hoststack.notifications.updateChannel>[2],\n\t\t);\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Updated notification channel #${args.channel_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Permanently delete a notification channel. Use update_notification_channel({ active: false }) to silence without deleting if you might want it back later.',\n\t\t'',\n\t\t'When to use: a channel is no longer needed (decommissioned Slack workspace, team member left, webhook leaked) and you want it gone from the team config entirely.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_notification_channel({ channel_id: 3 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.notifications.deleteChannel(teamId, args.channel_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted notification channel #${args.channel_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'test_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Fire a synthetic test event to the channel so the user can confirm the webhook is wired up correctly. The webhook receives an unmistakeable \"test message\" payload.',\n\t\t'',\n\t\t\"When to use: after creating a channel, or when alerts stop arriving — quickest way to tell whether the issue is on HostStack's side or the channel's.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { success: boolean, error?: string } — false + error string when the webhook returned non-2xx.',\n\t\t'',\n\t\t'Example: test_notification_channel({ channel_id: 3 }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.testChannel(teamId, args.channel_id);\n\t\tconst data = shape(response);\n\t\tconst okFlag =\n\t\t\tdata && typeof data === 'object' && 'success' in data\n\t\t\t\t? (data as { success: boolean }).success\n\t\t\t\t: false;\n\t\treturn respond({\n\t\t\tsummary: okFlag\n\t\t\t\t? `Test event delivered to channel #${args.channel_id}.`\n\t\t\t\t: `Test event FAILED for channel #${args.channel_id}: ${(data as { error?: string }).error ?? 'unknown error'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeList, shapeProject } from '../lib/shape.js';\n\n// HostStack region IDs (matches @hoststack/shared REGION_IDS and the SDK's\n// internal RegionId). The SDK narrows region to this union but doesn't export\n// the type name, so we mirror the literals here — structurally identical, so\n// it stays assignable to the SDK's create-project param.\nconst REGION_IDS = ['eu-central-1', 'eu-central-2', 'eu-west-1', 'us-east-1'] as const;\ntype RegionId = (typeof REGION_IDS)[number];\n\ndefineTool({\n\tname: 'list_projects',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'List every project in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs an overview of what projects exist before drilling into services, deploys, or databases. Use this as the first step when the user mentions a project by name and you need to resolve its ID.',\n\t\t'',\n\t\t'Returns: { items: Project[] } — each project includes id, publicId, name, description, createdAt.',\n\t\t'',\n\t\t'Example: list_projects() → { items: [{ id: 12, publicId: \"prj_…\", name: \"billing-api\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.list(teamId);\n\t\tconst data = shapeList(response, 'projects', shapeProject);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No projects yet — create one in the dashboard or via create_project.'\n\t\t\t\t: `Found ${data.items.length} project${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Create a new project (logical grouping of services + databases).',\n\t\t'',\n\t\t\"When to use: the user wants to set up a new app or environment in HostStack. Projects are a free organisational layer — they don't cost anything until you add services or databases inside them.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name: human-readable project name (1–60 chars).',\n\t\t' - description (optional): short blurb shown in the dashboard.',\n\t\t' - region (optional): \"eu-central-1\" (Falkenstein) | \"eu-central-2\" (Nuremberg) | \"eu-west-1\" (Helsinki) | \"us-east-1\" (Ashburn). Defaults to eu-central-2.',\n\t\t'',\n\t\t'Returns: { project: Project } — includes the new id and publicId.',\n\t\t'',\n\t\t'Example: create_project({ name: \"billing-api\", description: \"Stripe webhooks\", region: \"eu-central-1\" }) → { project: { id: 12, publicId: \"prj_…\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z.string().min(1).max(60).describe('Project name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('Short description (≤500 chars).'),\n\t\tregion: z\n\t\t\t.enum(REGION_IDS)\n\t\t\t.optional()\n\t\t\t.describe('Region: eu-central-1 | eu-central-2 | eu-west-1 | us-east-1.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; description?: string; region?: RegionId } = {\n\t\t\tname: args.name,\n\t\t};\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tif (args.region !== undefined) input.region = args.region;\n\t\tconst response = await ctx.hoststack.projects.create(teamId, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst publicId =\n\t\t\tdata.project && 'publicId' in data.project\n\t\t\t\t? (data.project as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created project \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Rename a project or update its description.',\n\t\t'',\n\t\t'When to use: the user wants to fix a typo in a project name, attach a clearer description, or align the dashboard label with their internal naming.',\n\t\t'',\n\t\t'Inputs (all optional, at least one required):',\n\t\t' - project_id: publicId of the project (required).',\n\t\t' - name: new name (1–60 chars).',\n\t\t' - description: new description (≤500 chars).',\n\t\t'',\n\t\t'Returns: { project: Project } — the updated record.',\n\t\t'',\n\t\t'Example: update_project({ project_id: \"prj_abc\", name: \"billing-prod\" }) → { project: { name: \"billing-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(60).optional().describe('New name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('New description (≤500 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif (args.name === undefined && args.description === undefined) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No fields to update — pass `name` and/or `description`.',\n\t\t\t\tdata: {},\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; description?: string } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tconst response = await ctx.hoststack.projects.update(teamId, args.project_id, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\treturn respond({ summary: `Updated project ${args.project_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Fetch a single project by ID. Includes name, description, and timestamps.',\n\t\t'',\n\t\t'When to use: confirming a project exists before creating resources inside it, or pulling the canonical name/description for a reply to the user.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: publicId of the project (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { project: Project }.',\n\t\t'',\n\t\t'Example: get_project({ project_id: \"prj_abc\" }) → { project: { id: 12, name: \"billing\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId (e.g. prj_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.get(teamId, args.project_id);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst name =\n\t\t\tdata.project && 'name' in data.project\n\t\t\t\t? (data.project as { name: string }).name\n\t\t\t\t: args.project_id;\n\t\treturn respond({ summary: `Project \"${name}\".`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeService } from '../lib/shape.js';\n\n// Keep in sync with the `dev-environment` preset in\n// packages/shared/src/templates.ts (the dashboard wizard's source of truth).\n// The MCP package only depends on the SDK, so these constants are duplicated\n// here intentionally rather than imported from @hoststack/shared.\nconst DEV_ENV_IMAGE = 'hoststack/dev-env:latest';\nconst DEV_ENV_VOLUME = { name: 'workspace', mountPath: '/workspace', sizeGb: 10 };\n\nconst SERVICE_TYPES = [\n\t'web_service',\n\t'private_service',\n\t'worker',\n\t'cron_job',\n\t'static_site',\n] as const;\nconst SERVICE_PLANS = [\n\t'pico',\n\t'nano',\n\t'micro',\n\t'starter',\n\t'standard',\n\t'pro_standard',\n\t'pro_large',\n] as const;\n\ndefineTool({\n\tname: 'list_services',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List services (web, worker, cron, private, static) in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs to find a service by name, check what is deployed, or pick a target for a follow-up tool (logs, deploys, env vars). This is the canonical way to resolve a publicId from a human-friendly name.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - project_id: narrow to one project (numeric id or publicId \"prj_…\").',\n\t\t' - environment_id: narrow to one environment (numeric id or publicId \"env_…\").',\n\t\t' - status: \"active\" | \"deploying\" | \"suspended\" | \"failed\" | \"not_deployed\".',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t'',\n\t\t'Returns: { items: Service[] } — each service includes id, publicId, name, type, status, projectId, repoUrl, branch, runtime, createdAt.',\n\t\t'',\n\t\t'Example: list_services({ status: \"failed\" }) → only services that need attention.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Project filter — numeric id or publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment filter — numeric id or publicId.'),\n\t\tstatus: z\n\t\t\t.enum(['active', 'deploying', 'suspended', 'failed', 'not_deployed'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by current runtime status.'),\n\t\ttype: z\n\t\t\t.enum(['web_service', 'private_service', 'worker', 'cron_job', 'static_site'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by service type.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst filters: Parameters<typeof ctx.hoststack.services.list>[1] = {};\n\t\tif (args.project_id !== undefined) {\n\t\t\t// SDK accepts publicId or numeric id; let resolveId handle it.\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\t\tkind: 'project',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.projectId = resolved;\n\t\t}\n\t\tif (args.environment_id !== undefined) {\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.environmentId = resolved;\n\t\t}\n\t\tif (args.status) filters.status = args.status;\n\t\tif (args.type) filters.type = args.type;\n\n\t\tconst response = await ctx.hoststack.services.list(teamId, filters);\n\t\tconst data = shapeList(response, 'services', shapeService);\n\t\tconst filterDesc = [\n\t\t\targs.project_id !== undefined ? `project=${args.project_id}` : null,\n\t\t\targs.environment_id !== undefined ? `env=${args.environment_id}` : null,\n\t\t\targs.status ? `status=${args.status}` : null,\n\t\t\targs.type ? `type=${args.type}` : null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? filterDesc\n\t\t\t\t\t? `No services match the given filters (${filterDesc}).`\n\t\t\t\t\t: 'No services yet — create one with create_service (or create_dev_environment for an AI dev box), or via the dashboard.'\n\t\t\t\t: `Found ${data.items.length} service${data.items.length === 1 ? '' : 's'}${filterDesc ? ` matching ${filterDesc}` : ''}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Create a new service in a project. Source is either a connected git repo (github_repo_id) OR a pre-built docker_image — never both.',\n\t\t'',\n\t\t'When to use: the user wants to deploy something new. For a one-command AI dev environment specifically, prefer create_dev_environment (it also attaches the /workspace volume and sets the MCP keys).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: service name (1–100 chars).',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - docker_image (optional): pre-built image ref to deploy instead of building from source.',\n\t\t' - github_repo_id (optional): connect a previously-linked GitHub repo by numeric id.',\n\t\t' - branch (optional): git branch (default \"main\").',\n\t\t' - install_command / build_command / start_command (optional): build/run shell commands. start_command is required for web/private services without a docker_image.',\n\t\t' - cron_schedule (optional): cron expression — required for cron_job.',\n\t\t' - publish_path (optional): static-site output dir (e.g. \"dist\").',\n\t\t' - runtime (optional): \"node\" | \"bun\" | \"python\" | … (auto-detected from a repo when omitted).',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - auto_deploy (optional, default true): trigger the first deploy immediately when a source is present.',\n\t\t'',\n\t\t'Returns: { service: Service, deployId: number | null }.',\n\t\t'',\n\t\t'Example: create_service({ project_id: \"prj_abc\", name: \"api\", type: \"web_service\", github_repo_id: 42 }) → { service: { publicId: \"svc_…\" }, deployId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z.string().min(1).max(100).describe('Service name (1–100 chars).'),\n\t\ttype: z.enum(SERVICE_TYPES).describe('Service type.'),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Pre-built image ref. Mutually exclusive with github_repo_id.'),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe('Linked GitHub repo numeric id. Mutually exclusive with docker_image.'),\n\t\tbranch: z.string().max(200).optional().describe('Git branch (default \"main\").'),\n\t\tinstall_command: z.string().max(1000).optional().describe('Install shell command.'),\n\t\tbuild_command: z.string().max(1000).optional().describe('Build shell command.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Start shell command (required for web/private services without an image).'),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Cron expression — required for cron_job.'),\n\t\tpublish_path: z.string().max(500).optional().describe('Static-site output dir.'),\n\t\truntime: z.string().max(50).optional().describe('Runtime hint (node/bun/python/…).'),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Bind to a specific environment; defaults to Production.'),\n\t\tauto_deploy: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Trigger the first deploy immediately (default true).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\n\t\tconst input: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.docker_image !== undefined) input.dockerImage = args.docker_image;\n\t\tif (args.github_repo_id !== undefined) input.githubRepoId = args.github_repo_id;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tif (args.install_command !== undefined) input.installCommand = args.install_command;\n\t\tif (args.build_command !== undefined) input.buildCommand = args.build_command;\n\t\tif (args.start_command !== undefined) input.startCommand = args.start_command;\n\t\tif (args.cron_schedule !== undefined) input.cronSchedule = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) input.publishPath = args.publish_path;\n\t\tif (args.runtime !== undefined) input.runtime = args.runtime;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.auto_deploy !== undefined) input.autoDeploy = args.auto_deploy;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\n\t\tconst response = await ctx.hoststack.services.create(teamId, input);\n\t\tconst data = {\n\t\t\tservice: shapeService(response.service),\n\t\t\tdeployId: (response as { deployId?: number | null }).deployId ?? null,\n\t\t};\n\t\tconst publicId =\n\t\t\tdata.service && 'publicId' in data.service\n\t\t\t\t? (data.service as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created service \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an AI dev environment in one call: a private service from the agentic dev-env image (Claude Code + Codex + OpenCode + MCPs preinstalled), with a persistent /workspace volume and the MCP API keys set, then fire the first deploy.',\n\t\t'',\n\t\t'When to use: the user wants a cloud terminal / dev box they can drive coding agents in (from a desk or a phone). This mirrors the dashboard\\'s \"AI Dev Environment\" wizard preset — create (deploy deferred) → set keys → attach /workspace → deploy, so the first container boots with its volume and keys already in place.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name (optional): service name (default \"dev-environment\").',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - disk_gb (optional): /workspace volume size in GB (default 10, 1–100).',\n\t\t' - hoststack_api_key (optional): sets HOSTSTACK_API_KEY so the hoststack MCP works inside the container.',\n\t\t' - poststack_api_key (optional): sets POSTSTACK_API_KEY so the poststack MCP works inside the container.',\n\t\t'',\n\t\t'Returns: { service: Service, volumeAttached: boolean, deployId: number | null }. Open the Terminal tab on the service (dashboard or phone) once it is running; log in once with `claude /login` inside the container.',\n\t\t'',\n\t\t'Example: create_dev_environment({ project_id: \"prj_abc\", name: \"scratch\", hoststack_api_key: \"hs_live_…\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Service name (default \"dev-environment\").'),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tdisk_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('/workspace volume size in GB (default 10).'),\n\t\thoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for HOSTSTACK_API_KEY (enables the hoststack MCP in-container).'),\n\t\tpoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for POSTSTACK_API_KEY (enables the poststack MCP in-container).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst name = args.name ?? 'dev-environment';\n\t\tconst sizeGb = args.disk_gb ?? DEV_ENV_VOLUME.sizeGb;\n\n\t\t// Defer the first deploy (autoDeploy:false) so we can set the MCP keys\n\t\t// and attach /workspace BEFORE the container boots — same ordering as\n\t\t// the dashboard wizard's use-new-service-form.\n\t\tconst createInput: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname,\n\t\t\ttype: 'private_service',\n\t\t\tprojectId,\n\t\t\tdockerImage: DEV_ENV_IMAGE,\n\t\t\tautoDeploy: false,\n\t\t};\n\t\tif (args.plan !== undefined) createInput.plan = args.plan;\n\t\tconst created = await ctx.hoststack.services.create(teamId, createInput);\n\t\tconst service = created.service;\n\n\t\tconst envVars: { key: string; value: string; isSecret: boolean }[] = [];\n\t\tif (args.hoststack_api_key)\n\t\t\tenvVars.push({\n\t\t\t\tkey: 'HOSTSTACK_API_KEY',\n\t\t\t\tvalue: args.hoststack_api_key,\n\t\t\t\tisSecret: true,\n\t\t\t});\n\t\tif (args.poststack_api_key)\n\t\t\tenvVars.push({\n\t\t\t\tkey: 'POSTSTACK_API_KEY',\n\t\t\t\tvalue: args.poststack_api_key,\n\t\t\t\tisSecret: true,\n\t\t\t});\n\t\tif (envVars.length > 0) {\n\t\t\tawait ctx.hoststack.envVars.bulkSet(teamId, service.id, { vars: envVars });\n\t\t}\n\n\t\tlet volumeAttached = false;\n\t\ttry {\n\t\t\tawait ctx.hoststack.volumes.create(teamId, service.id, {\n\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\tsizeGb,\n\t\t\t});\n\t\t\tvolumeAttached = true;\n\t\t} catch {\n\t\t\t// Surface but don't abort — the env still boots, just without the\n\t\t\t// persistent workspace. Caller sees volumeAttached:false.\n\t\t}\n\n\t\tconst deploy = await ctx.hoststack.deploys.trigger(teamId, service.id);\n\t\tconst deployId = deploy.deploy?.id ?? null;\n\n\t\treturn respond({\n\t\t\tsummary: `Created AI dev environment \"${name}\" (${service.publicId})${volumeAttached ? ' with a /workspace volume' : ''} — deploying. Open it in the dashboard's Development section (or the service's Terminal tab) once running.`,\n\t\t\tdata: { service: shapeService(service), volumeAttached, deployId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'spin_up_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an agentic dev environment FROM an existing service so you can reproduce a bug, fix it, view it, and ship it. Creates a dev box (Claude/Codex/OpenCode + MCPs) that RUNS a clone of the app: the repo is auto-cloned into /workspace, the service env-vars are copied, and its linked database is cloned (so the box never touches the prod DB). The box gets an unguessable public dev URL and seamless `git push`.',\n\t\t'',\n\t\t'When to use: \"spin up a dev environment for <service>\", \"I found a bug on <service>, give me a box to fix it\". Distinct from create_dev_environment, which makes a BARE box not tied to any app.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the source service to debug — numeric id or publicId (\"svc_…\").',\n\t\t' - include_database_clone (optional): clone the linked database so the app runs on a copy of real data (default true).',\n\t\t' - name (optional): dev box name (default \"<source>-dev\").',\n\t\t'',\n\t\t'Returns: { service, devUrl, deployId }. Once it is live: open the Terminal tab, run `claude`, start the dev server (it must bind $PORT), and view the app at https://<devUrl>. Tear it all down later with delete_dev_environment.',\n\t\t'',\n\t\t'Example: spin_up_dev_environment({ service_id: \"svc_api\" }) → a dev box running a clone of the api service (repo + env-vars + cloned DB) with a public dev URL.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Source service to debug — numeric id or publicId.'),\n\t\tinclude_database_clone: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Clone the linked database so the app runs on copied data (default true).'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Dev box name (default \"<source>-dev\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tconst opts: { includeDatabaseClone?: boolean; name?: string } = {};\n\t\tif (args.include_database_clone !== undefined)\n\t\t\topts.includeDatabaseClone = args.include_database_clone;\n\t\tif (args.name !== undefined) opts.name = args.name;\n\t\tconst result = await ctx.hoststack.services.spinUpDevEnvironment(teamId, serviceId, opts);\n\t\treturn respond({\n\t\t\tsummary: `Spun up dev environment \"${result.service.name}\" (${result.service.publicId}) — deploying. Once live: open the Terminal tab, run \\`claude\\`, start the dev server on $PORT, and view it at https://${result.devUrl}. \\`git push\\` works from inside.`,\n\t\t\tdata: {\n\t\t\t\tservice: shapeService(result.service),\n\t\t\t\tdevUrl: result.devUrl,\n\t\t\t\tdeployId: result.deployId,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Tear down an agentic dev environment in one call: removes the dev box AND cascade-deletes its cloned database, its /workspace volume, and the auto-created `development` environment if it is now empty.',\n\t\t'',\n\t\t'When to use: \"delete / tear down the dev environment\", once you have shipped the fix and no longer need the box.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the dev box to tear down — numeric id or publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }. Irreversible — the cloned database and workspace volume are deleted.',\n\t\t'',\n\t\t'Example: delete_dev_environment({ service_id: \"svc_api_dev\" }) → removes the dev box, its cloned database, and the /workspace volume.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('The dev box to tear down — numeric id or publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tawait ctx.hoststack.services.tearDownDevEnvironment(teamId, serviceId);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t'Dev environment torn down — box, cloned database, /workspace volume, and empty development environment removed.',\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a single service by ID with its current status AND its service_config row (resources, health-check tuning, scaling, restart policy).',\n\t\t'',\n\t\t'When to use: drilling into a service after list_services, checking deploy/runtime status, grabbing the repo+branch before triggering a deploy, or inspecting the health-check / autoscale knobs before tweaking them via update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\").',\n\t\t'',\n\t\t'Returns: { service: Service, config: ServiceConfig } — service has type/status/runtime/repoUrl/branch/autoDeploy/region/plan/timestamps; config has memoryMb, cpuShares, diskSizeGb, port, protocol, healthCheckEnabled, healthCheckInterval, healthCheckTimeout, healthCheckGracePeriodSec, restartPolicy, preDeployCommand, min/maxInstances, scale thresholds.',\n\t\t'',\n\t\t'Example: get_service({ service_id: \"svc_abc\" }) → { service: { type: \"web\", status: \"running\", … }, config: { healthCheckGracePeriodSec: 120, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Bundle service + config so a single call answers both \"how is it\n\t\t// running?\" and \"how is it tuned?\". Previously callers had to follow\n\t\t// up with a separate read for any of the health-check / scaling /\n\t\t// resource knobs, which lived only on the service_config row.\n\t\tconst [serviceResponse, configResponse] = await Promise.all([\n\t\t\tctx.hoststack.services.get(teamId, args.service_id),\n\t\t\tctx.hoststack.services.getConfig(teamId, args.service_id),\n\t\t]);\n\t\tconst data = {\n\t\t\tservice: shapeService(serviceResponse.service),\n\t\t\tconfig: shape(configResponse.config),\n\t\t};\n\t\tconst status =\n\t\t\tdata.service && 'status' in data.service\n\t\t\t\t? (data.service as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Service ${args.service_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch the latest CPU, memory, network, and request-rate metrics for a service.',\n\t\t'',\n\t\t'When to use: a service is reportedly slow, you suspect resource pressure, or you want a quick health snapshot before scaling. Returns a single point-in-time sample. For a time series — \"is memory growing?\", \"did CPU spike during that deploy?\" — use get_service_metrics_history instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { metrics: { cpu, memory, network, requests } } — values are normalised utilisation/throughput numbers.',\n\t\t'',\n\t\t'Example: get_service_metrics({ service_id: \"svc_abc\" }) → { metrics: { cpu: 0.42, memory: 0.71, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.getMetrics(teamId, args.service_id);\n\t\tconst data = { metrics: shape(response.metrics) };\n\t\treturn respond({ summary: `Metrics snapshot for service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics_history',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a metrics time series (CPU, memory, network, disk) for a service.',\n\t\t'',\n\t\t'When to use: answering \"is memory growing over the last hour?\", \"did CPU spike during deploy X?\", or \"what does normal look like for this service?\". Pairs well with get_deploy to align spikes against deploy times.',\n\t\t'',\n\t\t'Inputs (all optional — omit both for the trailing hour):',\n\t\t' - service_id: publicId of the service.',\n\t\t' - from: ISO-8601 lower bound OR relative offset like \"-15m\" / \"-2h\" / \"-7d\".',\n\t\t' - to: ISO-8601 upper bound (or relative offset). Defaults to now.',\n\t\t'',\n\t\t'Resolution: ≤7d → raw samples (~minute granularity), ≤30d → hourly pre-aggregates, >30d → daily. Up to ~500 points returned.',\n\t\t'',\n\t\t'Returns: { history: Array<{ timestamp, cpuPercent, memoryUsedMb, memoryLimitMb, networkRxBytes, networkTxBytes, diskUsedMb }> }.',\n\t\t'',\n\t\t'Example: get_service_metrics_history({ service_id: \"svc_abc\", from: \"-1h\" }) → 60-ish points for the last hour.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tfrom: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 lower bound or relative offset (e.g. \"-1h\", \"-2d\").'),\n\t\tto: z.string().optional().describe('ISO-8601 upper bound; defaults to now.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Translate the same `-Nm/h/d` relative shorthand we accept in logs\n\t\t// so callers don't have to learn a second convention. The metrics\n\t\t// history endpoint expects ISO timestamps server-side; we resolve\n\t\t// relative offsets here for symmetry with get_service_logs.\n\t\tconst opts: { from?: string; to?: string } = {};\n\t\tconst resolveRel = (raw: string): string => {\n\t\t\tconst rel = /^-(\\d+)(s|m|h|d)$/.exec(raw.trim());\n\t\t\tif (!rel) return raw;\n\t\t\tconst amount = Number.parseInt(rel[1] as string, 10);\n\t\t\tconst unit = rel[2] as 's' | 'm' | 'h' | 'd';\n\t\t\tconst ms =\n\t\t\t\tunit === 's'\n\t\t\t\t\t? amount * 1000\n\t\t\t\t\t: unit === 'm'\n\t\t\t\t\t\t? amount * 60_000\n\t\t\t\t\t\t: unit === 'h'\n\t\t\t\t\t\t\t? amount * 3_600_000\n\t\t\t\t\t\t\t: amount * 86_400_000;\n\t\t\treturn new Date(Date.now() - ms).toISOString();\n\t\t};\n\t\tif (args.from) opts.from = resolveRel(args.from);\n\t\tif (args.to) opts.to = resolveRel(args.to);\n\n\t\tconst response = await ctx.hoststack.services.getMetricsHistory(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\topts,\n\t\t);\n\t\tconst points = Array.isArray(response.history) ? response.history.length : 0;\n\t\treturn respond({\n\t\t\tsummary: `Returned ${points} metric point${points === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { history: response.history },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Rename a service. Only the human-readable name changes — the publicId is permanent.',\n\t\t'',\n\t\t'When to use: the user wants to relabel a service in the dashboard. For deeper config edits (build command, branch, scale, plan), use update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - name: new name (1–60 chars).',\n\t\t'',\n\t\t'Returns: { service: Service }.',\n\t\t'',\n\t\t'Example: update_service({ service_id: \"svc_abc\", name: \"api-prod\" }) → { service: { name: \"api-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z.string().min(1).max(60).describe('New service name (1–60 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.update(teamId, args.service_id, {\n\t\t\tname: args.name,\n\t\t});\n\t\tconst data = { service: shapeService(response.service) };\n\t\treturn respond({ summary: `Renamed service ${args.service_id} to \"${args.name}\".`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_service_config',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Update build/runtime configuration for a service. All fields optional — pass only what you want to change.',\n\t\t'',\n\t\t'When to use: the user wants to tweak how a service builds, runs, scales, or health-checks. Build/runtime fields (branch, install/build/start command, root, dockerfile) take effect on the next deploy — call trigger_deploy after if you need them applied immediately. Instance_count, resource and health-check changes rescale or rewire without a redeploy.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - install_command, build_command, start_command (optional): shell commands. Pass null to clear.',\n\t\t' - branch (optional): git branch to track.',\n\t\t' - root_directory (optional): build context root inside the repo.',\n\t\t' - dockerfile_path (optional): path to Dockerfile relative to root_directory. Pass null to clear.',\n\t\t' - auto_deploy (optional): boolean — auto-deploy on git push.',\n\t\t' - health_check_path (optional): HTTP path the platform GETs to verify liveness (e.g. \"/health\"). Pass null for TCP-only check.',\n\t\t' - health_check_enabled (optional): boolean — toggle health checking on/off.',\n\t\t' - health_check_interval (optional): integer 5–300 seconds — how often the check runs.',\n\t\t' - health_check_timeout (optional): integer 1–60 seconds — single-attempt timeout.',\n\t\t' - health_check_grace_period_sec (optional): integer 1–1800 seconds — startup tolerance before failures count. RAISE THIS (e.g. 180) when the agent reports \"Health check timed out\" on a cold-boot app (Bun + Vite SSR typically need 90–180s).',\n\t\t' - memory_mb (optional): integer 128–16384 — container memory cap.',\n\t\t' - cpu_shares (optional): integer 128–4096 — relative CPU weight.',\n\t\t' - disk_size_gb (optional): integer 1–100 — ephemeral disk cap.',\n\t\t' - port (optional): integer 1–65535 — container port the platform forwards traffic to.',\n\t\t' - protocol (optional): \"http\" | \"tcp\".',\n\t\t' - restart_policy (optional): \"always\" | \"on-failure\" | \"no\".',\n\t\t' - pre_deploy_command (optional): shell command run before the new release accepts traffic (typical use: migrations).',\n\t\t' - instance_count (optional): integer 1–50 — pin both min and max instances to this value.',\n\t\t' - min_instances, max_instances (optional): integers — autoscale bounds. Use instead of instance_count when you want a range.',\n\t\t' - scale_cpu_threshold, scale_memory_threshold (optional): integer 10–100 — autoscale trigger percentage.',\n\t\t' - log_filter_rules (optional): list of { pattern, action } rules applied to runtime logs at query time. Pattern matches the message by case-insensitive substring; action is \"drop\" (filter out) or \"downgrade\" (flip stderr → stdout so it stops looking like an error). Pass [] to clear all rules. Capped at 50 rules.',\n\t\t'',\n\t\t'Returns: { service?: Service, config?: ServiceConfig } — whichever rows were touched.',\n\t\t'',\n\t\t'Example: update_service_config({ service_id: \"svc_abc\", health_check_grace_period_sec: 180 }) → { config: { healthCheckGracePeriodSec: 180, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tinstall_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Install shell command. Null clears.'),\n\t\tbuild_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Build shell command. Null clears.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Start shell command. Null clears.'),\n\t\tbranch: z.string().optional().describe('Git branch to track.'),\n\t\troot_directory: z.string().optional().describe('Build context root.'),\n\t\tdockerfile_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Path to Dockerfile relative to root. Null clears.'),\n\t\tauto_deploy: z.boolean().optional().describe('Auto-deploy on push.'),\n\t\thealth_check_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('HTTP health-check path (e.g. \"/health\"). Null = TCP-only check.'),\n\t\thealth_check_enabled: z.boolean().optional().describe('Toggle health checking on/off.'),\n\t\thealth_check_interval: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(5)\n\t\t\t.max(300)\n\t\t\t.optional()\n\t\t\t.describe('How often the check runs, in seconds (5–300).'),\n\t\thealth_check_timeout: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(60)\n\t\t\t.optional()\n\t\t\t.describe('Single-attempt timeout in seconds (1–60).'),\n\t\thealth_check_grace_period_sec: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1800)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Startup grace period in seconds (1–1800). Raise this if the app needs more time to boot before health checks start counting failures.',\n\t\t\t),\n\t\tmemory_mb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(16384)\n\t\t\t.optional()\n\t\t\t.describe('Container memory cap in MB (128–16384).'),\n\t\tcpu_shares: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(4096)\n\t\t\t.optional()\n\t\t\t.describe('Relative CPU weight (128–4096).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Ephemeral disk size in GB (1–100).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe('Container port the platform forwards traffic to.'),\n\t\tprotocol: z.enum(['http', 'tcp']).optional().describe('Traffic protocol.'),\n\t\trestart_policy: z\n\t\t\t.enum(['always', 'on-failure', 'no'])\n\t\t\t.optional()\n\t\t\t.describe('Docker restart policy.'),\n\t\tpre_deploy_command: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Shell command run before the new release accepts traffic.'),\n\t\tinstance_count: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Pin min and max instances to this value (1–50).'),\n\t\tmin_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale lower bound. Use with max_instances for a range.'),\n\t\tmax_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale upper bound. Use with min_instances for a range.'),\n\t\tscale_cpu_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale CPU trigger percentage (10–100).'),\n\t\tscale_memory_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale memory trigger percentage (10–100).'),\n\t\tlog_filter_rules: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpattern: z.string().min(1).max(200),\n\t\t\t\t\taction: z.enum(['drop', 'downgrade']),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Runtime-log filter rules. Empty array [] clears all rules. Each pattern is case-insensitive substring match against the message.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\n\t\t// services-row fields (PATCH /services/:tid/:sid). Note: nullable\n\t\t// fields take `null` to clear, distinct from `undefined` which means\n\t\t// \"leave unchanged\" — passing `null` through to the underlying API\n\t\t// is what flips the column to NULL on the row.\n\t\tconst serviceUpdate: Record<string, unknown> = {};\n\t\tif (args.install_command !== undefined)\n\t\t\tserviceUpdate['installCommand'] = args.install_command;\n\t\tif (args.build_command !== undefined) serviceUpdate['buildCommand'] = args.build_command;\n\t\tif (args.start_command !== undefined) serviceUpdate['startCommand'] = args.start_command;\n\t\tif (args.dockerfile_path !== undefined)\n\t\t\tserviceUpdate['dockerfilePath'] = args.dockerfile_path;\n\t\tif (args.branch !== undefined) serviceUpdate['branch'] = args.branch;\n\t\tif (args.root_directory !== undefined) serviceUpdate['rootDirectory'] = args.root_directory;\n\t\tif (args.auto_deploy !== undefined) serviceUpdate['autoDeploy'] = args.auto_deploy;\n\t\tif (args.health_check_path !== undefined)\n\t\t\tserviceUpdate['healthCheckPath'] = args.health_check_path;\n\n\t\t// service_config-row fields (PATCH /services/:tid/:sid/config). The\n\t\t// schema expects min_instances/max_instances; \"pin to N\" means setting\n\t\t// both to the same value.\n\t\tconst configUpdate: Record<string, unknown> = {};\n\t\tif (args.health_check_enabled !== undefined)\n\t\t\tconfigUpdate['healthCheckEnabled'] = args.health_check_enabled;\n\t\tif (args.health_check_interval !== undefined)\n\t\t\tconfigUpdate['healthCheckInterval'] = args.health_check_interval;\n\t\tif (args.health_check_timeout !== undefined)\n\t\t\tconfigUpdate['healthCheckTimeout'] = args.health_check_timeout;\n\t\tif (args.health_check_grace_period_sec !== undefined)\n\t\t\tconfigUpdate['healthCheckGracePeriodSec'] = args.health_check_grace_period_sec;\n\t\tif (args.memory_mb !== undefined) configUpdate['memoryMb'] = args.memory_mb;\n\t\tif (args.cpu_shares !== undefined) configUpdate['cpuShares'] = args.cpu_shares;\n\t\tif (args.disk_size_gb !== undefined) configUpdate['diskSizeGb'] = args.disk_size_gb;\n\t\tif (args.port !== undefined) configUpdate['port'] = args.port;\n\t\tif (args.protocol !== undefined) configUpdate['protocol'] = args.protocol;\n\t\tif (args.restart_policy !== undefined) configUpdate['restartPolicy'] = args.restart_policy;\n\t\tif (args.pre_deploy_command !== undefined)\n\t\t\tconfigUpdate['preDeployCommand'] = args.pre_deploy_command;\n\t\tif (args.instance_count !== undefined) {\n\t\t\tconfigUpdate['minInstances'] = args.instance_count;\n\t\t\tconfigUpdate['maxInstances'] = args.instance_count;\n\t\t}\n\t\tif (args.min_instances !== undefined) configUpdate['minInstances'] = args.min_instances;\n\t\tif (args.max_instances !== undefined) configUpdate['maxInstances'] = args.max_instances;\n\t\tif (args.scale_cpu_threshold !== undefined)\n\t\t\tconfigUpdate['scaleCpuThreshold'] = args.scale_cpu_threshold;\n\t\tif (args.scale_memory_threshold !== undefined)\n\t\t\tconfigUpdate['scaleMemoryThreshold'] = args.scale_memory_threshold;\n\t\tif (args.log_filter_rules !== undefined) {\n\t\t\t// Empty array is a meaningful value (clears all rules);\n\t\t\t// undefined leaves rules unchanged. Don't short-circuit.\n\t\t\tconfigUpdate['logFilterRules'] = args.log_filter_rules;\n\t\t}\n\n\t\tif (Object.keys(serviceUpdate).length === 0 && Object.keys(configUpdate).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\n\t\tconst data: Record<string, unknown> = {};\n\t\tconst touchedFields: string[] = [];\n\t\tif (Object.keys(serviceUpdate).length > 0) {\n\t\t\tconst serviceResponse = await ctx.hoststack.services.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tserviceUpdate,\n\t\t\t);\n\t\t\tdata['service'] = shape(serviceResponse.service);\n\t\t\ttouchedFields.push(...Object.keys(serviceUpdate));\n\t\t}\n\t\tif (Object.keys(configUpdate).length > 0) {\n\t\t\tconst configResponse = await ctx.hoststack.services.updateConfig(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tconfigUpdate,\n\t\t\t);\n\t\t\tdata['config'] = shape(configResponse.config);\n\t\t\ttouchedFields.push(...Object.keys(configUpdate));\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary: `Updated ${touchedFields.join(', ')} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Suspend a service: stop all running instances and pause auto-deploys. The service stays configured and can be resumed later with resume_service.',\n\t\t'',\n\t\t'When to use: the user wants to temporarily stop traffic and billing without deleting the service (for example: dev environment overnight, debugging).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.suspend(teamId, args.service_id);\n\t\treturn respond({ summary: `Suspended service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'resume_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resume a previously suspended service: start instances back up and re-enable auto-deploys.',\n\t\t'',\n\t\t'When to use: the user wants to bring a suspended service back online. Pair with suspend_service.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: resume_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.resume(teamId, args.service_id);\n\t\treturn respond({ summary: `Resumed service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs',\n\tcategory: 'logs',\n\tdescription: [\n\t\t\"Fetch a snapshot of the running container's recent runtime logs (stdout/stderr). For *deploy* logs (build output), use get_deploy_logs instead.\",\n\t\t'',\n\t\t'When to use: a service is misbehaving in production and you need to read what it is currently logging. The tool returns a snapshot — there is no streaming over MCP. Re-call to get newer entries.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - lines (optional): tail size (default 200, max 1000).',\n\t\t' - since/until (optional): ISO-8601 timestamp OR a relative offset like \"-5m\", \"-1h\", \"-2d\".',\n\t\t' - stream (optional): \"stdout\" | \"stderr\". Omit to combine.',\n\t\t' - level (optional): real log level — trace/debug/info/warn/error/fatal. For structured JSON logs (pino, bunyan, OpenTelemetry severity) this filters on the parsed inner level field. For plain text logs it falls back to a stream-alias hint (info/debug → stdout, warn/error/fatal → stderr).',\n\t\t' - search (optional): case-insensitive substring grep, ≤100 chars.',\n\t\t' - count_only (optional): when true, returns { count } only — much cheaper for \"how many error lines in last 5m\" polling.',\n\t\t'',\n\t\t'Returns: { logs: LogEntry[] | string } when count_only is false. Each entry has { timestamp, level?, stream, message }. `level` is the parsed inner level when the message is a structured JSON envelope (pino numeric or string), and undefined for plain-text logs. `stream` is always one of stdout/stderr. Or { count: number } when count_only is true.',\n\t\t'',\n\t\t'Example: get_service_logs({ service_id: \"svc_abc\", search: \"OOM\", since: \"-15m\" }) → { logs: [...] }.',\n\t\t'',\n\t\t'More examples:',\n\t\t' - Last 50 stderr lines from the past hour: get_service_logs({ service_id: \"svc_abc\", lines: 50, stream: \"stderr\", since: \"-1h\" })',\n\t\t' - Just count error lines without fetching them: get_service_logs({ service_id: \"svc_abc\", level: \"error\", since: \"-5m\", count_only: true }) → { count: 47 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tlines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Tail size; default 200, hard cap 1000.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Filter by structured JSON log level (pino/bunyan/severity). Falls back to a stream-alias hint for plain-text logs (info/debug→stdout, warn/error/fatal→stderr).',\n\t\t\t),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only { count } — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts: {\n\t\t\tlines?: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = {\n\t\t\tlines: args.lines ?? 200,\n\t\t};\n\t\tif (args.since) opts.since = args.since;\n\t\tif (args.until) opts.until = args.until;\n\t\tif (args.stream) opts.stream = args.stream;\n\t\tif (args.level) opts.level = args.level;\n\t\tif (args.search) opts.search = args.search;\n\t\tif (args.count_only) opts.countOnly = args.count_only;\n\n\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, args.service_id, opts);\n\t\tif ('count' in response) {\n\t\t\treturn respond({\n\t\t\t\tsummary: `${response.count} matching log line${response.count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\t\tdata: { count: response.count },\n\t\t\t});\n\t\t}\n\t\tconst count = Array.isArray(response.logs)\n\t\t\t? response.logs.length\n\t\t\t: typeof response.logs === 'string'\n\t\t\t\t? response.logs.split('\\n').length\n\t\t\t\t: 0;\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${count} log line${count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { logs: response.logs },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs_bulk',\n\tcategory: 'logs',\n\tdescription: [\n\t\t'Fetch runtime logs for multiple services in parallel and return one response keyed by service_id. Single round-trip from the MCP client perspective.',\n\t\t'',\n\t\t'When to use: investigating something that spans a project — \"what does every service in the auth-fleet say about the OOM at 14:00?\", \"did anything log an error in the last 5 minutes across these 4 services?\". Pair with list_services (filter by project/status) to get the service_ids list first.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_ids: list of service publicIds (svc_…). Hard cap 10 to bound work — for larger fleets, partition the call.',\n\t\t' - lines_per_service (optional): tail size per service (default 100, max 500). Smaller default than get_service_logs since you are fanning out.',\n\t\t' - since/until (optional): same shape as get_service_logs (ISO-8601 or \"-5m\", \"-1h\").',\n\t\t' - stream (optional): \"stdout\" | \"stderr\".',\n\t\t' - level (optional): structured-log level filter (trace/debug/info/warn/error/fatal).',\n\t\t' - search (optional): case-insensitive substring filter applied to every service.',\n\t\t' - count_only (optional): when true, return { results: { [service_id]: { count } } } — much cheaper for \"how many error lines per service in the last 5m\" surveys.',\n\t\t'',\n\t\t'Returns: { results: { [service_id]: { logs?, count?, error? } } } — per-service result, with `error` populated when one service failed (the rest still succeed).',\n\t\t'',\n\t\t'Example: get_service_logs_bulk({ service_ids: [\"svc_api\", \"svc_worker\"], level: \"error\", since: \"-15m\", count_only: true }) → { results: { svc_api: { count: 0 }, svc_worker: { count: 12 } } }.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_ids: z\n\t\t\t.array(z.string())\n\t\t\t.min(1)\n\t\t\t.max(10)\n\t\t\t.describe('Service publicIds (1–10). Hard cap 10 to bound parallel work.'),\n\t\tlines_per_service: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Tail size per service; default 100, hard cap 500.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe('Structured log level filter (same as get_service_logs).'),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only counts per service — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst linesPerService = args.lines_per_service ?? 100;\n\n\t\t// Build a single options object reused across all fan-outs so we\n\t\t// don't reconstruct identical params in a loop.\n\t\tconst baseOpts: {\n\t\t\tlines: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = { lines: linesPerService };\n\t\tif (args.since) baseOpts.since = args.since;\n\t\tif (args.until) baseOpts.until = args.until;\n\t\tif (args.stream) baseOpts.stream = args.stream;\n\t\tif (args.level) baseOpts.level = args.level;\n\t\tif (args.search) baseOpts.search = args.search;\n\t\tif (args.count_only) baseOpts.countOnly = args.count_only;\n\n\t\t// Promise.allSettled so one failing service (e.g. deleted between\n\t\t// list_services and this call) doesn't void the rest.\n\t\tconst settled = await Promise.allSettled(\n\t\t\targs.service_ids.map(async (sid) => {\n\t\t\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, sid, baseOpts);\n\t\t\t\treturn { sid, response };\n\t\t\t}),\n\t\t);\n\n\t\tconst results: Record<string, unknown> = {};\n\t\tlet okCount = 0;\n\t\tlet errorCount = 0;\n\t\tfor (let i = 0; i < settled.length; i += 1) {\n\t\t\tconst outcome = settled[i] as PromiseSettledResult<{\n\t\t\t\tsid: string;\n\t\t\t\tresponse: { logs?: unknown; count?: number };\n\t\t\t}>;\n\t\t\tconst sid = args.service_ids[i] as string;\n\t\t\tif (outcome.status === 'fulfilled') {\n\t\t\t\tconst { response } = outcome.value;\n\t\t\t\tif ('count' in response) {\n\t\t\t\t\tresults[sid] = { count: response.count };\n\t\t\t\t} else {\n\t\t\t\t\tresults[sid] = { logs: response.logs };\n\t\t\t\t}\n\t\t\t\tokCount += 1;\n\t\t\t} else {\n\t\t\t\tconst reason = outcome.reason as { message?: string } | string;\n\t\t\t\tresults[sid] = {\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof reason === 'string' ? reason : (reason?.message ?? 'Unknown error'),\n\t\t\t\t};\n\t\t\t\terrorCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tconst summary =\n\t\t\terrorCount === 0\n\t\t\t\t? `Fetched logs from ${okCount} service${okCount === 1 ? '' : 's'}.`\n\t\t\t\t: `Fetched logs from ${okCount} of ${args.service_ids.length} service${args.service_ids.length === 1 ? '' : 's'} (${errorCount} failed — see per-service .error field).`;\n\t\treturn respond({ summary, data: { results } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeVolume } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_volumes',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'List persistent disks attached to a service. Volumes mount writable storage into the container at the path you choose, surviving redeploys and restarts.',\n\t\t'',\n\t\t'When to use: auditing what disks are attached, checking sizes before adding more, or confirming a volume took effect after creation. Note: storage is metered for billing — use this to spot oversized disks.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: Volume[] } — each entry has id, publicId, name, mountPath, sizeGb, status (pending|active|deleting), createdAt, updatedAt.',\n\t\t'',\n\t\t'Example: list_volumes({ service_id: \"svc_abc\" }) → { items: [{ name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.volumes.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'volumes', shapeVolume);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No volumes attached to service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} volume${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Attach a new persistent disk to a service. The disk gets provisioned on the host and bind-mounted into the container at `mount_path` on the next deploy. Use this when an app needs writable persistent storage — uploads, caches, SQLite databases, anything that must survive container restarts.',\n\t\t'',\n\t\t'When to use: the user mentions storing files, uploads, or any data that needs to outlive a container restart. For purely ephemeral scratch space, point the app at /tmp instead (already tmpfs-mounted, free, no provisioning needed) — see HOSTSTACK_TMP_DIR.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to attach to.',\n\t\t' - name: lowercase alphanumeric + hyphens, ≤64 chars (used as the docker volume identifier — change with care once data is written).',\n\t\t' - mount_path: in-container absolute path (e.g. \"/var/data\").',\n\t\t' - size_gb: optional, 1–100, default 1. Counts against your plan storage quota and is metered for billing.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the created record.',\n\t\t'',\n\t\t'Example: create_volume({ service_id: \"svc_abc\", name: \"data\", mount_path: \"/var/data\", size_gb: 10 }) → { volume: { name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(64)\n\t\t\t.regex(/^[a-z0-9-]+$/)\n\t\t\t.describe('Volume name (lowercase alphanumeric + hyphens).'),\n\t\tmount_path: z\n\t\t\t.string()\n\t\t\t.startsWith('/')\n\t\t\t.max(500)\n\t\t\t.describe('In-container mount path (absolute).'),\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Disk size in GB (default 1, max 100).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; mountPath: string; sizeGb?: number } = {\n\t\t\tname: args.name,\n\t\t\tmountPath: args.mount_path,\n\t\t};\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tconst response = await ctx.hoststack.volumes.create(teamId, args.service_id, input);\n\t\tconst data = { volume: shape(response.volume) };\n\t\treturn respond({\n\t\t\tsummary: `Attached volume \"${args.name}\" (${args.size_gb ?? 1}GB) at ${args.mount_path} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t\"Update a volume's mountPath or sizeGb. Resizes take effect on the next deploy; shrinking is rejected at the storage layer (filesystems can't safely shrink under live data).\",\n\t\t'',\n\t\t'When to use: the user wants to grow the disk for an existing volume, or move where it mounts inside the container.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume to update.',\n\t\t' - mount_path (optional): new in-container mount path.',\n\t\t' - size_gb (optional): new size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the updated record.',\n\t\t'',\n\t\t'Example: update_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\", size_gb: 20 }) → { volume: { sizeGb: 20, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId (e.g. vol_…).'),\n\t\tmount_path: z.string().startsWith('/').max(500).optional().describe('New mount path.'),\n\t\tsize_gb: z.number().int().min(1).max(100).optional().describe('New size in GB.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { mountPath?: string; sizeGb?: number } = {};\n\t\tif (args.mount_path !== undefined) input.mountPath = args.mount_path;\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.volumes.update(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.volume_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { volume: shape(response.volume) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on volume ${args.volume_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Detach and deprovision a volume. The underlying disk is destroyed — back up any data first. Async: marks the row deleting, the host agent finalises removal once it acks. A periodic reaper retries if the agent was offline at delete time.',\n\t\t'',\n\t\t\"When to use: the user explicitly says to remove a volume, or you're cleaning up after retiring a service. Confirm before calling on production data.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.volumes.delete(teamId, args.service_id, args.volume_id);\n\t\treturn respond({\n\t\t\tsummary: `Detached volume ${args.volume_id} from service ${args.service_id} (deprovisioning).`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n"],"mappings":"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","names":["apiKey","baseUrl","server","args","server","server","args","z","z","args","z","z","args","z","z","args","z","z","args","z","z","args","z","zones","z","args","z","z","args","z","z","args","response","data","z","z","args","z","z","args","z","z","args","z","z","args","z","z","args","baseUrl","server"]}
|