capix-mcp 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +51 -72
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +18 -8
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +130 -6
  8. package/dist/index.js.map +1 -1
  9. package/dist/prompts.d.ts +6 -1
  10. package/dist/prompts.d.ts.map +1 -1
  11. package/dist/prompts.js +41 -75
  12. package/dist/prompts.js.map +1 -1
  13. package/dist/route-families.d.ts +47 -0
  14. package/dist/route-families.d.ts.map +1 -0
  15. package/dist/route-families.js +96 -0
  16. package/dist/route-families.js.map +1 -0
  17. package/dist/server.d.ts +36 -6
  18. package/dist/server.d.ts.map +1 -1
  19. package/dist/server.js +67 -11
  20. package/dist/server.js.map +1 -1
  21. package/dist/tools/define-tool.d.ts +33 -0
  22. package/dist/tools/define-tool.d.ts.map +1 -0
  23. package/dist/tools/define-tool.js +28 -0
  24. package/dist/tools/define-tool.js.map +1 -0
  25. package/dist/tools/factory.d.ts +42 -0
  26. package/dist/tools/factory.d.ts.map +1 -0
  27. package/dist/tools/factory.js +363 -0
  28. package/dist/tools/factory.js.map +1 -0
  29. package/dist/tools/generate.d.ts +108 -0
  30. package/dist/tools/generate.d.ts.map +1 -0
  31. package/dist/tools/generate.js +149 -0
  32. package/dist/tools/generate.js.map +1 -0
  33. package/dist/tools/infra-context.d.ts +28 -0
  34. package/dist/tools/infra-context.d.ts.map +1 -0
  35. package/dist/tools/infra-context.js +90 -0
  36. package/dist/tools/infra-context.js.map +1 -0
  37. package/dist/tools/memes.d.ts +31 -0
  38. package/dist/tools/memes.d.ts.map +1 -0
  39. package/dist/tools/memes.js +175 -0
  40. package/dist/tools/memes.js.map +1 -0
  41. package/dist/tools.d.ts +48 -7
  42. package/dist/tools.d.ts.map +1 -1
  43. package/dist/tools.js +346 -911
  44. package/dist/tools.js.map +1 -1
  45. package/dist/types.d.ts +17 -2
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/types.js +1 -2
  48. package/dist/types.js.map +1 -1
  49. package/package.json +4 -3
package/dist/tools.js CHANGED
@@ -1,5 +1,11 @@
1
1
  /**
2
- * Capix MCP Server — all 59 tool definitions.
2
+ * Capix MCP Server — all 37 tool definitions.
3
+ *
4
+ * 2026-07 repair: every tool below targets a route family that EXISTS in the
5
+ * control plane (app/api/v1/* in the protocol repo), verified route-by-route
6
+ * against the live source. Each tool declares its canonical path (`routePath`)
7
+ * and the registry gate (tools/registry.test.ts) asserts it against the
8
+ * allowlist in route-families.ts — no phantom routes can be re-introduced.
3
9
  *
4
10
  * Each tool is declared with:
5
11
  * - inputShape : a Zod raw shape (validated by the McpServer before dispatch)
@@ -11,88 +17,63 @@
11
17
  * structured content. The CapixApiError (problem+json) thrown by the client is
12
18
  * caught by the server wrapper and surfaced as an MCP error result.
13
19
  *
14
- * Tools are grouped by scope (mirrors services/capix-mcp/tools/*.ts):
15
- * discovery (9) · planning (6) · lifecycle (7) · networking (8) ·
16
- * testing (6) · verification (6) · website (17)
20
+ * CRUD-shaped tools are declared with `defineGeneratedTool` (tools/generate.ts)
21
+ * a declarative spec (method, path, input) from which the registration AND
22
+ * the HTTP call are generated instead of a hand-written handler. Everything
23
+ * else uses `defineTool` (tools/define-tool.ts) with an explicit handler.
24
+ *
25
+ * Tools are grouped by scope:
26
+ * discovery (7) · planning (2) · lifecycle (19) · verification (1) ·
27
+ * website (6) · infra-context (2)
28
+ * (The factory tools — jobs/training/agent-deploys — live in
29
+ * tools/factory.ts and join lifecycle; the meme/image tools live in
30
+ * tools/memes.ts: the meme catalog joins discovery, the two billable
31
+ * generators join lifecycle.)
32
+ *
33
+ * Removed in the repair (no backing route in the control plane — hidden, not
34
+ * deleted from git history):
35
+ * - networking scope (8 tools): /api/v1/networking/* and /api/v1/vpc do not
36
+ * exist. Returns with the networking roadmap (N1–N5).
37
+ * - testing scope (6 tools): /api/v1/testing/* does not exist (disposable
38
+ * test envs are not built).
39
+ * - attestation/zkVM/measurement tools (5): /api/v1/verification/* and
40
+ * /api/v1/attestations do not exist. Secured Cloud (TEE/zkVM) is deferred.
41
+ * - planning stubs (capix_compute_plan, capix_model_plan, capix_stack_*):
42
+ * /api/v1/planning/* does not exist; quoting lives at /api/v1/quotes.
43
+ * - lifecycle stubs (capix_start/stop/restart/extend): start/stop exist
44
+ * upstream ONLY as PATCH /api/v1/deployments/[id] { desiredState } with a
45
+ * mandatory If-Match etag — unreachable through the current client
46
+ * surface (no PATCH, no etag access). Restart/extend have no backend.
47
+ * - discovery stubs: capix_projects (no /api/v1/projects),
48
+ * capix_model_catalog (real catalog is /api/v1/models, covered by
49
+ * capix_model_list), capix_attestations (Secured Cloud deferred).
50
+ * - website stubs (12 tools): singular /api/v1/website/* never existed and
51
+ * detect/plan/quote/deploy/preview/deployments/logs/metrics/domains have
52
+ * no routes under the real /api/v1/websites family (build logs surface as
53
+ * `logTail` inside capix_website_get; previews are implicit per release).
54
+ * - infra-context stubs: capix_node_status, capix_earnings_check (no
55
+ * /api/v1/nodes/status or /api/v1/earnings), capix_deployment_list
56
+ * (duplicate of capix_deployments).
17
57
  */
18
58
  import { z } from "zod";
19
59
  import { APPROVAL_ONLY, BILLABLE, READ_ONLY, } from "./types.js";
60
+ import { defineTool } from "./tools/define-tool.js";
61
+ import { defineGeneratedTool } from "./tools/generate.js";
62
+ import { factoryTools } from "./tools/factory.js";
63
+ import { infraContextTools } from "./tools/infra-context.js";
64
+ import { memeImageTools } from "./tools/memes.js";
20
65
  // ===========================================================================
21
66
  // Reusable Zod fragments
22
67
  // ===========================================================================
23
68
  const iso8601 = z.string().datetime().or(z.string());
24
- const moneyShape = z.object({
25
- amount: z.string().describe("Integer minor units (e.g. lamports, micro-USDC, cents)."),
26
- asset: z.enum(["SOL", "USDC", "USD-credit"]),
27
- scale: z.number().int(),
28
- }).describe("Integer minor/native money amount serialized as a JSON string.");
29
69
  const deploymentIdShape = z.string().describe("Canonical deployment id (dep_…).");
30
70
  const operationIdShape = z.string().describe("Canonical operation id (op_…).");
31
- const quoteIdShape = z.string().describe("Canonical quote id (qt_…) from capix_compute_quote / capix_model_quote.");
32
- const siteIdShape = z.string().describe("Capix website project id.");
33
- const listResultShape = {
34
- entries: z.array(z.record(z.unknown())).optional().describe("Paginated item array."),
35
- nextCursor: z.string().optional().describe("Opaque cursor for the next page; absent when exhausted."),
36
- fetchedAt: iso8601.optional().describe("ISO8601 fetch timestamp."),
37
- };
38
- const quoteResultShape = {
39
- quoteId: quoteIdShape.optional(),
40
- price: moneyShape.optional(),
41
- providerCostBasis: moneyShape.optional(),
42
- fees: moneyShape.optional(),
43
- margin: moneyShape.optional(),
44
- provider: z.string().optional(),
45
- region: z.string().optional(),
46
- expiresAt: iso8601.optional(),
47
- feasible: z.boolean().optional(),
48
- };
49
- const deploymentResultShape = {
50
- deploymentId: deploymentIdShape.optional(),
51
- operationId: operationIdShape.optional(),
52
- phase: z.string().optional().describe("DeploymentPhase after mutation."),
53
- quoteId: quoteIdShape.optional(),
54
- holdId: z.string().optional().describe("Ledger hold that funds the deployment."),
55
- estimatedCost: moneyShape.optional(),
56
- acceptedAt: iso8601.optional(),
57
- };
58
- const networkResourceShape = {
59
- resourceId: z.string().optional(),
60
- kind: z.string().optional().describe("vpc | endpoint | port_forward | private_connection | dedicated_ip."),
61
- deploymentId: deploymentIdShape.optional(),
62
- status: z.enum(["provisioning", "ready", "failed", "destroyed"]).optional(),
63
- endpoint: z.string().optional(),
64
- acceptedAt: iso8601.optional(),
65
- };
66
- const siteResultShape = {
67
- siteId: siteIdShape.optional(),
68
- deploymentId: z.string().optional(),
69
- url: z.string().optional(),
70
- status: z.enum(["queued", "building", "ready", "failed"]).optional(),
71
- estimatedCost: moneyShape.optional(),
72
- acceptedAt: iso8601.optional(),
73
- };
71
+ const quoteIdShape = z.string().describe("Canonical quote id from capix_compute_quote / capix_model_quote.");
72
+ const siteIdShape = z.string().describe("Capix website id.");
74
73
  // ===========================================================================
