@hoststack.dev/mcp 0.2.0 → 0.2.1

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.js CHANGED
@@ -589,8 +589,8 @@ defineTool({
589
589
  handler: async (args, ctx) => {
590
590
  const teamId = await ctx.resolveTeamId();
591
591
  const response = await ctx.hoststack.deploys.list(teamId, args.service_id);
592
- const data = shapeList(response, "deploys", shapeDeploy);
593
- const summary = data.items.length === 0 ? `No deploys yet for service ${args.service_id}.` : `Found ${data.items.length} deploy${data.items.length === 1 ? "" : "s"} for service ${args.service_id}.`;
592
+ const data = shapeList(response, "data", shapeDeploy);
593
+ const summary = data.items.length === 0 ? `No deploys yet for service ${args.service_id}.` : `Found ${data.items.length} deploy${data.items.length === 1 ? "" : "s"} for service ${args.service_id} (page ${response.page} of ${response.totalPages}, ${response.total} total).`;
594
594
  return respond({ summary, data });
595
595
  }
596
596
  });
@@ -696,22 +696,32 @@ defineTool({
696
696
  " - service_id: publicId of the service.",
697
697
  " - deploy_id: publicId of the deploy.",
698
698
  "",
699
- "Returns: { logs: string } \u2014 full build output as a single string. May be truncated by the API if the deploy is still in progress.",
699
+ "Returns: { logs: string, lineCount: number, nextAfterId: number | null } \u2014 flattened build output joined with newlines, line count, and a cursor for pagination (pass back as after_id to fetch the next page).",
700
700
  "",
701
- 'Example: get_deploy_logs({ service_id: "svc_abc", deploy_id: "dpl_xyz" }) \u2192 { logs: "Building\u2026\\nStep 1/8\u2026\\n\u2026" }'
701
+ 'Example: get_deploy_logs({ service_id: "svc_abc", deploy_id: "dpl_xyz" }) \u2192 { logs: "Building\u2026\\nStep 1/8\u2026\\n\u2026", lineCount: 42, nextAfterId: 1234 }'
702
702
  ].join("\n"),
703
703
  input: {
704
704
  service_id: z5.string().describe("Service publicId."),
705
- deploy_id: z5.string().describe("Deploy publicId.")
705
+ deploy_id: z5.string().describe("Deploy publicId."),
706
+ after_id: z5.number().int().optional().describe("Pagination cursor from a previous response\u2019s nextAfterId."),
707
+ limit: z5.number().int().min(1).max(5e3).optional().describe("Max log entries to fetch (default 500, hard cap 5000).")
706
708
  },
707
709
  handler: async (args, ctx) => {
708
710
  const teamId = await ctx.resolveTeamId();
709
- const response = await ctx.hoststack.deploys.getLogs(teamId, args.service_id, args.deploy_id);
710
- const logs = typeof response.logs === "string" ? response.logs : "";
711
- const lines = logs ? logs.split("\n").length : 0;
711
+ const response = await ctx.hoststack.deploys.getLogs(
712
+ teamId,
713
+ args.service_id,
714
+ args.deploy_id,
715
+ {
716
+ ...args.after_id !== void 0 ? { afterId: args.after_id } : {},
717
+ ...args.limit !== void 0 ? { limit: args.limit } : {}
718
+ }
719
+ );
720
+ const entries = response.logs;
721
+ const logs = entries.map((e) => e.message).join("\n");
712
722
  return respond({
713
- summary: `Fetched ${lines} log line${lines === 1 ? "" : "s"} for deploy ${args.deploy_id}.`,
714
- data: { logs }
723
+ summary: `Fetched ${entries.length} log line${entries.length === 1 ? "" : "s"} for deploy ${args.deploy_id}.`,
724
+ data: { logs, lineCount: entries.length, nextAfterId: response.nextAfterId }
715
725
  });
716
726
  }
717
727
  });
