@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.
@@ -8,7 +8,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { HostStack } from "@hoststack.dev/sdk";
9
9
 
10
10
  // src/version.ts
11
- var MCP_VERSION = true ? "0.20.0" : "0.0.0-dev";
11
+ var MCP_VERSION = true ? "0.21.0" : "0.0.0-dev";
12
12
  var USER_AGENT = `hoststack-mcp/${MCP_VERSION}`;
13
13
 
14
14
  // src/api-client.ts
@@ -3458,6 +3458,8 @@ defineTool({
3458
3458
  ' - task_id: "task_\u2026" publicId or numeric id.',
3459
3459
  ' - status: "done" or "idea". Only these two; `queued`/`running`/`failed`/`cancelled` belong to the runner and are rejected.',
3460
3460
  " - title / body / service_id: optional edits.",
3461
+ ' - provider: which agent runs it \u2014 "claude", "codex" or "opencode". null clears the pin and lets the box decide.',
3462
+ ' - 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.',
3461
3463
  "",
3462
3464
  "Only mark a task done when it is actually resolved - the backlog is what someone reads to decide what still needs doing.",
3463
3465
  "",
@@ -3470,7 +3472,11 @@ defineTool({
3470
3472
  status: z17.enum(["idea", "done"]).optional().describe("The only two a person may set. The runner owns the rest of the lifecycle."),
3471
3473
  title: z17.string().min(1).max(200).optional().describe("New title."),
3472
3474
  body: z17.string().max(2e4).optional().describe("New prompt body."),
3473
- 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.")
3475
+ 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
+ provider: z17.enum(["claude", "codex", "opencode"]).nullable().optional().describe("Which agent runs this task, or null to leave it to the box."),
3477
+ permission_mode: z17.enum(["acceptEdits", "bypassPermissions", "default"]).optional().describe(
3478
+ "How the agent may act. bypassPermissions asks for nothing \u2014 set it only on request."
3479
+ )
3474
3480
  },
3475
3481
  handler: async (args2, ctx) => {
3476
3482
  const teamId = await ctx.resolveTeamId();
@@ -3479,7 +3485,10 @@ defineTool({
3479
3485
  ...args2.status ? { status: args2.status } : {},
3480
3486
  ...args2.title ? { title: args2.title } : {},
3481
3487
  ...args2.body !== void 0 ? { body: args2.body } : {},
3482
- ...serviceId !== void 0 ? { serviceId } : {}
3488
+ ...serviceId !== void 0 ? { serviceId } : {},
3489
+ // null is meaningful on provider (unpin), so compare to undefined.
3490
+ ...args2.provider !== void 0 ? { provider: args2.provider } : {},
3491
+ ...args2.permission_mode !== void 0 ? { permissionMode: args2.permission_mode } : {}
3483
3492
  });
3484
3493
  const data = shape(response.task);
3485
3494
  return respond({
@@ -4674,6 +4683,8 @@ defineTool({
4674
4683
  " - instance_count (optional): integer 1\u201350 \u2014 pin both min and max instances to this value.",
4675
4684
  " - min_instances, max_instances (optional): integers \u2014 autoscale bounds. Use instead of instance_count when you want a range.",
4676
4685
  " - scale_cpu_threshold, scale_memory_threshold (optional): integer 10\u2013100 \u2014 autoscale trigger percentage.",
4686
+ " - 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.",
4687
+ " - 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.",
4677
4688
  ` - 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.`,
4678
4689
  "",
4679
4690
  "Returns: { service?: Service, config?: ServiceConfig } \u2014 whichever rows were touched.",
@@ -4714,6 +4725,8 @@ defineTool({
4714
4725
  max_instances: z19.number().int().min(1).max(50).optional().describe("Autoscale upper bound. Use with min_instances for a range."),
4715
4726
  scale_cpu_threshold: z19.number().int().min(10).max(100).optional().describe("Autoscale CPU trigger percentage (10\u2013100)."),
4716
4727
  scale_memory_threshold: z19.number().int().min(10).max(100).optional().describe("Autoscale memory trigger percentage (10\u2013100)."),
4728
+ 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."),
4729
+ 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."),
4717
4730
  log_filter_rules: z19.array(
4718
4731
  z19.object({
4719
4732
  pattern: z19.string().min(1).max(200),
@@ -4768,6 +4781,10 @@ defineTool({
4768
4781
  configUpdate["scaleCpuThreshold"] = args2.scale_cpu_threshold;
4769
4782
  if (args2.scale_memory_threshold !== void 0)
4770
4783
  configUpdate["scaleMemoryThreshold"] = args2.scale_memory_threshold;
4784
+ if (args2.scale_requests_per_sec_threshold !== void 0)
4785
+ configUpdate["scaleRequestsPerSecThreshold"] = args2.scale_requests_per_sec_threshold;
4786
+ if (args2.build_timeout_seconds !== void 0)
4787
+ configUpdate["buildTimeoutSeconds"] = args2.build_timeout_seconds;
4771
4788
  if (args2.log_filter_rules !== void 0) {
4772
4789
  configUpdate["logFilterRules"] = args2.log_filter_rules;
4773
4790
  }
@@ -5218,6 +5235,7 @@ defineTool({
5218
5235
  " - volume_id: publicId of the volume to update.",
5219
5236
  " - mount_path (optional): new in-container mount path.",
5220
5237
  " - size_gb (optional): new size in GB (must be \u2265 current).",
5238
+ " - backup_enabled (optional): whether this volume is backed up. Turning it off stops future backups; it does not delete the ones already taken.",
5221
5239
  "",
5222
5240
  "Returns: { volume: Volume } \u2014 the updated record.",
5223
5241
  "",
@@ -5227,13 +5245,15 @@ defineTool({
5227
5245
  service_id: z21.string().describe("Service publicId."),
5228
5246
  volume_id: z21.string().describe("Volume publicId (e.g. vol_\u2026)."),
5229
5247
  mount_path: z21.string().startsWith("/").max(500).optional().describe("New mount path."),
5230
- 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).`)
5248
+ 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
+ backup_enabled: z21.boolean().optional().describe("Whether to keep backing this volume up. Past backups are kept either way.")
5231
5250
  },
5232
5251
  handler: async (args2, ctx) => {
5233
5252
  const teamId = await ctx.resolveTeamId();
5234
5253
  const input = {};
5235
5254
  if (args2.mount_path !== void 0) input.mountPath = args2.mount_path;
5236
5255
  if (args2.size_gb !== void 0) input.sizeGb = args2.size_gb;
5256
+ if (args2.backup_enabled !== void 0) input.backupEnabled = args2.backup_enabled;
5237
5257
  if (Object.keys(input).length === 0) {
5238
5258
  return respond({ summary: "No fields to update.", data: {} });
5239
5259
  }