capix-mcp 2.1.1 → 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 (41) hide show
  1. package/README.md +51 -72
  2. package/dist/prompts.d.ts +6 -1
  3. package/dist/prompts.d.ts.map +1 -1
  4. package/dist/prompts.js +41 -75
  5. package/dist/prompts.js.map +1 -1
  6. package/dist/route-families.d.ts +47 -0
  7. package/dist/route-families.d.ts.map +1 -0
  8. package/dist/route-families.js +96 -0
  9. package/dist/route-families.js.map +1 -0
  10. package/dist/server.d.ts +5 -5
  11. package/dist/server.js +8 -8
  12. package/dist/server.js.map +1 -1
  13. package/dist/tools/define-tool.d.ts +6 -0
  14. package/dist/tools/define-tool.d.ts.map +1 -1
  15. package/dist/tools/define-tool.js +1 -0
  16. package/dist/tools/define-tool.js.map +1 -1
  17. package/dist/tools/factory.d.ts +42 -0
  18. package/dist/tools/factory.d.ts.map +1 -0
  19. package/dist/tools/factory.js +363 -0
  20. package/dist/tools/factory.js.map +1 -0
  21. package/dist/tools/generate.d.ts +108 -0
  22. package/dist/tools/generate.d.ts.map +1 -0
  23. package/dist/tools/generate.js +149 -0
  24. package/dist/tools/generate.js.map +1 -0
  25. package/dist/tools/infra-context.d.ts +15 -11
  26. package/dist/tools/infra-context.d.ts.map +1 -1
  27. package/dist/tools/infra-context.js +43 -92
  28. package/dist/tools/infra-context.js.map +1 -1
  29. package/dist/tools/memes.d.ts +31 -0
  30. package/dist/tools/memes.d.ts.map +1 -0
  31. package/dist/tools/memes.js +175 -0
  32. package/dist/tools/memes.js.map +1 -0
  33. package/dist/tools.d.ts +47 -7
  34. package/dist/tools.d.ts.map +1 -1
  35. package/dist/tools.js +341 -891
  36. package/dist/tools.js.map +1 -1
  37. package/dist/types.d.ts +17 -2
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +0 -2
  40. package/dist/types.js.map +1 -1
  41. package/package.json +3 -3
package/dist/tools.js CHANGED
@@ -1,5 +1,11 @@
1
1
  /**
2
- * Capix MCP Server — all 64 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,68 +17,59 @@
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) · infra-context (5)
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";
20
60
  import { defineTool } from "./tools/define-tool.js";
61
+ import { defineGeneratedTool } from "./tools/generate.js";
62
+ import { factoryTools } from "./tools/factory.js";
21
63
  import { infraContextTools } from "./tools/infra-context.js";
64
+ import { memeImageTools } from "./tools/memes.js";
22
65
  // ===========================================================================
23
66
  // Reusable Zod fragments
24
67
  // ===========================================================================
25
68
  const iso8601 = z.string().datetime().or(z.string());
26
- const moneyShape = z.object({
27
- amount: z.string().describe("Integer minor units (e.g. lamports, micro-USDC, cents)."),
28
- asset: z.enum(["SOL", "USDC", "USD-credit"]),
29
- scale: z.number().int(),
30
- }).describe("Integer minor/native money amount serialized as a JSON string.");
31
69
  const deploymentIdShape = z.string().describe("Canonical deployment id (dep_…).");
32
70
  const operationIdShape = z.string().describe("Canonical operation id (op_…).");
33
- const quoteIdShape = z.string().describe("Canonical quote id (qt_…) from capix_compute_quote / capix_model_quote.");
34
- const siteIdShape = z.string().describe("Capix website project id.");
35
- const listResultShape = {
36
- entries: z.array(z.record(z.unknown())).optional().describe("Paginated item array."),
37
- nextCursor: z.string().optional().describe("Opaque cursor for the next page; absent when exhausted."),
38
- fetchedAt: iso8601.optional().describe("ISO8601 fetch timestamp."),
39
- };
40
- const quoteResultShape = {
41
- quoteId: quoteIdShape.optional(),
42
- price: moneyShape.optional(),
43
- providerCostBasis: moneyShape.optional(),
44
- fees: moneyShape.optional(),
45
- margin: moneyShape.optional(),
46
- provider: z.string().optional(),
47
- region: z.string().optional(),
48
- expiresAt: iso8601.optional(),
49
- feasible: z.boolean().optional(),
50
- };
51
- const deploymentResultShape = {
52
- deploymentId: deploymentIdShape.optional(),
53
- operationId: operationIdShape.optional(),
54
- phase: z.string().optional().describe("DeploymentPhase after mutation."),
55
- quoteId: quoteIdShape.optional(),
56
- holdId: z.string().optional().describe("Ledger hold that funds the deployment."),
57
- estimatedCost: moneyShape.optional(),
58
- acceptedAt: iso8601.optional(),
59
- };
60
- const networkResourceShape = {
61
- resourceId: z.string().optional(),
62
- kind: z.string().optional().describe("vpc | endpoint | port_forward | private_connection | dedicated_ip."),
63
- deploymentId: deploymentIdShape.optional(),
64
- status: z.enum(["provisioning", "ready", "failed", "destroyed"]).optional(),
65
- endpoint: z.string().optional(),
66
- acceptedAt: iso8601.optional(),
67
- };
68
- const siteResultShape = {
69
- siteId: siteIdShape.optional(),
70
- deploymentId: z.string().optional(),
71
- url: z.string().optional(),
72
- status: z.enum(["queued", "building", "ready", "failed"]).optional(),
73
- estimatedCost: moneyShape.optional(),
74
- acceptedAt: iso8601.optional(),
75
- };
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.");
76
73
  // ===========================================================================
77
74
  // defineTool — imported from tools/define-tool.ts (shared with sibling tool
78
75
  // modules such as tools/infra-context.ts; see that file for the doc comment).
@@ -87,982 +84,435 @@ function asStr(value, field) {
87
84
  }
88
85
  return value;
89
86
  }
90
- /** Call the client, passing the approval token through for billable tools. */
91
- async function callBillable(client, approvalToken, path, body) {
92
- if (!approvalToken) {
93
- const err = new Error("approvalToken is required for this billable tool (§5)");
94
- err.capixCode = "approval_required";
95
- err.status = 402;
96
- throw err;
97
- }
98
- return client.post(path, body, { idempotent: true, approvalToken });
99
- }
100
87
  // ===========================================================================