@@ -1237,52 +1247,79 @@ defineTool({
1237
1247
  name: "update_service_config",
1238
1248
  category: "services",
1239
1249
  description: [
1240
- "Update build/runtime configuration for a service: build command, start command, branch, root directory, dockerfile path, auto-deploy flag, instance count, plan tier. All fields optional \u2014 pass only what you want to change.",
1250
+ "Update build/runtime configuration for a service: build command, start command, install command, branch, root directory, dockerfile path, auto-deploy flag, instance count. All fields optional \u2014 pass only what you want to change.",
1241
1251
  "",
1242
- "When to use: the user wants to tweak how a service builds or runs without redeploying manually. Updating any field marked dispatch-eligible (branch, build/start command, plan, root, dockerfile) typically triggers a follow-up deploy automatically; instance count scales without redeploying.",
1252
+ "When to use: the user wants to tweak how a service builds or runs. Build/runtime fields (branch, install/build/start command, root, dockerfile) take effect on the next deploy \u2014 call trigger_deploy after if you need them applied immediately. instance_count rescales without a redeploy.",
1243
1253
  "",
1244
1254
  "Inputs:",
1245
1255
  " - service_id: publicId of the service.",
1246
- " - build_command, start_command (optional): shell commands.",
1256
+ " - install_command, build_command, start_command (optional): shell commands. Pass null to clear.",
1247
1257
  " - branch (optional): git branch to track.",
1248
- " - root_directory, dockerfile_path (optional): build context overrides.",
1258
+ " - root_directory (optional): build context root inside the repo.",
1259
+ " - dockerfile_path (optional): path to Dockerfile relative to root_directory. Pass null to clear.",
1249
1260
  " - auto_deploy (optional): boolean \u2014 auto-deploy on git push.",
1250
- " - instance_count (optional): integer \u22651 \u2014 manual scale.",
1251
- ' - plan (optional): plan tier slug (e.g. "starter", "standard", "pro").',
1261
+ " - instance_count (optional): integer \u22651 \u2014 pin both min and max instances to this value.",
1252
1262
  "",
1253
- "Returns: { config: ServiceConfig } \u2014 the updated config record.",
1263
+ "Returns: { service?: Service, config?: ServiceConfig } \u2014 whichever rows were touched.",
1254
1264
  "",
1255
- 'Example: update_service_config({ service_id: "svc_abc", instance_count: 3 }) \u2192 { config: { instanceCount: 3, \u2026 } }'
1265
+ '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 } }'
1256
1266
  ].join("\n"),
1257
1267
  input: {
1258
1268
  service_id: z9.string().describe("Service publicId."),
1259
- build_command: z9.string().optional().describe("Build shell command."),
1260
- start_command: z9.string().optional().describe("Start shell command."),
1269
+ install_command: z9.string().nullable().optional().describe("Install shell command. Null clears."),
1270
+ build_command: z9.string().nullable().optional().describe("Build shell command. Null clears."),
1271
+ start_command: z9.string().nullable().optional().describe("Start shell command. Null clears."),
1261
1272
  branch: z9.string().optional().describe("Git branch to track."),
1262
1273
  root_directory: z9.string().optional().describe("Build context root."),
1263
- dockerfile_path: z9.string().optional().describe("Path to Dockerfile relative to root."),
1274
+ dockerfile_path: z9.string().nullable().optional().describe("Path to Dockerfile relative to root. Null clears."),
1264
1275
  auto_deploy: z9.boolean().optional().describe("Auto-deploy on push."),
1265
- instance_count: z9.number().int().positive().optional().describe("Manual scale (\u22651)."),
1266
- plan: z9.string().optional().describe("Plan tier slug.")
1276
+ instance_count: z9.number().int().positive().max(50).optional().describe("Pin min and max instances to this value (1\u201350).")
1267
1277
  },
1268
1278
  handler: async (args, ctx) => {
1269
1279
  const teamId = await ctx.resolveTeamId();
1270
- const input = {};
1271
- if (args.build_command !== void 0) input["buildCommand"] = args.build_command;
1272
- if (args.start_command !== void 0) input["startCommand"] = args.start_command;
1273
- if (args.branch !== void 0) input["branch"] = args.branch;
1274
- if (args.root_directory !== void 0) input["rootDirectory"] = args.root_directory;
1275
- if (args.dockerfile_path !== void 0) input["dockerfilePath"] = args.dockerfile_path;
1276
- if (args.auto_deploy !== void 0) input["autoDeploy"] = args.auto_deploy;
1277
- if (args.instance_count !== void 0) input["instanceCount"] = args.instance_count;
1278
- if (args.plan !== void 0) input["plan"] = args.plan;
1279
- if (Object.keys(input).length === 0) {
1280
+ const serviceUpdate = {};
1281
+ if (args.install_command !== void 0)
1282
+ serviceUpdate["installCommand"] = args.install_command;
1283
+ if (args.build_command !== void 0) serviceUpdate["buildCommand"] = args.build_command;
1284
+ if (args.start_command !== void 0) serviceUpdate["startCommand"] = args.start_command;
1285
+ if (args.dockerfile_path !== void 0)
1286
+ serviceUpdate["dockerfilePath"] = args.dockerfile_path;
1287
+ if (args.branch !== void 0) serviceUpdate["branch"] = args.branch;
1288
+ if (args.root_directory !== void 0)
1289
+ serviceUpdate["rootDirectory"] = args.root_directory;
1290
+ if (args.auto_deploy !== void 0) serviceUpdate["autoDeploy"] = args.auto_deploy;
1291
+ const configUpdate = {};
1292
+ if (args.instance_count !== void 0) {
1293
+ configUpdate["minInstances"] = args.instance_count;
1294
+ configUpdate["maxInstances"] = args.instance_count;
1295
+ }
1296
+ if (Object.keys(serviceUpdate).length === 0 && Object.keys(configUpdate).length === 0) {
1280
1297
  return respond({ summary: "No fields to update.", data: {} });
1281
1298
  }
1282
- const response = await ctx.hoststack.services.updateConfig(teamId, args.service_id, input);
1283
- const data = { config: shape(response.config) };
1284
- const fields = Object.keys(input).join(", ");
1285
- return respond({ summary: `Updated ${fields} on service ${args.service_id}.`, data });
1299
+ const data = {};
1300
+ const touchedFields = [];
1301
+ if (Object.keys(serviceUpdate).length > 0) {
1302
+ const serviceResponse = await ctx.hoststack.services.update(
1303
+ teamId,
1304
+ args.service_id,
1305
+ serviceUpdate
1306
+ );
1307
+ data["service"] = shape(serviceResponse.service);
1308
+ touchedFields.push(...Object.keys(serviceUpdate));
1309
+ }
1310
+ if (Object.keys(configUpdate).length > 0) {
1311
+ const configResponse = await ctx.hoststack.services.updateConfig(
1312
+ teamId,
1313
+ args.service_id,
1314
+ configUpdate
1315
+ );
1316
+ data["config"] = shape(configResponse.config);
1317
+ touchedFields.push(...Object.keys(configUpdate));
1318
+ }
1319
+ return respond({
1320
+ summary: `Updated ${touchedFields.join(", ")} on service ${args.service_id}.`,
1321
+ data
1322
+ });
1286
1323
  }
1287
1324
  });
1288
1325
  defineTool({