@hoststack.dev/mcp 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ declare class ApiClient {
20
20
  private handle;
21
21
  }
22
22
 
23
- type ToolCategory = 'projects' | 'services' | 'deploys' | 'databases' | 'domains' | 'volumes' | 'env-vars' | 'cron' | 'logs' | 'activity-log' | 'meta';
23
+ type ToolCategory = 'projects' | 'services' | 'deploys' | 'databases' | 'domains' | 'volumes' | 'env-vars' | 'environments' | 'cron' | 'logs' | 'activity-log' | 'meta';
24
24
  interface ToolContext {
25
25
  hoststack: HostStack;
26
26
  api: ApiClient;
package/dist/index.js CHANGED
@@ -996,6 +996,146 @@ defineTool({
996
996
  }
997
997
  });
998
998
 
999
+ // src/tools/environments.ts
1000
+ import { z as z8 } from "zod";
1001
+ defineTool({
1002
+ name: "list_environments",
1003
+ category: "environments",
1004
+ description: [
1005
+ "List every environment for a project (production / staging / development / preview).",
1006
+ "",
1007
+ "When to use: the user wants to see what envs exist before creating a service in one or promoting a deploy. Every project has at least Production.",
1008
+ "",
1009
+ "Inputs:",
1010
+ ' - project_id: project publicId (e.g. "prj_abc123").',
1011
+ "",
1012
+ "Returns: { items: Environment[] } \u2014 id, publicId, name, type, isDefault, isProtected.",
1013
+ "",
1014
+ 'Example: list_environments({ project_id: "prj_abc" }) \u2192 { items: [{ name: "Production", type: "production", isDefault: true }, { name: "Staging", type: "staging" }] }'
1015
+ ].join("\n"),
1016
+ input: {
1017
+ project_id: z8.string().describe("Project publicId.")
1018
+ },
1019
+ handler: async (args, ctx) => {
1020
+ const teamId = await ctx.resolveTeamId();
1021
+ const response = await ctx.hoststack.environments.list(teamId, args.project_id);
1022
+ const data = shapeList(response, "environments", shape);
1023
+ const summary = data.items.length === 0 ? "No environments yet \u2014 every project should have Production by default." : `Found ${data.items.length} environment${data.items.length === 1 ? "" : "s"}.`;
1024
+ return respond({ summary, data });
1025
+ }
1026
+ });
1027
+ defineTool({
1028
+ name: "create_environment",
1029
+ category: "environments",
1030
+ description: [
1031
+ "Create a new environment in a project (e.g. Staging, QA, Preview).",
1032
+ "",
1033
+ "When to use: the user wants to add an env so they can run a sibling service alongside production for testing or staging before release.",
1034
+ "",
1035
+ "Inputs:",
1036
+ " - project_id: project publicId.",
1037
+ " - name: human-readable name (1\u201364 chars). Shown in the env switcher.",
1038
+ ' - type: "production" | "staging" | "development" | "preview". Determines the hostname suffix on services in this env (production stays clean; others get -staging / -dev / -preview).',
1039
+ " - is_protected (optional): require admin role for destructive actions in this env. Default false.",
1040
+ "",
1041
+ "Returns: { environment: Environment }.",
1042
+ "",
1043
+ 'Example: create_environment({ project_id: "prj_abc", name: "Staging", type: "staging" }) \u2192 { environment: { id: 7, publicId: "environment_\u2026", name: "Staging", type: "staging" } }'
1044
+ ].join("\n"),
1045
+ input: {
1046
+ project_id: z8.string().describe("Project publicId."),
1047
+ name: z8.string().min(1).max(64).describe("Environment name (1\u201364 chars)."),
1048
+ type: z8.enum(["production", "staging", "development", "preview"]).describe("Environment type \u2014 drives the hostname suffix."),
1049
+ is_protected: z8.boolean().optional().describe("Require admin role for destructive actions. Default false.")
1050
+ },
1051
+ handler: async (args, ctx) => {
1052
+ const teamId = await ctx.resolveTeamId();
1053
+ const input = {
1054
+ name: args.name,
1055
+ type: args.type
1056
+ };
1057
+ if (args.is_protected !== void 0) input.isProtected = args.is_protected;
1058
+ const response = await ctx.hoststack.environments.create(teamId, args.project_id, input);
1059
+ const data = { environment: shape(response.environment) };
1060
+ return respond({
1061
+ summary: `Created environment "${args.name}" (${args.type}) in project ${args.project_id}.`,
1062
+ data
1063
+ });
1064
+ }
1065
+ });
1066
+ defineTool({
1067
+ name: "delete_environment",
1068
+ category: "environments",
1069
+ description: [
1070
+ "Delete an environment from a project.",
1071
+ "",
1072
+ "When to use: an env is no longer used (e.g. tearing down a feature branch staging). Blocked when the env still has live services or databases attached \u2014 destroy or move them first. The default env (usually Production) cannot be deleted.",
1073
+ "",
1074
+ "Inputs:",
1075
+ " - project_id: project publicId.",
1076
+ " - environment_id: environment publicId or numeric id.",
1077
+ "",
1078
+ "Returns: { success: true } on success.",
1079
+ "",
1080
+ 'Example: delete_environment({ project_id: "prj_abc", environment_id: "environment_xyz" }) \u2192 { success: true }'
1081
+ ].join("\n"),
1082
+ input: {
1083
+ project_id: z8.string().describe("Project publicId."),
1084
+ environment_id: z8.union([z8.string(), z8.number()]).describe("Environment publicId or numeric id.")
1085
+ },
1086
+ handler: async (args, ctx) => {
1087
+ const teamId = await ctx.resolveTeamId();
1088
+ await ctx.hoststack.environments.delete(teamId, args.project_id, args.environment_id);
1089
+ return respond({
1090
+ summary: `Deleted environment ${String(args.environment_id)} from project ${args.project_id}.`,
1091
+ data: { success: true }
1092
+ });
1093
+ }
1094
+ });
1095
+ defineTool({
1096
+ name: "promote_deploy",
1097
+ category: "environments",
1098
+ description: [
1099
+ "Promote a built deploy from one environment to another (build-once, run-many).",
1100
+ "",
1101
+ "When to use: the user has tested a deploy in staging and wants to ship that exact image to production without rebuilding. Atomic and image-based \u2014 same docker image runs on the sibling service in the target env.",
1102
+ "",
1103
+ "Inputs:",
1104
+ " - service_id: source service publicId (the one that owns the deploy).",
1105
+ " - deploy_id: source deploy publicId. Must have `dockerImageId` set (i.e. a successful build).",
1106
+ " - target_environment_id: env publicId or numeric id to promote into. Must be in the same project. Cannot be the source service's own env.",
1107
+ "",
1108
+ "Behaviour: if a sibling service with the same name already exists in the target env, the new deploy lands on it. Otherwise the API auto-clones the source service's build/runtime config into the target env first. Env-specific config (env vars, secret files, volumes, IP allowlists, custom domains) is NOT copied \u2014 those are per-env by design.",
1109
+ "",
1110
+ "Returns: { deploy: Deploy } \u2014 the new deploy on the target service.",
1111
+ "",
1112
+ 'Example: promote_deploy({ service_id: "svc_staging", deploy_id: "dpl_built", target_environment_id: "environment_prod" }) \u2192 { deploy: { id: 99, status: "pending", trigger: "rollback", dockerImageId: "sha256:\u2026" } }'
1113
+ ].join("\n"),
1114
+ input: {
1115
+ service_id: z8.string().describe("Source service publicId."),
1116
+ deploy_id: z8.string().describe("Source deploy publicId (must have a built image)."),
1117
+ target_environment_id: z8.union([z8.string(), z8.number()]).describe("Target environment publicId or numeric id.")
1118
+ },
1119
+ handler: async (args, ctx) => {
1120
+ const teamId = await ctx.resolveTeamId();
1121
+ const targetEnvId = await ctx.hoststack.resolveId(args.target_environment_id, {
1122
+ kind: "environment",
1123
+ teamId: await ctx.hoststack.resolveId(teamId, { kind: "team" })
1124
+ });
1125
+ const response = await ctx.hoststack.deploys.promote(
1126
+ teamId,
1127
+ args.service_id,
1128
+ args.deploy_id,
1129
+ targetEnvId
1130
+ );
1131
+ const data = { deploy: shape(response.deploy) };
1132
+ return respond({
1133
+ summary: `Promoted deploy ${args.deploy_id} from service ${args.service_id} to env ${String(args.target_environment_id)}.`,
1134
+ data
1135
+ });
1136
+ }
1137
+ });
1138
+
999
1139
  // src/tools/meta.ts