101
- // 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.)
102
90
  // ===========================================================================
103
91
  const discoveryTools = [
104
92
  defineTool({
105
93
  name: "capix_account",
106
- 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.",
107
95
  scope: "discovery",
108
96
  ...READ_ONLY,
97
+ routePath: "/api/v1/account",
109
98
  inputShape: {},
110
99
  outputShape: {
111
- accountId: z.string().optional(),
112
- walletAddress: z.string().optional(),
113
- balance: moneyShape.optional(),
114
- spendingLimitMinor: z.string().optional(),
115
- activeDeployments: z.number().int().optional(),
116
- activeAgents: z.number().int().optional(),
117
- 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(),
118
106
  },
119
107
  handler: async (_args, { client }) => client.get("/api/v1/account"),
120
108
  }),
121
109
  defineTool({
122
110
  name: "capix_balance",
123
- 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.",
124
112
  scope: "discovery",
125
113
  ...READ_ONLY,
114
+ routePath: "/api/v1/billing",
126
115
  inputShape: {
127
116
  asset: z.enum(["SOL", "USDC", "USD-credit"]).optional().describe("Optional asset filter."),
128
117
  },
129
118
  outputShape: {
119
+ ok: z.boolean().optional(),
130
120
  accountId: z.string().optional(),
131
- asset: z.enum(["SOL", "USDC", "USD-credit"]).optional(),
132
- available: z.string().optional(),
133
- held: z.string().optional(),
134
- total: z.string().optional(),
135
- 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(),
136
125
  },
137
- handler: async (args, { client }) => client.get("/api/v1/account/balance", {
126
+ handler: async (args, { client }) => client.get("/api/v1/billing", {
138
127
  asset: args.asset,
139
128
  }),
140
129
  }),
141
- defineTool({
142
- name: "capix_projects",
143
- description: "List projects visible to the authenticated account. Read-only.",
144
- scope: "discovery",
145
- ...READ_ONLY,
146
- inputShape: {
147
- limit: z.number().int().min(1).max(200).default(50).describe("Max projects to return."),
148
- },
149
- outputShape: listResultShape,
150
- handler: async (args, { client }) => client.get("/api/v1/projects", { limit: args.limit }),
151
- }),
152
130
  defineTool({
153
131
  name: "capix_compute_catalog",
154
- description: "List the live compute capability catalog (provider / region / tier / price / availability). 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.",
155
134
  scope: "discovery",
156
135
  ...READ_ONLY,
157
- inputShape: {
158
- region: z.string().optional().describe("Optional region filter."),
159
- gpu: z.boolean().optional().describe("Restrict to GPU-capable entries."),
160
- },
161
- outputShape: {
162
- entries: z.array(z.record(z.unknown())).optional(),
163
- catalogVersion: z.string().optional(),
164
- fetchedAt: iso8601.optional(),
165
- },
166
- handler: async (args, { client }) => client.get("/api/v1/catalog/compute", {
167
- region: args.region,
168
- gpu: args.gpu,
169
- }),
170
- }),
171
- defineTool({
172
- name: "capix_model_catalog",
173
- description: "List the live model endpoint catalog (model id, context length, price per 1k tokens). Read-only.",
174
- scope: "discovery",
175
- ...READ_ONLY,
176
- inputShape: {
177
- provider: z.string().optional().describe("Optional provider filter."),
178
- },
136
+ routePath: "/api/v1/catalog/capabilities",
137
+ inputShape: {},
179
138
  outputShape: {
180
- entries: z.array(z.record(z.unknown())).optional(),
181
- catalogVersion: z.string().optional(),
182
- 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(),
183
144
  },
184
- handler: async (args, { client }) => client.get("/api/v1/catalog/models", {
185
- provider: args.provider,
186
- }),
145
+ handler: async (_args, { client }) => client.get("/api/v1/catalog/capabilities"),
187
146
  }),
188
147
  defineTool({
189
148
  name: "capix_network_status",
190
- 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.",
191
151
  scope: "discovery",
192
152
  ...READ_ONLY,
153
+ routePath: "/api/v1/health",
193
154
  inputShape: {},
194
155
  outputShape: {
195
- gateway: z.string().optional(),
196
- status: z.enum(["ok", "degraded", "outage"]).optional(),
197
- providers: z.array(z.record(z.unknown())).optional(),
198
- lanes: z.array(z.record(z.unknown())).optional(),
199
- emergencyFlags: z.array(z.record(z.unknown())).optional(),
200
- 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(),
201
162
  },
202
- handler: async (_args, { client }) => client.get("/api/v1/network/status"),
163
+ handler: async (_args, { client }) => client.get("/api/v1/health"),
203
164
  }),
204
- defineTool({
165
+ defineGeneratedTool({
205
166
  name: "capix_deployments",
206
167
  description: "List deployments for the account/project with phase + allocation state. Read-only.",
207
168
  scope: "discovery",
208
169
  ...READ_ONLY,
209
- inputShape: {
210
- limit: z.number().int().min(1).max(200).default(50).describe("Max deployments to return."),
211
- 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(),
212
182
  },
213
- outputShape: listResultShape,
214
- handler: async (args, { client }) => client.get("/api/v1/deployments", {
215
- limit: args.limit,
216
- phase: args.phase,
217
- }),
218
183
  }),
219
- defineTool({
184
+ defineGeneratedTool({
220
185
  name: "capix_receipts",
221
- 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.",
222
187
  scope: "discovery",
223
188
  ...READ_ONLY,
224
- inputShape: {
225
- limit: z.number().int().min(1).max(200).default(50),
226
- 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(),
227
197
  },
228
- outputShape: listResultShape,
229
- handler: async (args, { client }) => client.get("/api/v1/receipts", {
230
- limit: args.limit,
231
- since: args.since,
232
- }),
233
- }),
234
- defineTool({
235
- name: "capix_attestations",
236
- description: "List attestation evidence records for the account (TEE / zkVM). Read-only.",
237
- scope: "discovery",
238
- ...READ_ONLY,
239
- inputShape: {
240
- limit: z.number().int().min(1).max(200).default(50),
241
- 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(),
242
202
  },
243
- outputShape: listResultShape,
244
- handler: async (args, { client }) => client.get("/api/v1/attestations", {
245
- limit: args.limit,
246
- kind: args.kind,
247
- }),
248
203
  }),
249
204
  ];
250
205
  // ===========================================================================
251
- // 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.
252
211
  // ===========================================================================
253
- const computeSpecShape = {
254
- workloadType: z.string().describe("e.g. replicated_service.v1, private_inference.v1, agent_run.v1."),
255
- cpu: z.number().int().min(1),
256
- ramMb: z.number().int().min(128),
257
- storageGb: z.number().int().min(0),
258
- gpu: z.number().int().min(0).optional(),
259
- vramMb: z.number().int().min(0).optional(),
260
- region: z.string().optional(),
261
- imageDigest: z.string().optional().describe("Pinned image digest."),
262
- maxPriceUsdHourly: z.number().optional(),
263
- maxDurationHours: z.number().optional(),
264
- };
265
- const modelSpecShape = {
266
- modelId: z.string(),
267
- quantization: z.string().optional(),
268
- contextLength: z.number().int().min(1).optional(),
269
- gpuType: z.string().optional(),
270
- gpuCount: z.number().int().min(1).optional(),
271
- region: z.string().optional(),
272
- maxPriceUsdHourly: z.number().optional(),
273
- maxConcurrency: z.number().int().min(1).optional(),
274
- };
275
- const stackManifestShape = {
276
- name: z.string(),
277
- services: z.array(z.record(z.unknown())),
278
- network: z.record(z.unknown()).optional(),
279
- 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(),
280
229
  };
281
230
  const planningTools = [
282
- defineTool({
283
- name: "capix_compute_plan",
284
- description: "Plan a compute deployment: pick provider/region/spec for the requested workload shape. Read-only.",
285
- scope: "planning",
286
- ...READ_ONLY,
287
- inputShape: computeSpecShape,
288
- outputShape: {
289
- feasible: z.boolean(),
290
- provider: z.string().optional(),
291
- region: z.string().optional(),
292
- cpu: z.number().int().optional(),
293
- ramMb: z.number().int().optional(),
294
- storageGb: z.number().int().optional(),
295
- gpu: z.number().int().optional(),
296
- estimatedCostHourly: moneyShape.optional(),
297
- estimatedCostTotal: moneyShape.optional(),
298
- notes: z.string().optional(),
299
- },
300
- handler: async (args, { client }) => client.post("/api/v1/planning/compute/plan", args),
301
- }),
302
- defineTool({
231
+ defineGeneratedTool({
303
232
  name: "capix_compute_quote",
304
- 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.",
305
235
  scope: "planning",
306
236
  ...READ_ONLY,
307
- inputShape: computeSpecShape,
308
- outputShape: quoteResultShape,
309
- handler: async (args, { client }) => client.post("/api/v1/planning/compute/quote", args),
310
- }),
311
- defineTool({
312
- name: "capix_model_plan",
313
- description: "Plan a model endpoint for the requested model hosting shape. Read-only.",
314
- scope: "planning",
315
- ...READ_ONLY,
316
- inputShape: modelSpecShape,
317
- outputShape: {
318
- feasible: z.boolean(),
319
- provider: z.string().optional(),
320
- region: z.string().optional(),
321
- gpu: z.number().int().optional(),
322
- estimatedCostHourly: moneyShape.optional(),
323
- estimatedCostTotal: moneyShape.optional(),
324
- 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(),
325
258
  },
326
- handler: async (args, { client }) => client.post("/api/v1/planning/model/plan", args),
327
- }),
328
- defineTool({
329
- name: "capix_model_quote",
330
- description: "Get a canonical quote for a model endpoint plan. Read-only.",
331
- scope: "planning",
332
- ...READ_ONLY,
333
- inputShape: modelSpecShape,
334
259
  outputShape: quoteResultShape,
335
- handler: async (args, { client }) => client.post("/api/v1/planning/model/quote", args),
336
- }),
337
- defineTool({
338
- name: "capix_stack_validate",
339
- description: "Validate a stack manifest (services, dependencies, network) without provisioning. Read-only.",
340
- scope: "planning",
341
- ...READ_ONLY,
342
- inputShape: stackManifestShape,
343
- outputShape: {
344
- valid: z.boolean(),
345
- errors: z.array(z.string()).optional(),
346
- warnings: z.array(z.string()).optional(),
347
- },
348
- handler: async (args, { client }) => client.post("/api/v1/planning/stack/validate", args),
349
260
  }),
350
261
  defineTool({
351
- name: "capix_stack_plan",
352
- 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.",
353
266
  scope: "planning",
354
267
  ...READ_ONLY,
355
- inputShape: stackManifestShape,
356
- outputShape: {
357
- feasible: z.boolean(),
358
- services: z.array(z.record(z.unknown())).optional(),
359
- aggregateQuote: z.record(z.unknown()).optional(),
360
- 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(),
361
280
  },
362
- 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 }),
363
297
  }),
364
298
  ];
365
299
  // ===========================================================================
366
- // 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).
367
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
+ };
368
316
  const lifecycleTools = [
369
- defineTool({
317
+ defineGeneratedTool({
370
318
  name: "capix_deploy",
371
- description: "Deploy a workload against a canonical quote. Takes a ledger hold + provisions resources. Billable; 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.",
372
322
  scope: "lifecycle",
373
323
  ...BILLABLE,
374
- inputShape: {
375
- quoteId: quoteIdShape.describe("Canonical quote from capix_compute_quote / capix_model_quote."),
376
- workloadType: z.string(),
377
- region: z.string().optional(),
378
- imageDigest: z.string().optional(),
379
- durationHours: z.number().min(1).optional(),
380
- env: z.record(z.string()).optional(),
381
- ingress: z.array(z.number().int()).optional(),
324
+ method: "POST",
325
+ path: "/api/v1/deployments",
326
+ input: {
327
+ quoteId: quoteIdShape,
328
+ projectId: z.string().optional(),
382
329
  },
383
330
  outputShape: deploymentResultShape,
384
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/lifecycle/deployments", args),
385
331
  }),
386
- defineTool({
387
- name: "capix_start",
388
- description: "Start a stopped deployment. Billable (resumes metering); requires approval.",
389
- scope: "lifecycle",
390
- ...BILLABLE,
391
- inputShape: {
392
- deploymentId: deploymentIdShape,
393
- },
394
- outputShape: deploymentResultShape,
395
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/start`),
396
- }),
397
- defineTool({
398
- name: "capix_stop",
399
- description: "Stop a running deployment (halts metering, keeps allocation). Billable (minimal); requires approval.",
400
- scope: "lifecycle",
401
- ...BILLABLE,
402
- inputShape: {
403
- deploymentId: deploymentIdShape,
404
- },
405
- outputShape: deploymentResultShape,
406
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/stop`),
407
- }),
408
- defineTool({
409
- name: "capix_restart",
410
- description: "Restart a deployment (stop + start cycle). Billable; requires approval.",
411
- scope: "lifecycle",
412
- ...BILLABLE,
413
- inputShape: {
414
- deploymentId: deploymentIdShape,
415
- },
416
- outputShape: deploymentResultShape,
417
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/restart`),
418
- }),
419
- defineTool({
332
+ defineGeneratedTool({
420
333
  name: "capix_delete",
421
- 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.",
422
336
  scope: "lifecycle",
423
337
  ...BILLABLE,
424
- inputShape: {
425
- deploymentId: deploymentIdShape,
426
- reason: z.string().optional(),
338
+ method: "DELETE",
339
+ path: "/api/v1/deployments/:id",
340
+ input: {
341
+ id: deploymentIdShape,
427
342
  },
428
343
  outputShape: deploymentResultShape,
429
- handler: async (args, { client, ctx }) => {
430
- const id = asStr(args.deploymentId, "deploymentId");
431
- const res = await callBillable(client, ctx.approvalToken, `/api/v1/lifecycle/deployments/${encodeURIComponent(id)}`, args.reason ? { reason: args.reason } : undefined);
432
- // DELETE semantics: the canonical route is DELETE; some gateways express
433
- // the mutation via POST with idempotency. Use the client delete path with
434
- // an approval header via a light POST-to-delete fallback.
435
- return res;
436
- },
437
344
  }),
438
- defineTool({
439
- name: "capix_extend",
440
- description: "Extend a running deployment by additional hours against a fresh hold. Billable; requires approval.",
441
- scope: "lifecycle",
442
- ...BILLABLE,
443
- inputShape: {
444
- deploymentId: deploymentIdShape,
445
- durationHours: z.number().min(1),
446
- quoteId: quoteIdShape.optional().describe("Canonical quote for the extension cost."),
447
- },
448
- outputShape: deploymentResultShape,
449
- 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 }),
450
- }),
451
- defineTool({
345
+ defineGeneratedTool({
452
346
  name: "capix_cancel",
453
- 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.",
454
349
  scope: "lifecycle",
455
350
  ...APPROVAL_ONLY,
456
- inputShape: {
351
+ method: "POST",
352
+ path: "/api/v1/operations/:operationId",
353
+ input: {
457
354
  operationId: operationIdShape,
458
- reason: z.string().optional(),
459
- },
460
- outputShape: {
461
- operationId: operationIdShape.optional(),
462
- status: z.string().optional(),
463
- acceptedAt: iso8601.optional(),
464
- },
465
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/operations/${encodeURIComponent(asStr(args.operationId, "operationId"))}/cancel`, args.reason ? { reason: args.reason } : undefined),
466
- }),
467
- ];
468
- // ===========================================================================
469
- // Networking tools (8) — billable mutations + read-only inspection.
470
- // ===========================================================================
471
- const networkingTools = [
472
- defineTool({
473
- name: "capix_create_vpc",
474
- description: "Create a VPC for a project. Billable; requires approval.",
475
- scope: "networking",
476
- ...BILLABLE,
477
- inputShape: {
478
- cidr: z.string().describe("VPC CIDR block, e.g. 10.0.0.0/16."),
479
- region: z.string(),
480
- name: z.string().optional(),
481
- },
482
- outputShape: networkResourceShape,
483
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/vpcs", args),
484
- }),
485
- defineTool({
486
- name: "capix_create_endpoint",
487
- description: "Create a public endpoint for a deployment. Billable; requires approval.",
488
- scope: "networking",
489
- ...BILLABLE,
490
- inputShape: {
491
- deploymentId: deploymentIdShape,
492
- port: z.number().int().min(1).max(65535),
493
- protocol: z.enum(["http", "https", "tcp"]).default("http"),
494
- hostname: z.string().optional().describe("Optional requested hostname."),
495
- },
496
- outputShape: networkResourceShape,
497
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/endpoints", args),
498
- }),
499
- defineTool({
500
- name: "capix_expose_port",
501
- description: "Expose a port (ingress) to the public internet. Billable; requires approval.",
502
- scope: "networking",
503
- ...BILLABLE,
504
- inputShape: {
505
- deploymentId: deploymentIdShape,
506
- port: z.number().int().min(1).max(65535),
507
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
508
- allowedCidrs: z.array(z.string()).optional().describe("Optional ingress allow-list."),
509
- },
510
- outputShape: networkResourceShape,
511
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/ports/expose", args),
512
- }),
513
- defineTool({
514
- name: "capix_close_port",
515
- description: "Close a previously exposed port. Non-billable soft mutation; requires approval.",
516
- scope: "networking",
517
- ...APPROVAL_ONLY,
518
- inputShape: {
519
- deploymentId: deploymentIdShape,
520
- port: z.number().int().min(1).max(65535),
521
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
522
- },
523
- outputShape: networkResourceShape,
524
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/ports/close", args),
525
- }),
526
- defineTool({
527
- name: "capix_inspect_routes",
528
- description: "Inspect the routing table for a deployment. Read-only.",
529
- scope: "networking",
530
- ...READ_ONLY,
531
- inputShape: {
532
- deploymentId: deploymentIdShape,
533
- },
534
- outputShape: {
535
- deploymentId: deploymentIdShape.optional(),
536
- routes: z.array(z.record(z.unknown())).optional(),
537
- fetchedAt: iso8601.optional(),
538
- },
539
- handler: async (args, { client }) => client.get(`/api/v1/networking/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/routes`),
540
- }),
541
- defineTool({
542
- name: "capix_create_private_connection",
543
- description: "Create a private endpoint connection (raw private endpoint). Billable; requires approval.",
544
- scope: "networking",
545
- ...BILLABLE,
546
- inputShape: {
547
- deploymentId: deploymentIdShape,
548
- service: z.string().describe("Target service identifier."),
549
- port: z.number().int().min(1).max(65535).optional(),
550
- },
551
- outputShape: networkResourceShape,
552
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/private-connections", args),
553
- }),
554
- defineTool({
555
- name: "capix_request_dedicated_ip",
556
- description: "Request a dedicated (non-shared) IP for a deployment. Paid; requires approval.",
557
- scope: "networking",
558
- ...BILLABLE,
559
- inputShape: {
560
- deploymentId: deploymentIdShape,
561
- region: z.string().optional(),
562
- quoteId: quoteIdShape.describe("Canonical quote for the dedicated IP."),
563
- },
564
- outputShape: networkResourceShape,
565
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/dedicated-ips", args),
566
- }),
567
- defineTool({
568
- name: "capix_port_forward",
569
- description: "Set up port forwarding between a deployment port and a host port. Billable; requires approval.",
570
- scope: "networking",
571
- ...BILLABLE,
572
- inputShape: {
573
- deploymentId: deploymentIdShape,
574
- sourcePort: z.number().int().min(1).max(65535),
575
- targetPort: z.number().int().min(1).max(65535),
576
- protocol: z.enum(["tcp", "udp"]).default("tcp"),
577
- },
578
- outputShape: networkResourceShape,
579
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/networking/port-forwards", args),
580
- }),
581
- ];
582
- // ===========================================================================
583
- // Testing tools (6) — disposable envs, inspection, bounded commands, cleanup.
584
- // ===========================================================================
585
- const testingTools = [
586
- defineTool({
587
- name: "capix_create_test_env",
588
- description: "Create a disposable test environment from a spec. Billable; requires approval.",
589
- scope: "testing",
590
- ...BILLABLE,
591
- inputShape: {
592
- name: z.string(),
593
- workloadType: z.string(),
594
- cpu: z.number().int().min(1),
595
- ramMb: z.number().int().min(128),
596
- region: z.string().optional(),
597
- imageDigest: z.string().optional(),
598
- ttlMinutes: z.number().int().min(1).max(1440).default(60).describe("Auto-destroy TTL."),
599
- quoteId: quoteIdShape.optional().describe("Canonical quote for the env."),
600
- },
601
- outputShape: {
602
- envId: z.string().optional(),
603
- status: z.enum(["provisioning", "ready", "failed"]).optional(),
604
- provider: z.string().optional(),
605
- region: z.string().optional(),
606
- endpoint: z.string().optional(),
607
- provisionedAt: iso8601.optional(),
608
- },
609
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/testing/environments", args),
610
- }),
611
- defineTool({
612
- name: "capix_run_health_checks",
613
- description: "Run health checks (liveness/readiness probes) on a deployment. Read-only.",
614
- scope: "testing",
615
- ...READ_ONLY,
616
- inputShape: {
617
- deploymentId: deploymentIdShape,
618
- },
619
- outputShape: {
620
- deploymentId: deploymentIdShape.optional(),
621
- healthy: z.boolean().optional(),
622
- checks: z.array(z.record(z.unknown())).optional(),
623
- ranAt: iso8601.optional(),
624
- },
625
- handler: async (args, { client }) => client.get(`/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/health`),
626
- }),
627
- defineTool({
628
- name: "capix_run_bounded_command",
629
- description: "Run an allow-listed, time-bounded command on a deployment node. Requires approval (may execute code).",
630
- scope: "testing",
631
- ...APPROVAL_ONLY,
632
- inputShape: {
633
- deploymentId: deploymentIdShape,
634
- command: z.string().describe("Command string from the allow-list."),
635
- timeoutSeconds: z.number().int().min(1).max(600).default(60),
636
- expectedExitCode: z.number().int().default(0),
637
- },
638
- outputShape: {
639
- deploymentId: deploymentIdShape.optional(),
640
- results: z.array(z.record(z.unknown())).optional(),
641
- completedAt: iso8601.optional(),
642
- },
643
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/commands`, args),
644
- }),
645
- defineTool({
646
- name: "capix_inspect_logs",
647
- description: "Inspect deployment logs. Read-only.",
648
- scope: "testing",
649
- ...READ_ONLY,
650
- inputShape: {
651
- deploymentId: deploymentIdShape,
652
- limit: z.number().int().min(1).max(1000).default(200),
653
- since: iso8601.optional(),
654
- level: z.enum(["debug", "info", "warn", "error"]).optional(),
655
- },
656
- outputShape: {
657
- resourceId: z.string().optional(),
658
- lines: z.array(z.record(z.unknown())).optional(),
659
- truncated: z.boolean().optional(),
660
- },
661
- 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 }),
662
- }),
663
- defineTool({
664
- name: "capix_inspect_metrics",
665
- description: "Inspect deployment metrics. Read-only.",
666
- scope: "testing",
667
- ...READ_ONLY,
668
- inputShape: {
669
- deploymentId: deploymentIdShape,
670
- windowMinutes: z.number().int().min(1).max(10080).default(60),
671
- names: z.array(z.string()).optional().describe("Optional metric name filter."),
672
- },
673
- outputShape: {
674
- resourceId: z.string().optional(),
675
- metrics: z.array(z.record(z.unknown())).optional(),
676
- },
677
- handler: async (args, { client }) => client.get(`/api/v1/testing/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/metrics`, { windowMinutes: args.windowMinutes, names: args.names }),
678
- }),
679
- defineTool({
680
- name: "capix_destroy_task_resources",
681
- description: "Destroy all resources scoped to a task (cleanup safety gate §18). Destructive; requires approval.",
682
- scope: "testing",
683
- ...APPROVAL_ONLY,
684
- inputShape: {
685
- resourceId: z.string().describe("Task-scoped resource id to destroy."),
686
- reason: z.string().optional(),
687
- },
688
- outputShape: {
689
- resourceId: z.string().optional(),
690
- status: z.enum(["destroyed", "failed", "not_found"]).optional(),
691
- destroyedAt: iso8601.optional(),
692
- },
693
- handler: async (args, { client, ctx }) => {
694
- const id = asStr(args.resourceId, "resourceId");
695
- return callBillable(client, ctx.approvalToken, `/api/v1/testing/environments/${encodeURIComponent(id)}`, args.reason ? { reason: args.reason } : undefined);
696
355
  },
356
+ outputShape: deploymentResultShape,
697
357
  }),
698
358
  ];
699
359
  // ===========================================================================
700
- // 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).
701
367
  // ===========================================================================
702
368
  const verificationTools = [
703
- defineTool({
704
- name: "capix_fetch_attestation",
705
- description: "Fetch attestation evidence for a deployment (TEE / zkVM). Read-only.",
706
- scope: "verification",
707
- ...READ_ONLY,
708
- inputShape: {
709
- deploymentId: deploymentIdShape,
710
- },
711
- outputShape: {
712
- attestationId: z.string().optional(),
713
- kind: z.enum(["tee", "zkvm"]).optional(),
714
- resourceId: z.string().optional(),
715
- evidence: z.string().optional(),
716
- measurement: z.string().optional(),
717
- fetchedAt: iso8601.optional(),
718
- },
719
- handler: async (args, { client }) => client.get(`/api/v1/verification/deployments/${encodeURIComponent(asStr(args.deploymentId, "deploymentId"))}/attestation`),
720
- }),
721
- defineTool({
722
- name: "capix_verify_attestation",
723
- description: "Verify an attestation against the expected reference measurement. Read-only.",
724
- scope: "verification",
725
- ...READ_ONLY,
726
- inputShape: {
727
- attestationId: z.string(),
728
- expectedMeasurement: z.string().optional().describe("Optional pinned reference measurement."),
729
- },
730
- outputShape: {
731
- subjectId: z.string().optional(),
732
- kind: z.enum(["tee", "zkvm", "measurement", "receipt"]).optional(),
733
- verified: z.boolean().optional(),
734
- reason: z.string().optional(),
735
- verifier: z.string().optional(),
736
- verifiedAt: iso8601.optional(),
737
- },
738
- handler: async (args, { client }) => client.post(`/api/v1/verification/attestations/${encodeURIComponent(asStr(args.attestationId, "attestationId"))}/verify`, { expectedMeasurement: args.expectedMeasurement }),
739
- }),
740
- defineTool({
741
- name: "capix_fetch_proof",
742
- description: "Fetch a zkVM proof artifact for a workload. Read-only.",
743
- scope: "verification",
744
- ...READ_ONLY,
745
- inputShape: {
746
- workloadId: z.string(),
747
- },
748
- outputShape: {
749
- proofId: z.string().optional(),
750
- workloadId: z.string().optional(),
751
- proofSystem: z.string().optional(),
752
- artifactRef: z.string().optional(),
753
- publicInputs: z.string().optional(),
754
- fetchedAt: iso8601.optional(),
755
- },
756
- handler: async (args, { client }) => client.get(`/api/v1/verification/workloads/${encodeURIComponent(asStr(args.workloadId, "workloadId"))}/proof`),
757
- }),
758
- defineTool({
759
- name: "capix_verify_proof",
760
- description: "Verify a zkVM proof artifact against its public inputs. Read-only.",
761
- scope: "verification",
762
- ...READ_ONLY,
763
- inputShape: {
764
- proofId: z.string(),
765
- },
766
- outputShape: {
767
- subjectId: z.string().optional(),
768
- kind: z.enum(["tee", "zkvm", "measurement", "receipt"]).optional(),
769
- verified: z.boolean().optional(),
770
- reason: z.string().optional(),
771
- verifier: z.string().optional(),
772
- verifiedAt: iso8601.optional(),
773
- },
774
- handler: async (args, { client }) => client.post(`/api/v1/verification/proofs/${encodeURIComponent(asStr(args.proofId, "proofId"))}/verify`),
775
- }),
776
- defineTool({
777
- name: "capix_inspect_measurement",
778
- description: "Inspect the recorded measurement of a workload (used to pin expected TEE state). Read-only.",
779
- scope: "verification",
780
- ...READ_ONLY,
781
- inputShape: {
782
- workloadId: z.string(),
783
- },
784
- outputShape: {
785
- workloadId: z.string().optional(),
786
- measurement: z.string().optional(),
787
- measurementAlgorithm: z.string().optional(),
788
- imageDigest: z.string().optional(),
789
- recordedAt: iso8601.optional(),
790
- },
791
- handler: async (args, { client }) => client.get(`/api/v1/verification/workloads/${encodeURIComponent(asStr(args.workloadId, "workloadId"))}/measurement`),
792
- }),
793
- defineTool({
369
+ defineGeneratedTool({
794
370
  name: "capix_inspect_receipt",
795
- 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.",
796
373
  scope: "verification",
797
374
  ...READ_ONLY,
798
- inputShape: {
799
- 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)."),
800
379
  },
801
380
  outputShape: {
802
- receiptId: z.string().optional(),
803
- approvalStatus: z.string().optional(),
804
- cost: z.record(z.unknown()).optional(),
805
- settlement: z.record(z.unknown()).optional(),
806
- 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(),
807
385
  },
808
- handler: async (args, { client }) => client.get(`/api/v1/verification/receipts/${encodeURIComponent(asStr(args.receiptId, "receiptId"))}`),
809
386
  }),
810
387
  ];
811
388
  // ===========================================================================
812
- // 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).
813
411
  // ===========================================================================
814
- const repoOptShape = {
815
- repoUrl: z.string().describe("Git repository URL."),
816
- branch: z.string().default("main"),
817
- subPath: z.string().optional().describe("Optional monorepo subpath."),
818
- framework: z.string().optional().describe("Optional pinned framework."),
819
- };
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.");
820
416
  const websiteTools = [
821
- defineTool({
822
- name: "capix_website_project_string_check",
823
- description: "Check a repository string for framework/dep compatibility with Capix hosting. Read-only.",
824
- scope: "website",
825
- ...READ_ONLY,
826
- inputShape: {
827
- repoUrl: z.string(),
828
- },
829
- outputShape: {
830
- ok: z.boolean().optional(),
831
- framework: z.string().optional(),
832
- notes: z.string().optional(),
833
- },
834
- handler: async (args, { client }) => client.post("/api/v1/website/check-project-string", { repoUrl: asStr(args.repoUrl, "repoUrl") }),
835
- }),
836
- defineTool({
417
+ defineGeneratedTool({
837
418
  name: "capix_website_create",
838
- 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.",
839
421
  scope: "website",
840
422
  ...BILLABLE,
841
- inputShape: repoOptShape,
842
- outputShape: siteResultShape,
843
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, "/api/v1/website/sites", args),
844
- }),
845
- defineTool({
846
- name: "capix_website_detect",
847
- description: "Detect the framework/build settings of a repo without provisioning. Read-only.",
848
- scope: "website",
849
- ...READ_ONLY,
850
- inputShape: {
851
- repoUrl: z.string(),
852
- 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(),
853
430
  },
854
431
  outputShape: {
855
- framework: z.string().optional(),
856
- buildCommand: z.string().optional(),
857
- outputDir: z.string().optional(),
858
- nodeVersion: z.string().optional(),
859
- detectedAt: iso8601.optional(),
432
+ website: websiteObjectShape.optional(),
860
433
  },
861
- handler: async (args, { client }) => client.post("/api/v1/website/detect", {
862
- repoUrl: asStr(args.repoUrl, "repoUrl"),
863
- branch: args.branch,
864
- }),
865
434
  }),
866
- defineTool({
867
- name: "capix_website_plan",
868
- 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.",
869
438
  scope: "website",
870
439
  ...READ_ONLY,
871
- inputShape: {
872
- siteId: siteIdShape.optional(),
873
- repoUrl: z.string().optional(),
874
- branch: z.string().optional(),
875
- subPath: z.string().optional(),
876
- framework: z.string().optional(),
877
- },
440
+ method: "GET",
441
+ path: "/api/v1/websites",
442
+ input: {},
878
443
  outputShape: {
879
- feasible: z.boolean().optional(),
880
- plan: z.record(z.unknown()).optional(),
881
- notes: z.string().optional(),
444
+ websites: z.array(z.record(z.unknown())).optional(),
882
445
  },
883
- handler: async (args, { client }) => client.post("/api/v1/website/plan", args),
884
446
  }),
885
- defineTool({
886
- name: "capix_website_quote",
887
- 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.",
888
451
  scope: "website",
889
452
  ...READ_ONLY,
890
- inputShape: {
891
- repoUrl: z.string().optional(),
892
- branch: z.string().optional(),
893
- durationHours: z.number().optional(),
453
+ method: "GET",
454
+ path: "/api/v1/websites/:id",
455
+ input: {
456
+ id: siteIdShape,
894
457
  },
895
458
  outputShape: {
896
- quoteId: z.string().optional(),
897
- price: moneyShape.optional(),
898
- feasible: z.boolean().optional(),
899
- expiresAt: iso8601.optional(),
459
+ website: websiteObjectShape.optional(),
900
460
  },
901
- handler: async (args, { client }) => client.post("/api/v1/website/quote", args),
902
461
  }),
903
- defineTool({
904
- name: "capix_website_deploy",
905
- description: "Deploy a website build to production. Billable; requires approval.",
906
- scope: "website",
907
- ...BILLABLE,
908
- inputShape: {
909
- siteId: siteIdShape,
910
- branch: z.string().optional(),
911
- commit: z.string().optional(),
912
- quoteId: quoteIdShape.optional(),
913
- },
914
- outputShape: siteResultShape,
915
- 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 }),
916
- }),
917
- defineTool({
918
- name: "capix_website_preview",
919
- description: "Create a preview deployment for a branch. Billable; requires approval.",
920
- scope: "website",
921
- ...BILLABLE,
922
- inputShape: {
923
- siteId: siteIdShape,
924
- branch: z.string(),
925
- },
926
- outputShape: siteResultShape,
927
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/preview`, { branch: args.branch }),
928
- }),
929
- defineTool({
462
+ defineGeneratedTool({
930
463
  name: "capix_website_promote",
931
- description: "Promote a preview deployment to production. Billable; requires approval.",
932
- scope: "website",
933
- ...BILLABLE,
934
- inputShape: {
935
- siteId: siteIdShape,
936
- },
937
- outputShape: siteResultShape,
938
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/promote`),
939
- }),
940
- defineTool({
941
- name: "capix_website_rollback",
942
- 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.",
943
467
  scope: "website",
944
468
  ...BILLABLE,
945
- inputShape: {
946
- siteId: siteIdShape,
947
- targetDeploymentId: z.string().optional(),
948
- },
949
- outputShape: siteResultShape,
950
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/rollback`, { targetDeploymentId: args.targetDeploymentId }),
951
- }),
952
- defineTool({
953
- name: "capix_website_get",
954
- description: "Get a website project descriptor. Read-only.",
955
- scope: "website",
956
- ...READ_ONLY,
957
- inputShape: {
958
- siteId: siteIdShape,
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."),
959
474
  },
960
475
  outputShape: {
961
- siteId: siteIdShape.optional(),
962
- name: z.string().optional(),
963
- url: z.string().optional(),
964
- framework: z.string().optional(),
965
- status: z.string().optional(),
966
- createdAt: iso8601.optional(),
967
- },
968
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}`),
969
- }),
970
- defineTool({
971
- name: "capix_website_deployments",
972
- description: "List deployments for a website. Read-only.",
973
- scope: "website",
974
- ...READ_ONLY,
975
- inputShape: {
976
- siteId: siteIdShape,
977
- limit: z.number().int().min(1).max(200).default(50),
978
- },
979
- outputShape: listResultShape,
980
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/deployments`, { limit: args.limit }),
981
- }),
982
- defineTool({
983
- name: "capix_website_logs",
984
- description: "Inspect build/runtime logs for a website. Read-only.",
985
- scope: "website",
986
- ...READ_ONLY,
987
- inputShape: {
988
- siteId: siteIdShape,
989
- deploymentId: z.string().optional(),
990
- limit: z.number().int().min(1).max(1000).default(200),
991
- },
992
- outputShape: {
993
- lines: z.array(z.record(z.unknown())).optional(),
994
- truncated: z.boolean().optional(),
995
- },
996
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/logs`, { deploymentId: args.deploymentId, limit: args.limit }),
997
- }),
998
- defineTool({
999
- name: "capix_website_metrics",
1000
- description: "Inspect request/bandwidth metrics for a website. Read-only.",
1001
- scope: "website",
1002
- ...READ_ONLY,
1003
- inputShape: {
1004
- siteId: siteIdShape,
1005
- windowMinutes: z.number().int().min(1).max(10080).default(60),
1006
- },
1007
- outputShape: {
1008
- metrics: z.array(z.record(z.unknown())).optional(),
476
+ ok: z.boolean().optional(),
477
+ productionUrl: z.string().optional(),
478
+ releaseId: z.string().optional(),
1009
479
  },
1010
- handler: async (args, { client }) => client.get(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/metrics`, { windowMinutes: args.windowMinutes }),
1011
480
  }),
1012
- defineTool({
1013
- name: "capix_website_domain_add",
1014
- 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.",
1015
486
  scope: "website",
1016
487
  ...BILLABLE,
1017
- inputShape: {
1018
- siteId: siteIdShape,
1019
- domain: z.string(),
1020
- },
1021
- outputShape: siteResultShape,
1022
- handler: async (args, { client, ctx }) => callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains`, { domain: asStr(args.domain, "domain") }),
1023
- }),
1024
- defineTool({
1025
- name: "capix_website_domain_verify",
1026
- description: "Verify DNS ownership for a pending custom domain. Read-only.",
1027
- scope: "website",
1028
- ...READ_ONLY,
1029
- inputShape: {
1030
- siteId: siteIdShape,
1031
- 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."),
1032
493
  },
1033
494
  outputShape: {
1034
- verified: z.boolean().optional(),
1035
- records: z.array(z.record(z.unknown())).optional(),
1036
- reason: z.string().optional(),
1037
- },
1038
- handler: async (args, { client }) => client.post(`/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains/verify`, { domain: asStr(args.domain, "domain") }),
1039
- }),
1040
- defineTool({
1041
- name: "capix_website_domain_remove",
1042
- description: "Detach a custom domain from a website. Billable; requires approval.",
1043
- scope: "website",
1044
- ...BILLABLE,
1045
- inputShape: {
1046
- siteId: siteIdShape,
1047
- domain: z.string(),
1048
- },
1049
- outputShape: siteResultShape,
1050
- handler: async (args, { client, ctx }) => {
1051
- const res = await callBillable(client, ctx.approvalToken, `/api/v1/website/sites/${encodeURIComponent(asStr(args.siteId, "siteId"))}/domains/${encodeURIComponent(asStr(args.domain, "domain"))}`);
1052
- return res;
495
+ ok: z.boolean().optional(),
496
+ productionUrl: z.string().optional(),
497
+ releaseId: z.string().optional(),
498
+ rolledBack: z.boolean().optional(),
1053
499
  },
1054
500
  }),
1055
- defineTool({
501
+ defineGeneratedTool({
1056
502
  name: "capix_website_destroy",
1057
- 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.",
1058
504
  scope: "website",
1059
505
  ...BILLABLE,
1060
- inputShape: {
1061
- siteId: siteIdShape,
1062
- 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(),
1063
515
  },
1064
- outputShape: siteResultShape,
1065
- 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),
1066
516
  }),
1067
517
  ];
1068
518
  // ===========================================================================
@@ -1072,15 +522,15 @@ export const TOOLS = [
1072
522
  ...discoveryTools,
1073
523
  ...planningTools,
1074
524
  ...lifecycleTools,
1075
- ...networkingTools,
1076
- ...testingTools,
1077
525
  ...verificationTools,
1078
526
  ...websiteTools,
527
+ ...factoryTools,
1079
528
  ...infraContextTools,
529
+ ...memeImageTools,
1080
530
  ];
1081
531
  export const TOOL_NAMES = TOOLS.map((t) => t.name);
1082
532
  export const TOOL_COUNT = TOOLS.length;
1083
533
  /** Map of tool name → definition, used by the server for O(1) lookup. */
1084
534
  export const TOOL_MAP = new Map(TOOLS.map((t) => [t.name, t]));
1085
- export { discoveryTools, planningTools, lifecycleTools, networkingTools, testingTools, verificationTools, websiteTools, infraContextTools };
535
+ export { discoveryTools, planningTools, lifecycleTools, verificationTools, websiteTools, factoryTools, infraContextTools, memeImageTools };
1086
536
  //# sourceMappingURL=tools.js.map