75
- // defineTool — type-safe tool declaration helper
74
+ // defineTool — imported from tools/define-tool.ts (shared with sibling tool
75
+ // modules such as tools/infra-context.ts; see that file for the doc comment).
76
76
  // ===========================================================================
77
- /**
78
- * Declare a Capix tool with a Zod raw input shape. The handler receives
79
- * Zod-validated arguments (typed via inference) plus the Capix API client and
80
- * call context; it returns the upstream JSON object as structured content.
81
- */
82
- function defineTool(opts) {
83
- return {
84
- name: opts.name,
85
- description: opts.description,
86
- scope: opts.scope,
87
- billable: opts.billable,
88
- requiresApproval: opts.requiresApproval,
89
- inputShape: opts.inputShape,
90
- outputShape: opts.outputShape,
91
- // Sound cast: the McpServer validates args against inputShape before
92
- // invoking the handler, so the inferred arg shape is guaranteed at runtime.
93
- handler: opts.handler,
94
- };
95
- }
96
77
  /** Convenience: pull a string argument, throwing a typed error if missing. */
97
78
  function asStr(value, field) {
98
79
  if (typeof value !== "string" || value.length === 0) {
@@ -103,982 +84,435 @@ function asStr(value, field) {
103
84
  }
104
85
  return value;
105
86
  }
106
- /** Call the client, passing the approval token through for billable tools. */
107
- async function callBillable(client, approvalToken, path, body) {
108
- if (!approvalToken) {
109
- const err = new Error("approvalToken is required for this billable tool (§5)");
110
- err.capixCode = "approval_required";
111
- err.status = 402;
112
- throw err;
113
- }
114
- return client.post(path, body, { idempotent: true, approvalToken });
115
- }
116
87
  // ===========================================================================
117
- // Discovery tools (9) — read-only, auto-run after authentication.
88
+ // Discovery tools (6) — read-only, auto-run after authentication.
89
+ // (capix_meme_templates from tools/memes.ts is the 7th discovery tool.)
118
90
  // ===========================================================================
119
91
  const discoveryTools = [
120
92
  defineTool({
121
93
  name: "capix_account",
122
- description: "Inspect the authenticated account: wallet address, balance, spending limits, active deployments/agents. Read-only — safe to auto-run.",
94
+ description: "Inspect the authenticated account: id, email, wallet address, billing status, identities, devices. Read-only — safe to auto-run.",
123
95
  scope: "discovery",
124
96
  ...READ_ONLY,
97
+ routePath: "/api/v1/account",
125
98
  inputShape: {},
126
99
  outputShape: {
127
- accountId: z.string().optional(),
128
- walletAddress: z.string().optional(),
129
- balance: moneyShape.optional(),
130
- spendingLimitMinor: z.string().optional(),
131
- activeDeployments: z.number().int().optional(),
132
- activeAgents: z.number().int().optional(),
133
- lastBilledAt: iso8601.optional(),
100
+ id: z.string().optional(),
101
+ email: z.string().optional(),
102
+ wallet_address: z.string().optional(),
103
+ billing_status: z.string().optional(),
104
+ identities: z.array(z.record(z.unknown())).optional(),
105
+ devices: z.array(z.record(z.unknown())).optional(),
134
106
  },
135
107
  handler: async (_args, { client }) => client.get("/api/v1/account"),
136
108
  }),
137
109
  defineTool({
138
110
  name: "capix_balance",
139
- description: "Get the customer cash balance (available / held / total) for the account. Read-only.",
111
+ description: "Get the account balances (SOL / USDC ledger), USD valuation, and recent transactions. Read-only.",
140
112
  scope: "discovery",
141
113
  ...READ_ONLY,
114
+ routePath: "/api/v1/billing",
142
115
  inputShape: {
143
116
  asset: z.enum(["SOL", "USDC", "USD-credit"]).optional().describe("Optional asset filter."),
144
117
  },
145
118
  outputShape: {
119
+ ok: z.boolean().optional(),
146
120
  accountId: z.string().optional(),
147
- asset: z.enum(["SOL", "USDC", "USD-credit"]).optional(),
148
- available: z.string().optional(),
149
- held: z.string().optional(),
150
- total: z.string().optional(),
151
- asOf: iso8601.optional(),
121
+ balances: z.record(z.unknown()).optional(),
122
+ valuation: z.record(z.unknown()).optional(),
123
+ transactions: z.array(z.record(z.unknown())).optional(),
124
+ nextCursor: z.string().optional(),
152
125
  },
153
- handler: async (args, { client }) => client.get("/api/v1/account/balance", {
126
+ handler: async (args, { client }) => client.get("/api/v1/billing", {
154
127
  asset: args.asset,
155
128
  }),
156
129
  }),
157
- defineTool({
158
- name: "capix_projects",
159
- description: "List projects visible to the authenticated account. Read-only.",
160
- scope: "discovery",
161
- ...READ_ONLY,
162
- inputShape: {
163
- limit: z.number().int().min(1).max(200).default(50).describe("Max projects to return."),
164
- },
165
- outputShape: listResultShape,
166
- handler: async (args, { client }) => client.get("/api/v1/projects", { limit: args.limit }),
167
- }),
168
130
  defineTool({
169
131
  name: "capix_compute_catalog",
170
- description: "List the live compute capability catalog (provider / region / tier / price / availability). Read-only.",
171
- scope: "discovery",
172
- ...READ_ONLY,
173
- inputShape: {
174
- region: z.string().optional().describe("Optional region filter."),
175
- gpu: z.boolean().optional().describe("Restrict to GPU-capable entries."),
176
- },
177
- outputShape: {
178
- entries: z.array(z.record(z.unknown())).optional(),
179
- catalogVersion: z.string().optional(),
180
- fetchedAt: iso8601.optional(),
181
- },
182
- handler: async (args, { client }) => client.get("/api/v1/catalog/compute", {
183
- region: args.region,
184
- gpu: args.gpu,
185
- }),
186
- }),
187
- defineTool({
188
- name: "capix_model_catalog",
189
- description: "List the live model endpoint catalog (model id, context length, price per 1k tokens). Read-only.",
132
+ description: "List the enabled compute capability catalog: workload types (with features) and " +
133
+ "configured providers/regions. Public; reflects live feature gates. Read-only.",
190
134
  scope: "discovery",
191
135
  ...READ_ONLY,
192
- inputShape: {
193
- provider: z.string().optional().describe("Optional provider filter."),
194
- },
136
+ routePath: "/api/v1/catalog/capabilities",
137
+ inputShape: {},
195
138
  outputShape: {
196
- entries: z.array(z.record(z.unknown())).optional(),
197
- catalogVersion: z.string().optional(),
198
- fetchedAt: iso8601.optional(),
139
+ workloadTypes: z.array(z.record(z.unknown())).optional(),
140
+ providers: z.array(z.record(z.unknown())).optional(),
141
+ regions: z.array(z.string()).optional(),
142
+ releaseId: z.string().optional(),
143
+ timestamp: iso8601.optional(),
199
144
  },
200
- handler: async (args, { client }) => client.get("/api/v1/catalog/models", {
201
- provider: args.provider,
202
- }),
145
+ handler: async (_args, { client }) => client.get("/api/v1/catalog/capabilities"),
203
146
  }),
204
147
  defineTool({
205
148
  name: "capix_network_status",
206
- description: "Inspect network and gateway status (provider health, lanes, emergency flags). Read-only.",
149
+ description: "Inspect the Capix service health: status, version, release, and live feature gates " +
150
+ "(real money, provider writes, distributed, smart router, …). Public. Read-only.",
207
151
  scope: "discovery",
208
152
  ...READ_ONLY,
153
+ routePath: "/api/v1/health",
209
154
  inputShape: {},
210
155
  outputShape: {
211
- gateway: z.string().optional(),
212
- status: z.enum(["ok", "degraded", "outage"]).optional(),
213
- providers: z.array(z.record(z.unknown())).optional(),
214
- lanes: z.array(z.record(z.unknown())).optional(),
215
- emergencyFlags: z.array(z.record(z.unknown())).optional(),
216
- fetchedAt: iso8601.optional(),
156
+ status: z.string().optional(),
157
+ service: z.string().optional(),
158
+ version: z.string().optional(),
159
+ releaseId: z.string().optional(),
160
+ features: z.record(z.unknown()).optional(),
161
+ timestamp: iso8601.optional(),
217
162
  },
218
- handler: async (_args, { client }) => client.get("/api/v1/network/status"),
163
+ handler: async (_args, { client }) => client.get("/api/v1/health"),
219
164
  }),
220
- defineTool({
165
+ defineGeneratedTool({
221
166
  name: "capix_deployments",
222
167
  description: "List deployments for the account/project with phase + allocation state. Read-only.",
223
168
  scope: "discovery",
224
169
  ...READ_ONLY,
225
- inputShape: {
226
- limit: z.number().int().min(1).max(200).default(50).describe("Max deployments to return."),
227
- phase: z.string().optional().describe("Optional DeploymentPhase filter."),
170
+ method: "GET",
171
+ path: "/api/v1/deployments",
172
+ input: {
173
+ limit: z.number().int().min(1).max(100).default(25).describe("Max deployments to return (upstream bounds)."),
174
+ status: z.string().optional().describe("Optional phase filter (the upstream query param is `status`)."),
175
+ cursor: z.string().optional().describe("Opaque pagination cursor from a previous page."),
176
+ projectId: z.string().optional(),
177
+ },
178
+ outputShape: {
179
+ data: z.array(z.record(z.unknown())).optional(),
180
+ cursor: z.string().optional(),
181
+ hasMore: z.boolean().optional(),
228
182
  },
229
- outputShape: listResultShape,
230
- handler: async (args, { client }) => client.get("/api/v1/deployments", {
231
- limit: args.limit,
232
- phase: args.phase,
233
- }),
234
183
  }),
235
- defineTool({
184
+ defineGeneratedTool({
236
185
  name: "capix_receipts",
237
- description: "List settled work receipts (cost records) for the account. Read-only.",
186
+ description: "List work receipts (task/commit approval records) for the account. Read-only.",
238
187
  scope: "discovery",
239
188
  ...READ_ONLY,
240
- inputShape: {
241
- limit: z.number().int().min(1).max(200).default(50),
242
- since: iso8601.optional().describe("Optional lower bound (inclusive)."),
189
+ method: "GET",
190
+ path: "/api/v1/receipts",
191
+ input: {
192
+ limit: z.number().int().min(1).max(100).default(25),
193
+ approvalStatus: z.string().optional().describe("Optional approval-status filter."),
194
+ agentId: z.string().optional().describe("Optional agent filter."),
195
+ cursor: z.string().optional(),
196
+ projectId: z.string().optional(),
243
197
  },
244
- outputShape: listResultShape,
245
- handler: async (args, { client }) => client.get("/api/v1/receipts", {
246
- limit: args.limit,
247
- since: args.since,
248
- }),
249
- }),
250
- defineTool({
251
- name: "capix_attestations",
252
- description: "List attestation evidence records for the account (TEE / zkVM). Read-only.",
253
- scope: "discovery",
254
- ...READ_ONLY,
255
- inputShape: {
256
- limit: z.number().int().min(1).max(200).default(50),
257
- kind: z.enum(["tee", "zkvm"]).optional().describe("Optional attestation kind filter."),
198
+ outputShape: {
199
+ data: z.array(z.record(z.unknown())).optional(),
200
+ cursor: z.string().optional(),
201
+ hasMore: z.boolean().optional(),
258
202
  },
259
- outputShape: listResultShape,
260
- handler: async (args, { client }) => client.get("/api/v1/attestations", {
261
- limit: args.limit,
262
- kind: args.kind,
263
- }),
264
203
  }),
265
204
  ];
266
205
  // ===========================================================================
267
- // Planning tools (6) — read-only planning & quoting.
206
+ // Planning tools (2) — read-only quoting against POST /api/v1/quotes.
207
+ // The old /api/v1/planning/* family does not exist; the canonical quote route
208
+ // is /api/v1/quotes (15-minute TTL, locks price/cost/fees/margin/FX/expiry).
209
+ // The route REQUIRES an Idempotency-Key header even though quoting moves no
210
+ // money, so both tools post with the client's idempotency key.
268
211
  // ===========================================================================
269
- const computeSpecShape = {
270
- workloadType: z.string().describe("e.g. replicated_service.v1, private_inference.v1, agent_run.v1."),
271
- cpu: z.number().int().min(1),
272
- ramMb: z.number().int().min(128),
273
- storageGb: z.number().int().min(0),
274
- gpu: z.number().int().min(0).optional(),
275
- vramMb: z.number().int().min(0).optional(),
276
- region: z.string().optional(),
277
- imageDigest: z.string().optional().describe("Pinned image digest."),
278
- maxPriceUsdHourly: z.number().optional(),
279
- maxDurationHours: z.number().optional(),
280
- };
281
- const modelSpecShape = {
282
- modelId: z.string(),
283
- quantization: z.string().optional(),
284
- contextLength: z.number().int().min(1).optional(),
285
- gpuType: z.string().optional(),
286
- gpuCount: z.number().int().min(1).optional(),
287
- region: z.string().optional(),
288
- maxPriceUsdHourly: z.number().optional(),
289
- maxConcurrency: z.number().int().min(1).optional(),
290
- };
291
- const stackManifestShape = {
292
- name: z.string(),
293
- services: z.array(z.record(z.unknown())),
294
- network: z.record(z.unknown()).optional(),
295
- region: z.string().optional(),
212
+ /** Workload types accepted by POST /api/v1/quotes (workloadSpec.type). */
213
+ const WORKLOAD_TYPES = [
214
+ "dedicated_machine.v1",
215
+ "private_inference.v1",
216
+ "remote_workspace.v1",
217
+ "replicated_service.v1",
218
+ "sharded_batch.v1",
219
+ "agent_run.v1",
220
+ ];
221
+ const quoteResultShape = {
222
+ id: quoteIdShape.optional(),
223
+ price: z.record(z.unknown()).optional(),
224
+ providerCostBasis: z.record(z.unknown()).optional(),
225
+ fees: z.record(z.unknown()).optional(),
226
+ margin: z.record(z.unknown()).optional(),
227
+ expiresAt: iso8601.optional(),
228
+ state: z.string().optional(),
296
229
  };
297
230
  const planningTools = [
298
- defineTool({
299
- name: "capix_compute_plan",
300
- description: "Plan a compute deployment: pick provider/region/spec for the requested workload shape. Read-only.",
301
- scope: "planning",
302
- ...READ_ONLY,
303
- inputShape: computeSpecShape,
304
- outputShape: {
305
- feasible: z.boolean(),
306
- provider: z.string().optional(),
307
- region: z.string().optional(),
308
- cpu: z.number().int().optional(),
309
- ramMb: z.number().int().optional(),
310
- storageGb: z.number().int().optional(),
311
- gpu: z.number().int().optional(),
312
- estimatedCostHourly: moneyShape.optional(),
313
- estimatedCostTotal: moneyShape.optional(),
314
- notes: z.string().optional(),
315
- },
316
- handler: async (args, { client }) => client.post("/api/v1/planning/compute/plan", args),
317
- }),
318
- defineTool({
231
+ defineGeneratedTool({
319
232
  name: "capix_compute_quote",
320
- description: "Get a canonical quote for a compute deployment plan (locks asset/scale/FX/fee/expiry). Read-only.",
233
+ description: "Get a canonical quote for a compute workload (locks price/fees/FX/expiry for 15 min). " +
234
+ "Read-only intent; the quote route still requires an Idempotency-Key, which the client supplies.",
321
235
  scope: "planning",
322
236
  ...READ_ONLY,
323
- inputShape: computeSpecShape,
324
- outputShape: quoteResultShape,
325
- handler: async (args, { client }) => client.post("/api/v1/planning/compute/quote", args),
326
- }),
327
- defineTool({
328
- name: "capix_model_plan",
329
- description: "Plan a model endpoint for the requested model hosting shape. Read-only.",
330
- scope: "planning",
331
- ...READ_ONLY,
332
- inputShape: modelSpecShape,
333
- outputShape: {
334
- feasible: z.boolean(),
335
- provider: z.string().optional(),
336
- region: z.string().optional(),
337
- gpu: z.number().int().optional(),
338
- estimatedCostHourly: moneyShape.optional(),
339
- estimatedCostTotal: moneyShape.optional(),
340
- notes: z.string().optional(),
237
+ method: "POST",
238
+ path: "/api/v1/quotes",
239
+ // Read-only, but the route rejects requests without an Idempotency-Key.
240
+ idempotent: true,
241
+ input: {
242
+ workloadSpec: z
243
+ .object({
244
+ type: z.enum(WORKLOAD_TYPES),
245
+ cpu: z.number().positive(),
246
+ ramMb: z.number().positive(),
247
+ storageGb: z.number().min(0),
248
+ gpu: z.number().min(0).optional(),
249
+ vramMb: z.number().min(0).optional(),
250
+ region: z.string().optional().describe("eu | us | asia | global."),
251
+ maxDurationHours: z.number().optional(),
252
+ maxSpendUsd: z.number().optional(),
253
+ })
254
+ .describe("Workload shape quoted upstream."),
255
+ placementPolicy: z.record(z.unknown()).optional(),
256
+ paymentAsset: z.enum(["AUTO", "SOL", "USDC"]).optional(),
257
+ projectId: z.string().optional(),
341
258
  },
342
- handler: async (args, { client }) => client.post("/api/v1/planning/model/plan", args),
343
- }),
344
- defineTool({
345
- name: "capix_model_quote",
346
- description: "Get a canonical quote for a model endpoint plan. Read-only.",
347
- scope: "planning",
348
- ...READ_ONLY,
349
- inputShape: modelSpecShape,
350
259
  outputShape: quoteResultShape,
351
- handler: async (args, { client }) => client.post("/api/v1/planning/model/quote", args),
352
- }),
353
- defineTool({
354
- name: "capix_stack_validate",
355
- description: "Validate a stack manifest (services, dependencies, network) without provisioning. Read-only.",
356
- scope: "planning",
357
- ...READ_ONLY,
358
- inputShape: stackManifestShape,
359
- outputShape: {
360
- valid: z.boolean(),
361
- errors: z.array(z.string()).optional(),
362
- warnings: z.array(z.string()).optional(),
363
- },
364
- handler: async (args, { client }) => client.post("/api/v1/planning/stack/validate", args),
365
260
  }),
366
261
  defineTool({
367
- name: "capix_stack_plan",
368
- description: "Plan a multi-service stack: produce a deployment plan graph + aggregate quote. Read-only.",
262
+ name: "capix_model_quote",
263
+ description: "Get a canonical quote for a private model endpoint (private_inference.v1) hosting " +
264
+ "the given model. Read-only intent; the quote route still requires an Idempotency-Key, " +
265
+ "which the client supplies.",
369
266
  scope: "planning",
370
267
  ...READ_ONLY,
371
- inputShape: stackManifestShape,
372
- outputShape: {
373
- feasible: z.boolean(),
374
- services: z.array(z.record(z.unknown())).optional(),
375
- aggregateQuote: z.record(z.unknown()).optional(),
376
- notes: z.string().optional(),
268
+ routePath: "/api/v1/quotes",
269
+ inputShape: {
270
+ modelId: z.string().describe("Model id from capix_model_list."),
271
+ cpu: z.number().positive().describe("vCPUs for the endpoint (required upstream)."),
272
+ ramMb: z.number().positive().describe("RAM for the endpoint (required upstream)."),
273
+ storageGb: z.number().min(0).describe("Storage for weights/cache (required upstream)."),
274
+ gpu: z.number().int().min(1).optional(),
275
+ vramMb: z.number().int().min(0).optional(),
276
+ region: z.string().optional().describe("eu | us | asia | global."),
277
+ maxDurationHours: z.number().optional(),
278
+ paymentAsset: z.enum(["AUTO", "SOL", "USDC"]).optional(),
279
+ projectId: z.string().optional(),
377
280
  },
378
- handler: async (args, { client }) => client.post("/api/v1/planning/stack/plan", args),
281
+ outputShape: quoteResultShape,
282
+ handler: async (args, { client }) => client.post("/api/v1/quotes", {
283
+ workloadSpec: {
284
+ type: "private_inference.v1",
285
+ cpu: args.cpu,
286
+ ramMb: args.ramMb,
287
+ storageGb: args.storageGb,
288
+ gpu: args.gpu,
289
+ vramMb: args.vramMb,
290
+ region: args.region,
291
+ maxDurationHours: args.maxDurationHours,
292
+ payload: { modelId: asStr(args.modelId, "modelId"), source: "capix-mcp" },
293
+ },
294
+ paymentAsset: args.paymentAsset,
295
+ projectId: args.projectId,
296
+ }, { idempotent: true }),
379
297
  }),
380
298
  ];
381
299
  // ===========================================================================
382
- // Lifecycle tools (7) — billable mutations, require approval.
300
+ // Lifecycle tools (3 here) — billable mutations, require approval.
301
+ // The factory tools (jobs/training/agent-deploys, tools/factory.ts) and the
302
+ // two meme/image generators (tools/memes.ts) join this scope: 19 total.
303
+ //
304
+ // Removed: capix_start / capix_stop / capix_restart / capix_extend.
305
+ // Start/stop exist upstream ONLY as PATCH /api/v1/deployments/[id]
306
+ // { desiredState: "RUNNING" | "STOPPED" } with a mandatory If-Match etag —
307
+ // unreachable through the current CapixClientLike surface (no PATCH method,
308
+ // no etag access). Restart/extend have no backend at all. They return when
309
+ // the client grows PATCH+etag support (see route-families.ts).
383
310
  // ===========================================================================
311
+ const deploymentResultShape = {
312
+ operation: z.record(z.unknown()).optional().describe("Long-running operation (poll via its id)."),
313
+ deployment: z.record(z.unknown()).optional(),
314
+ settlement: z.record(z.unknown()).optional().describe("Early-termination settlement, when applicable."),
315
+ };
384
316
  const lifecycleTools = [
385
- defineTool({
317
+ defineGeneratedTool({
386
318
  name: "capix_deploy",
387
- description: "Deploy a workload against a canonical quote. Takes a ledger hold + provisions resources. Billable; requires approval.",
388
- scope: "lifecycle",
389
- ...BILLABLE,
390
- inputShape: {
391
- quoteId: quoteIdShape.describe("Canonical quote from capix_compute_quote / capix_model_quote."),
392
- workloadType: z.string(),
393
- region: z.string().optional(),
394
- imageDigest: z.string().optional(),
395
- durationHours: z.number().min(1).optional(),
396
- env: z.record(z.string()).optional(),
397
- ingress: z.array(z.number().int()).optional(),
398
- },
399
- outputShape: deploymentResultShape,
400
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/lifecycle/deployments", args),
401
- }),
402
- defineTool({
403
- name: "capix_start",
404
- description: "Start a stopped deployment. Billable (resumes metering); requires approval.",
319
+ description: "Deploy a workload against a canonical quote (from capix_compute_quote / " +
320
+ "capix_model_quote). Takes a ledger hold and returns a long-running operation. " +
321
+ "Billable; requires approval.",
405
322
  scope: "lifecycle",
406
323
  ...BILLABLE,
407
- inputShape: {
408
- deploymentId: deploymentIdShape,
409
- },
410
- outputShape: deploymentResultShape,
411
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/start`),
412
- }),
413
- defineTool({
414
- name: "capix_stop",
415
- description: "Stop a running deployment (halts metering, keeps allocation). Billable (minimal); requires approval.",
416
- scope: "lifecycle",
417
- ...BILLABLE,
418
- inputShape: {
419
- deploymentId: deploymentIdShape,
420
- },
421
- outputShape: deploymentResultShape,
422
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/stop`),
423
- }),
424
- defineTool({
425
- name: "capix_restart",
426
- description: "Restart a deployment (stop + start cycle). Billable; requires approval.",
427
- scope: "lifecycle",
428
- ...BILLABLE,
429
- inputShape: {
430
- deploymentId: deploymentIdShape,
324
+ method: "POST",
325
+ path: "/api/v1/deployments",
326
+ input: {
327
+ quoteId: quoteIdShape,
328
+ projectId: z.string().optional(),
431
329
  },
432
330
  outputShape: deploymentResultShape,
433
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/restart`),
434
331
  }),
435
- defineTool({
332
+ defineGeneratedTool({
436
333
  name: "capix_delete",
437
- description: "Delete (terminate) a deployment and release its allocation. Billable (final settlement); requires approval.",
334
+ description: "Terminate a deployment and release its allocation (settles early termination). " +
335
+ "Destructive; billable; requires approval.",
438
336
  scope: "lifecycle",
439
337
  ...BILLABLE,
440
- inputShape: {
441
- deploymentId: deploymentIdShape,
442
- reason: z.string().optional(),
338
+ method: "DELETE",
339
+ path: "/api/v1/deployments/:id",
340
+ input: {
341
+ id: deploymentIdShape,
443
342
  },
444
343
  outputShape: deploymentResultShape,
445
- handler: async (args, { client, ctx }) => {
446
- const id = asStr(args.deploymentId, "deploymentId");
447
- const res = await callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(id)}`, args.reason ? { reason: args.reason } : undefined);
448
- // DELETE semantics: the canonical route is DELETE; some gateways express
449
- // the mutation via POST with idempotency. Use the client delete path with
450
- // an approval header via a light POST-to-delete fallback.
451
- return res;
452
- },
453
344
  }),
454
- defineTool({
455
- name: "capix_extend",
456
- description: "Extend a running deployment by additional hours against a fresh hold. Billable; requires approval.",
457
- scope: "lifecycle",
458
- ...BILLABLE,
459
- inputShape: {
460
- deploymentId: deploymentIdShape,
461
- durationHours: z.number().min(1),
462
- quoteId: quoteIdShape.optional().describe("Canonical quote for the extension cost."),
463
- },
464
- outputShape: deploymentResultShape,
465
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/extend`, { durationHours: args.durationHours, quoteId: args.quoteId }),
466
- }),
467
- defineTool({
345
+ defineGeneratedTool({
468
346
  name: "capix_cancel",
469
- description: "Cancel an in-flight operation (best-effort; may trigger compensation). Read-only intent; requires approval to mutate.",
347
+ description: "Cancel an in-flight operation (sets CANCELLING; 409 on terminal states). " +
348
+ "Requires approval to mutate.",
470
349
  scope: "lifecycle",
471
350
  ...APPROVAL_ONLY,
472
- inputShape: {
351
+ method: "POST",
352
+ path: "/api/v1/operations/:operationId",
353
+ input: {
473
354
  operationId: operationIdShape,
474
- reason: z.string().optional(),
475
- },
476
- outputShape: {
477
- operationId: operationIdShape.optional(),
478
- status: z.string().optional(),
479
- acceptedAt: iso8601.optional(),
480
- },
481
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/operations/${encodeURIComponent(asStr(args.operationId, "operationId"))}/cancel`, args.reason ? { reason: args.reason } : undefined),
482
- }),
483
- ];
484
- // ===========================================================================
485
- // Networking tools (8) — billable mutations + read-only inspection.
486
- // ===========================================================================
487
- const networkingTools = [
488
- defineTool({
489
- name: "capix_create_vpc",
490
- description: "Create a VPC for a project. Billable; requires approval.",
491
- scope: "networking",
492
- ...BILLABLE,
493
- inputShape: {
494
- cidr: z.string().describe("VPC CIDR block, e.g. 10.0.0.0/16."),
495
- region: z.string(),
496
- name: z.string().optional(),
497
- },
498
- outputShape: networkResourceShape,
499
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/vpcs", args),
500
- }),
501
- defineTool({
502
- name: "capix_create_endpoint",
503
- description: "Create a public endpoint for a deployment. Billable; requires approval.",
504
- scope: "networking",
505
- ...BILLABLE,
506
- inputShape: {
507
- deploymentId: deploymentIdShape,
508
- port: z.number().int().min(1).max(65535),
509
- protocol: z.enum(["http", "https", "tcp"]).default("http"),
510
- hostname: z.string().optional().describe("Optional requested hostname."),
511
- },
512
- outputShape: networkResourceShape,
513
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/endpoints", args),
514
- }),
515
- defineTool({
516
- name: "capix_expose_port",
517
- description: "Expose a port (ingress) to the public internet. Billable; requires approval.",
518
- scope: "networking",
519
- ...BILLABLE,
520
- inputShape: {
521
- deploymentId: deploymentIdShape,
522
- port: z.number().int().min(1).max(65535),
523
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
524
- allowedCidrs: z.array(z.string()).optional().describe("Optional ingress allow-list."),
525
- },
526
- outputShape: networkResourceShape,
527
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/ports/expose", args),
528
- }),
529
- defineTool({
530
- name: "capix_close_port",
531
- description: "Close a previously exposed port. Non-billable soft mutation; requires approval.",
532
- scope: "networking",
533
- ...APPROVAL_ONLY,
534
- inputShape: {
535
- deploymentId: deploymentIdShape,
536
- port: z.number().int().min(1).max(65535),
537
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
538
- },
539
- outputShape: networkResourceShape,
540
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/ports/close", args),
541
- }),
542
- defineTool({
543
- name: "capix_inspect_routes",
544
- description: "Inspect the routing table for a deployment. Read-only.",
545
- scope: "networking",
546
- ...READ_ONLY,
547
- inputShape: {
548
- deploymentId: deploymentIdShape,
549
- },
550
- outputShape: {
551
- deploymentId: deploymentIdShape.optional(),
552
- routes: z.array(z.record(z.unknown())).optional(),
553
- fetchedAt: iso8601.optional(),
554
- },
555
- handler: async (args, { client }) => client.get(`/api/v1/networking/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/routes`),
556
- }),
557
- defineTool({
558
- name: "capix_create_private_connection",
559
- description: "Create a private endpoint connection (raw private endpoint). Billable; requires approval.",
560
- scope: "networking",
561
- ...BILLABLE,
562
- inputShape: {
563
- deploymentId: deploymentIdShape,
564
- service: z.string().describe("Target service identifier."),
565
- port: z.number().int().min(1).max(65535).optional(),
566
- },
567
- outputShape: networkResourceShape,
568
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/private-connections", args),
569
- }),
570
- defineTool({
571
- name: "capix_request_dedicated_ip",
572
- description: "Request a dedicated (non-shared) IP for a deployment. Paid; requires approval.",
573
- scope: "networking",
574
- ...BILLABLE,
575
- inputShape: {
576
- deploymentId: deploymentIdShape,
577
- region: z.string().optional(),
578
- quoteId: quoteIdShape.describe("Canonical quote for the dedicated IP."),
579
- },
580
- outputShape: networkResourceShape,
581
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/dedicated-ips", args),
582
- }),
583
- defineTool({
584
- name: "capix_port_forward",
585
- description: "Set up port forwarding between a deployment port and a host port. Billable; requires approval.",
586
- scope: "networking",
587
- ...BILLABLE,
588
- inputShape: {
589
- deploymentId: deploymentIdShape,
590
- sourcePort: z.number().int().min(1).max(65535),
591
- targetPort: z.number().int().min(1).max(65535),
592
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
593
- },
594
- outputShape: networkResourceShape,
595
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/port-forwards", args),
596
- }),
597
- ];
598
- // ===========================================================================
599
- // Testing tools (6) — disposable envs, inspection, bounded commands, cleanup.
600
- // ===========================================================================
601
- const testingTools = [
602
- defineTool({
603
- name: "capix_create_test_env",
604
- description: "Create a disposable test environment from a spec. Billable; requires approval.",
605
- scope: "testing",
606
- ...BILLABLE,
607
- inputShape: {
608
- name: z.string(),
609
- workloadType: z.string(),
610
- cpu: z.number().int().min(1),
611
- ramMb: z.number().int().min(128),
612
- region: z.string().optional(),
613
- imageDigest: z.string().optional(),
614
- ttlMinutes: z.number().int().min(1).max(1440).default(60).describe("Auto-destroy TTL."),
615
- quoteId: quoteIdShape.optional().describe("Canonical quote for the env."),
616
- },
617
- outputShape: {
618
- envId: z.string().optional(),
619
- status: z.enum(["provisioning", "ready", "failed"]).optional(),
620
- provider: z.string().optional(),
621
- region: z.string().optional(),
622
- endpoint: z.string().optional(),
623
- provisionedAt: iso8601.optional(),
624
- },
625
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/testing/environments", args),
626
- }),
627
- defineTool({
628
- name: "capix_run_health_checks",
629
- description: "Run health checks (liveness/readiness probes) on a deployment. Read-only.",
630
- scope: "testing",
631
- ...READ_ONLY,
632
- inputShape: {
633
- deploymentId: deploymentIdShape,
634
- },
635
- outputShape: {
636
- deploymentId: deploymentIdShape.optional(),
637
- healthy: z.boolean().optional(),
638
- checks: z.array(z.record(z.unknown())).optional(),
639
- ranAt: iso8601.optional(),
640
- },
641
- handler: async (args, { client }) => client.get(`/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/health`),
642
- }),
643
- defineTool({
644
- name: "capix_run_bounded_command",
645
- description: "Run an allow-listed, time-bounded command on a deployment node. Requires approval (may execute code).",
646
- scope: "testing",
647
- ...APPROVAL_ONLY,
648
- inputShape: {
649
- deploymentId: deploymentIdShape,
650
- command: z.string().describe("Command string from the allow-list."),
651
- timeoutSeconds: z.number().int().min(1).max(600).default(60),
652
- expectedExitCode: z.number().int().default(0),
653
- },
654
- outputShape: {
655
- deploymentId: deploymentIdShape.optional(),
656
- results: z.array(z.record(z.unknown())).optional(),
657
- completedAt: iso8601.optional(),
658
- },
659
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/commands`, args),
660
- }),
661
- defineTool({
662
- name: "capix_inspect_logs",
663
- description: "Inspect deployment logs. Read-only.",
664
- scope: "testing",
665
- ...READ_ONLY,
666
- inputShape: {
667
- deploymentId: deploymentIdShape,
668
- limit: z.number().int().min(1).max(1000).default(200),
669
- since: iso8601.optional(),
670
- level: z.enum(["debug", "info", "warn", "error"]).optional(),
671
- },
672
- outputShape: {
673
- resourceId: z.string().optional(),
674
- lines: z.array(z.record(z.unknown())).optional(),
675
- truncated: z.boolean().optional(),
676
- },
677
- handler: async (args, { client }) => client.get(`/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/logs`, { limit: args.limit, since: args.since, level: args.level }),
678
- }),
679
- defineTool({
680
- name: "capix_inspect_metrics",
681
- description: "Inspect deployment metrics. Read-only.",
682
- scope: "testing",
683
- ...READ_ONLY,
684
- inputShape: {
685
- deploymentId: deploymentIdShape,
686
- windowMinutes: z.number().int().min(1).max(10080).default(60),
687
- names: z.array(z.string()).optional().describe("Optional metric name filter."),
688
- },
689
- outputShape: {
690
- resourceId: z.string().optional(),
691
- metrics: z.array(z.record(z.unknown())).optional(),
692
- },
693
- handler: async (args, { client }) => client.get(`/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/metrics`, { windowMinutes: args.windowMinutes, names: args.names }),
694
- }),
695
- defineTool({
696
- name: "capix_destroy_task_resources",
697
- description: "Destroy all resources scoped to a task (cleanup safety gate §18). Destructive; requires approval.",
698
- scope: "testing",
699
- ...APPROVAL_ONLY,
700
- inputShape: {
701
- resourceId: z.string().describe("Task-scoped resource id to destroy."),
702
- reason: z.string().optional(),
703
- },
704
- outputShape: {
705
- resourceId: z.string().optional(),
706
- status: z.enum(["destroyed", "failed", "not_found"]).optional(),
707
- destroyedAt: iso8601.optional(),
708
- },
709
- handler: async (args, { client, ctx }) => {
710
- const id = asStr(args.resourceId, "resourceId");
711
- return callBillable(client, ctx.approvalToken, `/api/v1/testing/environments/${encodeURIComponent(id)}`, args.reason ? { reason: args.reason } : undefined);
712
355
  },
356
+ outputShape: deploymentResultShape,
713
357
  }),
714
358
  ];
715
359
  // ===========================================================================
716
- // Verification tools (6) — read-only attestation/proof/receipt inspection.
360
+ // Verification tools (1) — read-only receipt inspection.
361
+ // The attestation/zkVM/measurement tools were removed: /api/v1/verification/*
362
+ // and /api/v1/attestations do not exist (Secured Cloud deferred — see
363
+ // route-families.ts). capix_inspect_receipt survives, retargeted from the
364
+ // nonexistent /api/v1/verification/receipts/[id] to the real signed
365
+ // route-receipt route (the scheduler's placement receipt tied to a quote:
366
+ // customer price, provider cost basis, fees, margin).
717
367
  // ===========================================================================
718
368
  const verificationTools = [
719
- defineTool({
720
- name: "capix_fetch_attestation",
721
- description: "Fetch attestation evidence for a deployment (TEE / zkVM). Read-only.",
722
- scope: "verification",
723
- ...READ_ONLY,
724
- inputShape: {
725
- deploymentId: deploymentIdShape,
726
- },
727
- outputShape: {
728
- attestationId: z.string().optional(),
729
- kind: z.enum(["tee", "zkvm"]).optional(),
730
- resourceId: z.string().optional(),
731
- evidence: z.string().optional(),
732
- measurement: z.string().optional(),
733
- fetchedAt: iso8601.optional(),
734
- },
735
- handler: async (args, { client }) => client.get(`/api/v1/verification/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/attestation`),
736
- }),
737
- defineTool({
738
- name: "capix_verify_attestation",
739
- description: "Verify an attestation against the expected reference measurement. Read-only.",
740
- scope: "verification",
741
- ...READ_ONLY,
742
- inputShape: {
743
- attestationId: z.string(),
744
- expectedMeasurement: z.string().optional().describe("Optional pinned reference measurement."),
745
- },
746
- outputShape: {
747
- subjectId: z.string().optional(),
748
- kind: z.enum(["tee", "zkvm", "measurement", "receipt"]).optional(),
749
- verified: z.boolean().optional(),
750
- reason: z.string().optional(),
751
- verifier: z.string().optional(),
752
- verifiedAt: iso8601.optional(),
753
- },
754
- handler: async (args, { client }) => client.post(`/api/v1/verification/attestations/${encodeURIComponent(asStr(args.attestationId, "attestationId"))}/verify`, { expectedMeasurement: args.expectedMeasurement }),
755
- }),
756
- defineTool({
757
- name: "capix_fetch_proof",
758
- description: "Fetch a zkVM proof artifact for a workload. Read-only.",
759
- scope: "verification",
760
- ...READ_ONLY,
761
- inputShape: {
762
- workloadId: z.string(),
763
- },
764
- outputShape: {
765
- proofId: z.string().optional(),
766
- workloadId: z.string().optional(),
767
- proofSystem: z.string().optional(),
768
- artifactRef: z.string().optional(),
769
- publicInputs: z.string().optional(),
770
- fetchedAt: iso8601.optional(),
771
- },
772
- handler: async (args, { client }) => client.get(`/api/v1/verification/workloads/${encodeURIComponent(asStr(args.workloadId, "workloadId"))}/proof`),
773
- }),
774
- defineTool({
775
- name: "capix_verify_proof",
776
- description: "Verify a zkVM proof artifact against its public inputs. Read-only.",
777
- scope: "verification",
778
- ...READ_ONLY,
779
- inputShape: {
780
- proofId: z.string(),
781
- },
782
- outputShape: {
783
- subjectId: z.string().optional(),
784
- kind: z.enum(["tee", "zkvm", "measurement", "receipt"]).optional(),
785
- verified: z.boolean().optional(),
786
- reason: z.string().optional(),
787
- verifier: z.string().optional(),
788
- verifiedAt: iso8601.optional(),
789
- },
790
- handler: async (args, { client }) => client.post(`/api/v1/verification/proofs/${encodeURIComponent(asStr(args.proofId, "proofId"))}/verify`),
791
- }),
792
- defineTool({
793
- name: "capix_inspect_measurement",
794
- description: "Inspect the recorded measurement of a workload (used to pin expected TEE state). Read-only.",
795
- scope: "verification",
796
- ...READ_ONLY,
797
- inputShape: {
798
- workloadId: z.string(),
799
- },
800
- outputShape: {
801
- workloadId: z.string().optional(),
802
- measurement: z.string().optional(),
803
- measurementAlgorithm: z.string().optional(),
804
- imageDigest: z.string().optional(),
805
- recordedAt: iso8601.optional(),
806
- },
807
- handler: async (args, { client }) => client.get(`/api/v1/verification/workloads/${encodeURIComponent(asStr(args.workloadId, "workloadId"))}/measurement`),
808
- }),
809
- defineTool({
369
+ defineGeneratedTool({
810
370
  name: "capix_inspect_receipt",
811
- description: "Inspect a settled work receipt (cost breakdown, settlement, approval status). Read-only.",
371
+ description: "Inspect a signed route receipt: the scheduler's placement decision for a quote — " +
372
+ "customer price, provider cost basis, fees, margin, constraints, allocations. Read-only.",
812
373
  scope: "verification",
813
374
  ...READ_ONLY,
814
- inputShape: {
815
- receiptId: z.string(),
375
+ method: "GET",
376
+ path: "/api/v1/route-receipts/:receiptId",
377
+ input: {
378
+ receiptId: z.string().describe("Route receipt id (returned as x-capix-route-receipt-id with a quote)."),
816
379
  },
817
380
  outputShape: {
818
- receiptId: z.string().optional(),
819
- approvalStatus: z.string().optional(),
820
- cost: z.record(z.unknown()).optional(),
821
- settlement: z.record(z.unknown()).optional(),
822
- postedAt: iso8601.optional(),
381
+ id: z.string().optional(),
382
+ data: z.record(z.unknown()).optional().describe("RouteReceiptData: workloadType, quoteId, customerPrice, providerCostBasis, fees, margin, allocations, …"),
383
+ signature: z.string().optional(),
384
+ kmsKeyId: z.string().optional(),
823
385
  },
824
- handler: async (args, { client }) => client.get(`/api/v1/verification/receipts/${encodeURIComponent(asStr(args.receiptId, "receiptId"))}`),
825
386
  }),
826
387
  ];
827
388
  // ===========================================================================
828
- // Website tools (17) — static-site + preview hosting.
389
+ // Website tools (6) — static-site hosting on the REAL /api/v1/websites family
390
+ // (plural; the singular /api/v1/website/* these tools used to target never
391
+ // existed). Real surface (app/api/v1/websites/*):
392
+ // POST /api/v1/websites create + build (name, sourceRef, buildCommand?)
393
+ // GET /api/v1/websites list (with up to 5 releases each)
394
+ // GET /api/v1/websites/[id] detail (releases + 50-line build logTail)
395
+ // POST /api/v1/websites/[id]/promote promote a built release to production
396
+ // POST /api/v1/websites/[id]/rollback roll back to a previous built release
397
+ // DELETE /api/v1/websites/[id] destroy (soft)
398
+ //
399
+ // Known backend gap: promote/rollback are dispatched inside [id]/route.ts on
400
+ // the URL suffix, but the App Router has no [id]/promote/route.ts shim or
401
+ // rewrite, so those paths 404 until the control plane adds the segment
402
+ // shims. The tools are registered against the documented contract (the route
403
+ // file's own header advertises the paths) — track the backend fix before
404
+ // relying on them.
405
+ //
406
+ // Removed (no route): check-project-string, detect (worker-internal only),
407
+ // plan, quote (no website workload type in /api/v1/quotes), deploy (no
408
+ // redeploy route — create builds+deploys), preview (implicit per release),
409
+ // deployments (releases are embedded in list/get), logs (logTail in get),
410
+ // metrics, domain add/verify/remove (wildcard *.capix.dev not live yet).
829
411
  // ===========================================================================
830
- const repoOptShape = {
831
- repoUrl: z.string().describe("Git repository URL."),
832
- branch: z.string().default("main"),
833
- subPath: z.string().optional().describe("Optional monorepo subpath."),
834
- framework: z.string().optional().describe("Optional pinned framework."),
835
- };
412
+ const websiteObjectShape = z
413
+ .record(z.unknown())
414
+ .describe("Website: id, name, slug, status (building|preview|live|failed|destroyed), sourceRef, " +
415
+ "previewUrl, productionUrl, futureUrls, releases[{id, contentHash, status, step, error, createdAt, url}], createdAt.");
836
416
  const websiteTools = [
837
- defineTool({
838
- name: "capix_website_project_string_check",
839
- description: "Check a repository string for framework/dep compatibility with Capix hosting. Read-only.",
840
- scope: "website",
841
- ...READ_ONLY,
842
- inputShape: {
843
- repoUrl: z.string(),
844
- },
845
- outputShape: {
846
- ok: z.boolean().optional(),
847
- framework: z.string().optional(),
848
- notes: z.string().optional(),
849
- },
850
- handler: async (args, { client }) => client.post("/api/v1/website/check-project-string", { repoUrl: asStr(args.repoUrl, "repoUrl") }),
851
- }),
852
- defineTool({
417
+ defineGeneratedTool({
853
418
  name: "capix_website_create",
854
- description: "Create a Capix website project from a repo. Billable; requires approval.",
419
+ description: "Create a website from a source ref (repo/archive URL): clones, detects the framework " +
420
+ "server-side, builds, and serves a preview release. Billable; requires approval.",
855
421
  scope: "website",
856
422
  ...BILLABLE,
857
- inputShape: repoOptShape,
858
- outputShape: siteResultShape,
859
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/website/sites", args),
860
- }),
861
- defineTool({
862
- name: "capix_website_detect",
863
- description: "Detect the framework/build settings of a repo without provisioning. Read-only.",
864
- scope: "website",
865
- ...READ_ONLY,
866
- inputShape: {
867
- repoUrl: z.string(),
868
- branch: z.string().optional(),
423
+ method: "POST",
424
+ path: "/api/v1/websites",
425
+ input: {
426
+ name: z.string().min(1).describe("Site name (required upstream; slugified, unique per account)."),
427
+ sourceRef: z.string().min(1).describe("Source reference (repository/archive URL). Required upstream."),
428
+ buildCommand: z.string().optional().describe("Optional build command override (auto-detected when omitted)."),
429
+ projectId: z.string().optional(),
869
430
  },
870
431
  outputShape: {
871
- framework: z.string().optional(),
872
- buildCommand: z.string().optional(),
873
- outputDir: z.string().optional(),
874
- nodeVersion: z.string().optional(),
875
- detectedAt: iso8601.optional(),
432
+ website: websiteObjectShape.optional(),
876
433
  },
877
- handler: async (args, { client }) => client.post("/api/v1/website/detect", {
878
- repoUrl: asStr(args.repoUrl, "repoUrl"),
879
- branch: args.branch,
880
- }),
881
434
  }),
882
- defineTool({
883
- name: "capix_website_plan",
884
- description: "Plan a website deployment (build + hosting plan). Read-only.",
435
+ defineGeneratedTool({
436
+ name: "capix_website_list",
437
+ description: "List the account's websites with their latest releases. Read-only.",
885
438
  scope: "website",
886
439
  ...READ_ONLY,
887
- inputShape: {
888
- siteId: siteIdShape.optional(),
889
- repoUrl: z.string().optional(),
890
- branch: z.string().optional(),
891
- subPath: z.string().optional(),
892
- framework: z.string().optional(),
893
- },
440
+ method: "GET",
441
+ path: "/api/v1/websites",
442
+ input: {},
894
443
  outputShape: {
895
- feasible: z.boolean().optional(),
896
- plan: z.record(z.unknown()).optional(),
897
- notes: z.string().optional(),
444
+ websites: z.array(z.record(z.unknown())).optional(),
898
445
  },
899
- handler: async (args, { client }) => client.post("/api/v1/website/plan", args),
900
446
  }),
901
- defineTool({
902
- name: "capix_website_quote",
903
- description: "Get a canonical quote for a website deploy. Read-only.",
447
+ defineGeneratedTool({
448
+ name: "capix_website_get",
449
+ description: "Get a website by id: status, URLs, and the last 20 releases including per-release " +
450
+ "build step, error, and a 50-line build log tail. Read-only.",
904
451
  scope: "website",
905
452
  ...READ_ONLY,
906
- inputShape: {
907
- repoUrl: z.string().optional(),
908
- branch: z.string().optional(),
909
- durationHours: z.number().optional(),
453
+ method: "GET",
454
+ path: "/api/v1/websites/:id",
455
+ input: {
456
+ id: siteIdShape,
910
457
  },
911
458
  outputShape: {
912
- quoteId: z.string().optional(),
913
- price: moneyShape.optional(),
914
- feasible: z.boolean().optional(),
915
- expiresAt: iso8601.optional(),
459
+ website: websiteObjectShape.optional(),
916
460
  },
917
- handler: async (args, { client }) => client.post("/api/v1/website/quote", args),
918
461
  }),
919
- defineTool({
920
- name: "capix_website_deploy",
921
- description: "Deploy a website build to production. Billable; requires approval.",
922
- scope: "website",
923
- ...BILLABLE,
924
- inputShape: {
925
- siteId: siteIdShape,
926
- branch: z.string().optional(),
927
- commit: z.string().optional(),
928
- quoteId: quoteIdShape.optional(),
929
- },
930
- outputShape: siteResultShape,
931
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/deploy`, { branch: args.branch, commit: args.commit, quoteId: args.quoteId }),
932
- }),
933
- defineTool({
934
- name: "capix_website_preview",
935
- description: "Create a preview deployment for a branch. Billable; requires approval.",
936
- scope: "website",
937
- ...BILLABLE,
938
- inputShape: {
939
- siteId: siteIdShape,
940
- branch: z.string(),
941
- },
942
- outputShape: siteResultShape,
943
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/preview`, { branch: args.branch }),
944
- }),
945
- defineTool({
462
+ defineGeneratedTool({
946
463
  name: "capix_website_promote",
947
- description: "Promote a preview deployment to production. Billable; requires approval.",
948
- scope: "website",
949
- ...BILLABLE,
950
- inputShape: {
951
- siteId: siteIdShape,
952
- },
953
- outputShape: siteResultShape,
954
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/promote`),
955
- }),
956
- defineTool({
957
- name: "capix_website_rollback",
958
- description: "Roll a website back to its previous production deployment. Billable; requires approval.",
464
+ description: "Promote a built release to production (defaults to the latest release). " +
465
+ "NOTE: depends on a pending control-plane route shim for /promote — see tools.ts. " +
466
+ "Requires approval.",
959
467
  scope: "website",
960
468
  ...BILLABLE,
961
- inputShape: {
962
- siteId: siteIdShape,
963
- targetDeploymentId: z.string().optional(),
964
- },
965
- outputShape: siteResultShape,
966
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/rollback`, { targetDeploymentId: args.targetDeploymentId }),
967
- }),
968
- defineTool({
969
- name: "capix_website_get",
970
- description: "Get a website project descriptor. Read-only.",
971
- scope: "website",
972
- ...READ_ONLY,
973
- inputShape: {
974
- siteId: siteIdShape,
975
- },
976
- outputShape: {
977
- siteId: siteIdShape.optional(),
978
- name: z.string().optional(),
979
- url: z.string().optional(),
980
- framework: z.string().optional(),
981
- status: z.string().optional(),
982
- createdAt: iso8601.optional(),
983
- },
984
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}`),
985
- }),
986
- defineTool({
987
- name: "capix_website_deployments",
988
- description: "List deployments for a website. Read-only.",
989
- scope: "website",
990
- ...READ_ONLY,
991
- inputShape: {
992
- siteId: siteIdShape,
993
- limit: z.number().int().min(1).max(200).default(50),
994
- },
995
- outputShape: listResultShape,
996
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/deployments`, { limit: args.limit }),
997
- }),
998
- defineTool({
999
- name: "capix_website_logs",
1000
- description: "Inspect build/runtime logs for a website. Read-only.",
1001
- scope: "website",
1002
- ...READ_ONLY,
1003
- inputShape: {
1004
- siteId: siteIdShape,
1005
- deploymentId: z.string().optional(),
1006
- limit: z.number().int().min(1).max(1000).default(200),
469
+ method: "POST",
470
+ path: "/api/v1/websites/:id/promote",
471
+ input: {
472
+ id: siteIdShape,
473
+ releaseId: z.string().optional().describe("Release to promote; defaults to the latest built release."),
1007
474
  },
1008
475
  outputShape: {
1009
- lines: z.array(z.record(z.unknown())).optional(),
1010
- truncated: z.boolean().optional(),
1011
- },
1012
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/logs`, { deploymentId: args.deploymentId, limit: args.limit }),
1013
- }),
1014
- defineTool({
1015
- name: "capix_website_metrics",
1016
- description: "Inspect request/bandwidth metrics for a website. Read-only.",
1017
- scope: "website",
1018
- ...READ_ONLY,
1019
- inputShape: {
1020
- siteId: siteIdShape,
1021
- windowMinutes: z.number().int().min(1).max(10080).default(60),
1022
- },
1023
- outputShape: {
1024
- metrics: z.array(z.record(z.unknown())).optional(),
476
+ ok: z.boolean().optional(),
477
+ productionUrl: z.string().optional(),
478
+ releaseId: z.string().optional(),
1025
479
  },
1026
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/metrics`, { windowMinutes: args.windowMinutes }),
1027
480
  }),
1028
- defineTool({
1029
- name: "capix_website_domain_add",
1030
- description: "Attach a custom domain to a website. Billable; requires approval.",
481
+ defineGeneratedTool({
482
+ name: "capix_website_rollback",
483
+ description: "Roll a website back to a previous built release (defaults to the last built release " +
484
+ "before the current production one). NOTE: depends on a pending control-plane route " +
485
+ "shim for /rollback — see tools.ts. Requires approval.",
1031
486
  scope: "website",
1032
487
  ...BILLABLE,
1033
- inputShape: {
1034
- siteId: siteIdShape,
1035
- domain: z.string(),
1036
- },
1037
- outputShape: siteResultShape,
1038
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains`, { domain: asStr(args.domain, "domain") }),
1039
- }),
1040
- defineTool({
1041
- name: "capix_website_domain_verify",
1042
- description: "Verify DNS ownership for a pending custom domain. Read-only.",
1043
- scope: "website",
1044
- ...READ_ONLY,
1045
- inputShape: {
1046
- siteId: siteIdShape,
1047
- domain: z.string(),
488
+ method: "POST",
489
+ path: "/api/v1/websites/:id/rollback",
490
+ input: {
491
+ id: siteIdShape,
492
+ releaseId: z.string().optional().describe("Release to restore; defaults to the previous built release."),
1048
493
  },
1049
494
  outputShape: {
1050
- verified: z.boolean().optional(),
1051
- records: z.array(z.record(z.unknown())).optional(),
1052
- reason: z.string().optional(),
1053
- },
1054
- handler: async (args, { client }) => client.post(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains/verify`, { domain: asStr(args.domain, "domain") }),
1055
- }),
1056
- defineTool({
1057
- name: "capix_website_domain_remove",
1058
- description: "Detach a custom domain from a website. Billable; requires approval.",
1059
- scope: "website",
1060
- ...BILLABLE,
1061
- inputShape: {
1062
- siteId: siteIdShape,
1063
- domain: z.string(),
1064
- },
1065
- outputShape: siteResultShape,
1066
- handler: async (args, { client, ctx }) => {
1067
- const res = await callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains/${encodeURIComponent(asStr(args.domain, "domain"))}`);
1068
- return res;
495
+ ok: z.boolean().optional(),
496
+ productionUrl: z.string().optional(),
497
+ releaseId: z.string().optional(),
498
+ rolledBack: z.boolean().optional(),
1069
499
  },
1070
500
  }),
1071
- defineTool({
501
+ defineGeneratedTool({
1072
502
  name: "capix_website_destroy",
1073
- description: "Destroy a website project and all its deployments. Billable; requires approval.",
503
+ description: "Destroy a website (soft delete; releases stop serving). Destructive; requires approval.",
1074
504
  scope: "website",
1075
505
  ...BILLABLE,
1076
- inputShape: {
1077
- siteId: siteIdShape,
1078
- reason: z.string().optional(),
506
+ method: "DELETE",
507
+ path: "/api/v1/websites/:id",
508
+ input: {
509
+ id: siteIdShape,
510
+ },
511
+ outputShape: {
512
+ ok: z.boolean().optional(),
513
+ id: z.string().optional(),
514
+ status: z.string().optional(),
1079
515
  },
1080
- outputShape: siteResultShape,
1081
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/destroy`, args.reason ? { reason: args.reason } : undefined),
1082
516
  }),
1083
517
  ];
1084
518
  // ===========================================================================
@@ -1088,14 +522,15 @@ export const TOOLS = [
1088
522
  ...discoveryTools,
1089
523
  ...planningTools,
1090
524
  ...lifecycleTools,
1091
- ...networkingTools,
1092
- ...testingTools,
1093
525
  ...verificationTools,
1094
526
  ...websiteTools,
527
+ ...factoryTools,
528
+ ...infraContextTools,
529
+ ...memeImageTools,
1095
530
  ];
1096
531
  export const TOOL_NAMES = TOOLS.map((t) => t.name);
1097
532
  export const TOOL_COUNT = TOOLS.length;
1098
533
  /** Map of tool name → definition, used by the server for O(1) lookup. */
1099
534
  export const TOOL_MAP = new Map(TOOLS.map((t) => [t.name, t]));
1100
- export { discoveryTools, planningTools, lifecycleTools, networkingTools, testingTools, verificationTools, websiteTools };
535
+ export { discoveryTools, planningTools, lifecycleTools, verificationTools, websiteTools, factoryTools, infraContextTools, memeImageTools };
1101
536
  //# sourceMappingURL=tools.js.map