1000
1140
  defineTool({
1001
1141
  name: "get_me",
@@ -1023,7 +1163,7 @@ defineTool({
1023
1163
  });
1024
1164
 
1025
1165
  // src/tools/projects.ts
1026
- import { z as z8 } from "zod";
1166
+ import { z as z9 } from "zod";
1027
1167
  defineTool({
1028
1168
  name: "list_projects",
1029
1169
  category: "projects",
@@ -1063,9 +1203,9 @@ defineTool({
1063
1203
  'Example: create_project({ name: "billing-api", description: "Stripe webhooks", region: "fsn1" }) \u2192 { project: { id: 12, publicId: "prj_\u2026", \u2026 } }'
1064
1204
  ].join("\n"),
1065
1205
  input: {
1066
- name: z8.string().min(1).max(60).describe("Project name (1\u201360 chars)."),
1067
- description: z8.string().max(500).optional().describe("Short description (\u2264500 chars)."),
1068
- region: z8.enum(["fsn1", "nbg1", "hel1"]).optional().describe("Hetzner region: fsn1 | nbg1 | hel1.")
1206
+ name: z9.string().min(1).max(60).describe("Project name (1\u201360 chars)."),
1207
+ description: z9.string().max(500).optional().describe("Short description (\u2264500 chars)."),
1208
+ region: z9.enum(["fsn1", "nbg1", "hel1"]).optional().describe("Hetzner region: fsn1 | nbg1 | hel1.")
1069
1209
  },
1070
1210
  handler: async (args, ctx) => {
1071
1211
  const teamId = await ctx.resolveTeamId();
@@ -1096,9 +1236,9 @@ defineTool({
1096
1236
  'Example: update_project({ project_id: "prj_abc", name: "billing-prod" }) \u2192 { project: { name: "billing-prod", \u2026 } }'
1097
1237
  ].join("\n"),
1098
1238
  input: {
1099
- project_id: z8.string().describe("Project publicId."),
1100
- name: z8.string().min(1).max(60).optional().describe("New name (1\u201360 chars)."),
1101
- description: z8.string().max(500).optional().describe("New description (\u2264500 chars).")
1239
+ project_id: z9.string().describe("Project publicId."),
1240
+ name: z9.string().min(1).max(60).optional().describe("New name (1\u201360 chars)."),
1241
+ description: z9.string().max(500).optional().describe("New description (\u2264500 chars).")
1102
1242
  },
1103
1243
  handler: async (args, ctx) => {
1104
1244
  if (args.name === void 0 && args.description === void 0) {
@@ -1132,7 +1272,7 @@ defineTool({
1132
1272
  'Example: get_project({ project_id: "prj_abc" }) \u2192 { project: { id: 12, name: "billing", \u2026 } }'
1133
1273
  ].join("\n"),
1134
1274
  input: {
1135
- project_id: z8.string().describe("Project publicId (e.g. prj_abc123).")
1275
+ project_id: z9.string().describe("Project publicId (e.g. prj_abc123).")
1136
1276
  },
1137
1277
  handler: async (args, ctx) => {
1138
1278
  const teamId = await ctx.resolveTeamId();
@@ -1144,7 +1284,7 @@ defineTool({
1144
1284
  });
1145
1285
 
1146
1286
  // src/tools/services.ts
1147
- import { z as z9 } from "zod";
1287
+ import { z as z10 } from "zod";
1148
1288
  defineTool({
1149
1289
  name: "list_services",
1150
1290
  category: "services",
@@ -1182,7 +1322,7 @@ defineTool({
1182
1322
  'Example: get_service({ service_id: "svc_abc" }) \u2192 { service: { type: "web", status: "running", \u2026 } }'
1183
1323
  ].join("\n"),
1184
1324
  input: {
1185
- service_id: z9.string().describe("Service publicId (e.g. svc_abc123).")
1325
+ service_id: z10.string().describe("Service publicId (e.g. svc_abc123).")
1186
1326
  },
1187
1327
  handler: async (args, ctx) => {
1188
1328
  const teamId = await ctx.resolveTeamId();
@@ -1208,7 +1348,7 @@ defineTool({
1208
1348
  'Example: get_service_metrics({ service_id: "svc_abc" }) \u2192 { metrics: { cpu: 0.42, memory: 0.71, \u2026 } }'
1209
1349
  ].join("\n"),
1210
1350
  input: {
1211
- service_id: z9.string().describe("Service publicId.")
1351
+ service_id: z10.string().describe("Service publicId.")
1212
1352
  },
1213
1353
  handler: async (args, ctx) => {
1214
1354
  const teamId = await ctx.resolveTeamId();
@@ -1234,8 +1374,8 @@ defineTool({
1234
1374
  'Example: update_service({ service_id: "svc_abc", name: "api-prod" }) \u2192 { service: { name: "api-prod", \u2026 } }'
1235
1375
  ].join("\n"),
1236
1376
  input: {
1237
- service_id: z9.string().describe("Service publicId."),
1238
- name: z9.string().min(1).max(60).describe("New service name (1\u201360 chars).")
1377
+ service_id: z10.string().describe("Service publicId."),
1378
+ name: z10.string().min(1).max(60).describe("New service name (1\u201360 chars).")
1239
1379
  },
1240
1380
  handler: async (args, ctx) => {
1241
1381
  const teamId = await ctx.resolveTeamId();
@@ -1268,15 +1408,15 @@ defineTool({
1268
1408
  'Example: update_service_config({ service_id: "svc_abc", start_command: "bun apps/api/src/index.ts" }) \u2192 { service: { startCommand: "bun apps/api/src/index.ts", \u2026 } }'
1269
1409
  ].join("\n"),
1270
1410
  input: {
1271
- service_id: z9.string().describe("Service publicId."),
1272
- install_command: z9.string().nullable().optional().describe("Install shell command. Null clears."),
1273
- build_command: z9.string().nullable().optional().describe("Build shell command. Null clears."),
1274
- start_command: z9.string().nullable().optional().describe("Start shell command. Null clears."),
1275
- branch: z9.string().optional().describe("Git branch to track."),
1276
- root_directory: z9.string().optional().describe("Build context root."),
1277
- dockerfile_path: z9.string().nullable().optional().describe("Path to Dockerfile relative to root. Null clears."),
1278
- auto_deploy: z9.boolean().optional().describe("Auto-deploy on push."),
1279
- instance_count: z9.number().int().positive().max(50).optional().describe("Pin min and max instances to this value (1\u201350).")
1411
+ service_id: z10.string().describe("Service publicId."),
1412
+ install_command: z10.string().nullable().optional().describe("Install shell command. Null clears."),
1413
+ build_command: z10.string().nullable().optional().describe("Build shell command. Null clears."),
1414
+ start_command: z10.string().nullable().optional().describe("Start shell command. Null clears."),
1415
+ branch: z10.string().optional().describe("Git branch to track."),
1416
+ root_directory: z10.string().optional().describe("Build context root."),
1417
+ dockerfile_path: z10.string().nullable().optional().describe("Path to Dockerfile relative to root. Null clears."),
1418
+ auto_deploy: z10.boolean().optional().describe("Auto-deploy on push."),
1419
+ instance_count: z10.number().int().positive().max(50).optional().describe("Pin min and max instances to this value (1\u201350).")
1280
1420
  },
1281
1421
  handler: async (args, ctx) => {
1282
1422
  const teamId = await ctx.resolveTeamId();
@@ -1341,7 +1481,7 @@ defineTool({
1341
1481
  'Example: suspend_service({ service_id: "svc_dev" }) \u2192 { ok: true }'
1342
1482
  ].join("\n"),
1343
1483
  input: {
1344
- service_id: z9.string().describe("Service publicId.")
1484
+ service_id: z10.string().describe("Service publicId.")
1345
1485
  },
1346
1486
  handler: async (args, ctx) => {
1347
1487
  const teamId = await ctx.resolveTeamId();
@@ -1365,7 +1505,7 @@ defineTool({
1365
1505
  'Example: resume_service({ service_id: "svc_dev" }) \u2192 { ok: true }'
1366
1506
  ].join("\n"),
1367
1507
  input: {
1368
- service_id: z9.string().describe("Service publicId.")
1508
+ service_id: z10.string().describe("Service publicId.")
1369
1509
  },
1370
1510
  handler: async (args, ctx) => {
1371
1511
  const teamId = await ctx.resolveTeamId();
@@ -1384,18 +1524,29 @@ defineTool({
1384
1524
  "Inputs:",
1385
1525
  " - service_id: publicId of the service.",
1386
1526
  " - lines (optional): tail size (default 200, max 1000).",
1387
- " - since (optional): ISO-8601 timestamp; only return entries newer than this.",
1527
+ ' - since/until (optional): ISO-8601 timestamp OR a relative offset like "-5m", "-1h", "-2d".',
1388
1528
  ' - stream (optional): "stdout" | "stderr". Omit to combine.',
1529
+ " - level (optional): friendly synonym \u2014 info/debug \u2192 stdout, warn/error \u2192 stderr.",
1530
+ " - search (optional): case-insensitive substring grep, \u2264100 chars.",
1531
+ ' - count_only (optional): when true, returns { count } only \u2014 much cheaper for "how many error lines in last 5m" polling.',
1532
+ "",
1533
+ "Returns: { logs: LogEntry[] | string } when count_only is false (each entry has { timestamp, level?, stream?, message }), or { count: number } when count_only is true.",
1389
1534
  "",
1390
- "Returns: { logs: LogEntry[] | string } \u2014 each entry has { timestamp, level?, stream?, message }. Older HostStack agents return a single string blob.",
1535
+ 'Example: get_service_logs({ service_id: "svc_abc", search: "OOM", since: "-15m" }) \u2192 { logs: [...] }.',
1391
1536
  "",
1392
- 'Example: get_service_logs({ service_id: "svc_abc", lines: 100 }) \u2192 { logs: [{ timestamp: "2026-04-25T\u2026", message: "GET /health 200" }, \u2026] }'
1537
+ "More examples:",
1538
+ ' - Last 50 stderr lines from the past hour: get_service_logs({ service_id: "svc_abc", lines: 50, stream: "stderr", since: "-1h" })',
1539
+ ' - Just count error lines without fetching them: get_service_logs({ service_id: "svc_abc", level: "error", since: "-5m", count_only: true }) \u2192 { count: 47 }'
1393
1540
  ].join("\n"),
1394
1541
  input: {
1395
- service_id: z9.string().describe("Service publicId."),
1396
- lines: z9.number().int().positive().max(1e3).optional().describe("Tail size; default 200, hard cap 1000."),
1397
- since: z9.string().datetime().optional().describe("ISO-8601 timestamp lower bound."),
1398
- stream: z9.enum(["stdout", "stderr"]).optional().describe("Restrict to one stream.")
1542
+ service_id: z10.string().describe("Service publicId."),
1543
+ lines: z10.number().int().positive().max(1e3).optional().describe("Tail size; default 200, hard cap 1000."),
1544
+ since: z10.string().optional().describe('ISO-8601 timestamp or relative offset (e.g. "-5m", "-1h").'),
1545
+ until: z10.string().optional().describe("ISO-8601 timestamp or relative offset upper bound."),
1546
+ stream: z10.enum(["stdout", "stderr"]).optional().describe("Restrict to one stream."),
1547
+ level: z10.enum(["stdout", "stderr", "info", "warn", "error", "debug"]).optional().describe("Friendly stream alias: info/debug\u2192stdout, warn/error\u2192stderr."),
1548
+ search: z10.string().max(100).optional().describe("Case-insensitive substring filter."),
1549
+ count_only: z10.boolean().optional().describe("When true, return only { count } \u2014 skips the log payload.")
1399
1550
  },
1400
1551
  handler: async (args, ctx) => {
1401
1552
  const teamId = await ctx.resolveTeamId();
@@ -1403,8 +1554,18 @@ defineTool({
1403
1554
  lines: args.lines ?? 200
1404
1555
  };
1405
1556
  if (args.since) opts.since = args.since;
1557
+ if (args.until) opts.until = args.until;
1406
1558
  if (args.stream) opts.stream = args.stream;
1559
+ if (args.level) opts.level = args.level;
1560
+ if (args.search) opts.search = args.search;
1561
+ if (args.count_only) opts.countOnly = args.count_only;
1407
1562
  const response = await ctx.hoststack.services.getRuntimeLogs(teamId, args.service_id, opts);
1563
+ if ("count" in response) {
1564
+ return respond({
1565
+ summary: `${response.count} matching log line${response.count === 1 ? "" : "s"} for service ${args.service_id}.`,
1566
+ data: { count: response.count }
1567
+ });
1568
+ }
1408
1569
  const count = Array.isArray(response.logs) ? response.logs.length : typeof response.logs === "string" ? response.logs.split("\n").length : 0;
1409
1570
  return respond({
1410
1571
  summary: `Fetched ${count} log line${count === 1 ? "" : "s"} for service ${args.service_id}.`,
@@ -1414,7 +1575,7 @@ defineTool({
1414
1575
  });
1415
1576
 
1416
1577
  // src/tools/volumes.ts
1417
- import { z as z10 } from "zod";
1578
+ import { z as z11 } from "zod";
1418
1579
  defineTool({
1419
1580
  name: "list_volumes",
1420
1581
  category: "volumes",
@@ -1431,7 +1592,7 @@ defineTool({
1431
1592
  'Example: list_volumes({ service_id: "svc_abc" }) \u2192 { items: [{ name: "data", mountPath: "/var/data", sizeGb: 10, status: "active" }] }'
1432
1593
  ].join("\n"),
1433
1594
  input: {
1434
- service_id: z10.string().describe("Service publicId (e.g. svc_abc123).")
1595
+ service_id: z11.string().describe("Service publicId (e.g. svc_abc123).")
1435
1596
  },
1436
1597
  handler: async (args, ctx) => {
1437
1598
  const teamId = await ctx.resolveTeamId();
@@ -1460,10 +1621,10 @@ defineTool({
1460
1621
  'Example: create_volume({ service_id: "svc_abc", name: "data", mount_path: "/var/data", size_gb: 10 }) \u2192 { volume: { name: "data", mountPath: "/var/data", sizeGb: 10, status: "active" } }'
1461
1622
  ].join("\n"),
1462
1623
  input: {
1463
- service_id: z10.string().describe("Service publicId."),
1464
- name: z10.string().min(1).max(64).regex(/^[a-z0-9-]+$/).describe("Volume name (lowercase alphanumeric + hyphens)."),
1465
- mount_path: z10.string().startsWith("/").max(500).describe("In-container mount path (absolute)."),
1466
- size_gb: z10.number().int().min(1).max(100).optional().describe("Disk size in GB (default 1, max 100).")
1624
+ service_id: z11.string().describe("Service publicId."),
1625
+ name: z11.string().min(1).max(64).regex(/^[a-z0-9-]+$/).describe("Volume name (lowercase alphanumeric + hyphens)."),
1626
+ mount_path: z11.string().startsWith("/").max(500).describe("In-container mount path (absolute)."),
1627
+ size_gb: z11.number().int().min(1).max(100).optional().describe("Disk size in GB (default 1, max 100).")
1467
1628
  },
1468
1629
  handler: async (args, ctx) => {
1469
1630
  const teamId = await ctx.resolveTeamId();
@@ -1499,10 +1660,10 @@ defineTool({
1499
1660
  'Example: update_volume({ service_id: "svc_abc", volume_id: "vol_xyz", size_gb: 20 }) \u2192 { volume: { sizeGb: 20, \u2026 } }'
1500
1661
  ].join("\n"),
1501
1662
  input: {
1502
- service_id: z10.string().describe("Service publicId."),
1503
- volume_id: z10.string().describe("Volume publicId (e.g. vol_\u2026)."),
1504
- mount_path: z10.string().startsWith("/").max(500).optional().describe("New mount path."),
1505
- size_gb: z10.number().int().min(1).max(100).optional().describe("New size in GB.")
1663
+ service_id: z11.string().describe("Service publicId."),
1664
+ volume_id: z11.string().describe("Volume publicId (e.g. vol_\u2026)."),
1665
+ mount_path: z11.string().startsWith("/").max(500).optional().describe("New mount path."),
1666
+ size_gb: z11.number().int().min(1).max(100).optional().describe("New size in GB.")
1506
1667
  },
1507
1668
  handler: async (args, ctx) => {
1508
1669
  const teamId = await ctx.resolveTeamId();
@@ -1540,8 +1701,8 @@ defineTool({
1540
1701
  'Example: delete_volume({ service_id: "svc_abc", volume_id: "vol_xyz" }) \u2192 { ok: true }'
1541
1702
  ].join("\n"),
1542
1703
  input: {
1543
- service_id: z10.string().describe("Service publicId."),
1544
- volume_id: z10.string().describe("Volume publicId.")
1704
+ service_id: z11.string().describe("Service publicId."),
1705
+ volume_id: z11.string().describe("Volume publicId.")
1545
1706
  },
1546
1707
  handler: async (args, ctx) => {
1547
1708
  const teamId = await ctx.resolveTeamId();