@hoststack.dev/mcp 0.20.0 → 0.21.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.js CHANGED
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { HostStack } from "@hoststack.dev/sdk";
4
4
 
5
5
  // src/version.ts
6
- var MCP_VERSION = true ? "0.20.0" : "0.0.0-dev";
6
+ var MCP_VERSION = true ? "0.21.0" : "0.0.0-dev";
7
7
  var USER_AGENT = `hoststack-mcp/${MCP_VERSION}`;
8
8
 
9
9
  // src/api-client.ts
@@ -3459,6 +3459,8 @@ defineTool({
3459
3459
  ' - task_id: "task_\u2026" publicId or numeric id.',
3460
3460
  ' - status: "done" or "idea". Only these two; `queued`/`running`/`failed`/`cancelled` belong to the runner and are rejected.',
3461
3461
  " - title / body / service_id: optional edits.",
3462
+ ' - provider: which agent runs it \u2014 "claude", "codex" or "opencode". null clears the pin and lets the box decide.',
3463
+ ' - permission_mode: how that agent is allowed to act \u2014 "default" (ask before acting), "acceptEdits" (edit files without asking) or "bypassPermissions" (no prompts at all). Only set bypassPermissions when the person asked for it: it is the box-wide yolo setting scoped to one task.',
3462
3464
  "",
3463
3465
  "Only mark a task done when it is actually resolved - the backlog is what someone reads to decide what still needs doing.",
3464
3466
  "",
@@ -3471,7 +3473,11 @@ defineTool({
3471
3473
  status: z17.enum(["idea", "done"]).optional().describe("The only two a person may set. The runner owns the rest of the lifecycle."),
3472
3474
  title: z17.string().min(1).max(200).optional().describe("New title."),
3473
3475
  body: z17.string().max(2e4).optional().describe("New prompt body."),
3474
- service_id: z17.union([z17.number().int().positive(), z17.string()]).nullable().optional().describe("Pin to a dev box, or null to unpin it back to a loose idea.")
3476
+ service_id: z17.union([z17.number().int().positive(), z17.string()]).nullable().optional().describe("Pin to a dev box, or null to unpin it back to a loose idea."),
3477
+ provider: z17.enum(["claude", "codex", "opencode"]).nullable().optional().describe("Which agent runs this task, or null to leave it to the box."),
3478
+ permission_mode: z17.enum(["acceptEdits", "bypassPermissions", "default"]).optional().describe(
3479
+ "How the agent may act. bypassPermissions asks for nothing \u2014 set it only on request."
3480
+ )
3475
3481
  },
3476
3482
  handler: async (args, ctx) => {
3477
3483
  const teamId = await ctx.resolveTeamId();
@@ -3480,7 +3486,10 @@ defineTool({
3480
3486
  ...args.status ? { status: args.status } : {},
3481
3487
  ...args.title ? { title: args.title } : {},
3482
3488
  ...args.body !== void 0 ? { body: args.body } : {},
3483
- ...serviceId !== void 0 ? { serviceId } : {}
3489
+ ...serviceId !== void 0 ? { serviceId } : {},
3490
+ // null is meaningful on provider (unpin), so compare to undefined.
3491
+ ...args.provider !== void 0 ? { provider: args.provider } : {},
3492
+ ...args.permission_mode !== void 0 ? { permissionMode: args.permission_mode } : {}
3484
3493
  });
3485
3494
  const data = shape(response.task);
3486
3495
  return respond({
@@ -4675,6 +4684,8 @@ defineTool({
4675
4684
  " - instance_count (optional): integer 1\u201350 \u2014 pin both min and max instances to this value.",
4676
4685
  " - min_instances, max_instances (optional): integers \u2014 autoscale bounds. Use instead of instance_count when you want a range.",
4677
4686
  " - scale_cpu_threshold, scale_memory_threshold (optional): integer 10\u2013100 \u2014 autoscale trigger percentage.",
4687
+ " - scale_requests_per_sec_threshold (optional): integer 1\u201310000 \u2014 autoscale on request rate instead of (or alongside) CPU and memory. Pass null to stop scaling on request rate.",
4688
+ " - build_timeout_seconds (optional): integer 60\u20131800 \u2014 how long a build may run before it is killed. Raise it for a service whose build legitimately takes longer than the default; pass null to go back to the default.",
4678
4689
  ` - log_filter_rules (optional): list of { pattern, action } rules applied at read time \u2014 to get_service_logs (tail AND count) and to the dashboard's live stream alike. Pattern matches the message by case-insensitive substring, literally (a % or _ is not a wildcard); action is "drop" (filter out) or "downgrade" (flip stderr \u2192 stdout so it stops looking like an error). Stored lines are never altered, so removing a rule re-exposes everything it was hiding. Pass [] to clear all rules. Capped at 50 rules.`,
4679
4690
  "",
4680
4691
  "Returns: { service?: Service, config?: ServiceConfig } \u2014 whichever rows were touched.",
@@ -4715,6 +4726,8 @@ defineTool({
4715
4726
  max_instances: z19.number().int().min(1).max(50).optional().describe("Autoscale upper bound. Use with min_instances for a range."),
4716
4727
  scale_cpu_threshold: z19.number().int().min(10).max(100).optional().describe("Autoscale CPU trigger percentage (10\u2013100)."),
4717
4728
  scale_memory_threshold: z19.number().int().min(10).max(100).optional().describe("Autoscale memory trigger percentage (10\u2013100)."),
4729
+ scale_requests_per_sec_threshold: z19.number().int().min(1).max(1e4).nullable().optional().describe("Autoscale request-rate trigger (1\u201310000 req/s). null stops using it."),
4730
+ build_timeout_seconds: z19.number().int().min(60).max(1800).nullable().optional().describe("Kill a build that runs longer than this (60\u20131800s). null = default."),
4718
4731
  log_filter_rules: z19.array(
4719
4732
  z19.object({
4720
4733
  pattern: z19.string().min(1).max(200),
@@ -4769,6 +4782,10 @@ defineTool({
4769
4782
  configUpdate["scaleCpuThreshold"] = args.scale_cpu_threshold;
4770
4783
  if (args.scale_memory_threshold !== void 0)
4771
4784
  configUpdate["scaleMemoryThreshold"] = args.scale_memory_threshold;
4785
+ if (args.scale_requests_per_sec_threshold !== void 0)
4786
+ configUpdate["scaleRequestsPerSecThreshold"] = args.scale_requests_per_sec_threshold;
4787
+ if (args.build_timeout_seconds !== void 0)
4788
+ configUpdate["buildTimeoutSeconds"] = args.build_timeout_seconds;
4772
4789
  if (args.log_filter_rules !== void 0) {
4773
4790
  configUpdate["logFilterRules"] = args.log_filter_rules;
4774
4791
  }
@@ -5219,6 +5236,7 @@ defineTool({
5219
5236
  " - volume_id: publicId of the volume to update.",
5220
5237
  " - mount_path (optional): new in-container mount path.",
5221
5238
  " - size_gb (optional): new size in GB (must be \u2265 current).",
5239
+ " - backup_enabled (optional): whether this volume is backed up. Turning it off stops future backups; it does not delete the ones already taken.",
5222
5240
  "",
5223
5241
  "Returns: { volume: Volume } \u2014 the updated record.",
5224
5242
  "",
@@ -5228,13 +5246,15 @@ defineTool({
5228
5246
  service_id: z21.string().describe("Service publicId."),
5229
5247
  volume_id: z21.string().describe("Volume publicId (e.g. vol_\u2026)."),
5230
5248
  mount_path: z21.string().startsWith("/").max(500).optional().describe("New mount path."),
5231
- size_gb: z21.number().int().min(MIN_VOLUME_SIZE_GB).max(100).optional().describe(`New size in GB (minimum ${MIN_VOLUME_SIZE_GB}, grow-only).`)
5249
+ size_gb: z21.number().int().min(MIN_VOLUME_SIZE_GB).max(100).optional().describe(`New size in GB (minimum ${MIN_VOLUME_SIZE_GB}, grow-only).`),
5250
+ backup_enabled: z21.boolean().optional().describe("Whether to keep backing this volume up. Past backups are kept either way.")
5232
5251
  },
5233
5252
  handler: async (args, ctx) => {
5234
5253
  const teamId = await ctx.resolveTeamId();
5235
5254
  const input = {};
5236
5255
  if (args.mount_path !== void 0) input.mountPath = args.mount_path;
5237
5256
  if (args.size_gb !== void 0) input.sizeGb = args.size_gb;
5257
+ if (args.backup_enabled !== void 0) input.backupEnabled = args.backup_enabled;
5238
5258
  if (Object.keys(input).length === 0) {
5239
5259
  return respond({ summary: "No fields to update.", data: {} });
5240
5260
  }