@gethmy/mcp 2.13.3 → 2.14.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
@@ -1452,6 +1452,8 @@ function serializeCommentThread(comments, options = {}) {
1452
1452
  const tags = [];
1453
1453
  if (c.edited_at)
1454
1454
  tags.push("edited");
1455
+ if (c.reply_to_id)
1456
+ tags.push(`reply to ${ref(c.reply_to_id)}`);
1455
1457
  if (c.supersedes_id)
1456
1458
  tags.push(`supersedes ${ref(c.supersedes_id)}`);
1457
1459
  if (c.confirms_id)
@@ -1863,6 +1865,12 @@ class HarmonyApiClient {
1863
1865
  async getCardExternalLinks(cardId) {
1864
1866
  return this.request("GET", `/cards/${cardId}/external-links`);
1865
1867
  }
1868
+ async addExternalLink(cardId, url, title) {
1869
+ return this.request("POST", `/cards/${cardId}/external-links`, {
1870
+ url,
1871
+ title
1872
+ });
1873
+ }
1866
1874
  async uploadArtifact(data) {
1867
1875
  return this.request("POST", "/artifacts", data);
1868
1876
  }
@@ -1914,6 +1922,7 @@ class HarmonyApiClient {
1914
1922
  commentType: opts?.commentType,
1915
1923
  supersedesId: opts?.supersedesId,
1916
1924
  confirmsId: opts?.confirmsId,
1925
+ replyToId: opts?.replyToId,
1917
1926
  agentSessionId: opts?.agentSessionId
1918
1927
  });
1919
1928
  }
@@ -2366,12 +2375,6 @@ ${planContent.trim()}`;
2366
2375
  async updatePlaybook(playbookId, updates) {
2367
2376
  return this.request("PATCH", `/playbooks/${playbookId}`, updates);
2368
2377
  }
2369
- async runPlaybook(playbookId) {
2370
- return this.request("POST", `/playbooks/${playbookId}/run`);
2371
- }
2372
- async savePlaybookFromCard(data) {
2373
- return this.request("POST", "/playbooks/from-card", data);
2374
- }
2375
2378
  }
2376
2379
  var _promptModules = null;
2377
2380
  async function loadPromptModules() {
@@ -3235,7 +3238,7 @@ Start work on a Harmony card. Card reference: $ARGUMENTS
3235
3238
  ## 1. Find & Fetch Card
3236
3239
 
3237
3240
  Parse the reference and fetch the card:
3238
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
3241
+ - \`#42\` or \`42\` → \`harmony_get_card\` with \`shortId: 42\`
3239
3242
  - UUID → \`harmony_get_card\` with \`cardId\`
3240
3243
  - Name/text → \`harmony_search_cards\` with \`query\`
3241
3244
 
@@ -3289,7 +3292,7 @@ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
3289
3292
 
3290
3293
  ## Key Tools Reference
3291
3294
 
3292
- **Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
3295
+ **Cards:** \`harmony_get_card\` (by \`cardId\`, \`shortId\`, or \`shortIds\`), \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
3293
3296
 
3294
3297
  **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
3295
3298
 
@@ -3483,6 +3486,37 @@ function requireExactlyOneScope(scope) {
3483
3486
  throw new Error("Provide exactly one of cardId, planId, or workspaceId.");
3484
3487
  }
3485
3488
  }
3489
+ var DEPRECATED_TOOL_ALIASES = {
3490
+ harmony_get_card_by_short_id: "harmony_get_card",
3491
+ harmony_bulk_get_cards: "harmony_get_card",
3492
+ harmony_upload_artifact: "harmony_upload",
3493
+ harmony_upload_card_attachment: "harmony_upload",
3494
+ harmony_request_artifact_upload_url: "harmony_request_upload_url",
3495
+ harmony_request_card_attachment_upload_url: "harmony_request_upload_url",
3496
+ harmony_finalize_artifact: "harmony_finalize_upload",
3497
+ harmony_finalize_card_attachment: "harmony_finalize_upload"
3498
+ };
3499
+ var _warnedDeprecatedTools = new Set;
3500
+ function warnDeprecatedTool(oldName) {
3501
+ if (_warnedDeprecatedTools.has(oldName))
3502
+ return;
3503
+ _warnedDeprecatedTools.add(oldName);
3504
+ const replacement = DEPRECATED_TOOL_ALIASES[oldName];
3505
+ console.error(`[harmony-mcp] Tool "${oldName}" is deprecated (still accepted) — ` + `use "${replacement}" instead. The old name is no longer advertised and ` + `will be removed in a future major version.`);
3506
+ }
3507
+ var DEPRECATED_REMOVED_TOOLS = {
3508
+ harmony_run_playbook: "Playbooks are stage models run by the board and agent daemon as a card moves through their stages — there is no server-side macro to trigger. This tool no longer runs anything.",
3509
+ harmony_save_card_as_playbook: "The steps_version=1 automation macro was removed. Build a stage playbook with harmony_create_playbook instead."
3510
+ };
3511
+ var _warnedRemovedTools = new Set;
3512
+ function deprecatedRemovedToolResult(name) {
3513
+ const message = DEPRECATED_REMOVED_TOOLS[name];
3514
+ if (!_warnedRemovedTools.has(name)) {
3515
+ _warnedRemovedTools.add(name);
3516
+ console.error(`[harmony-mcp] Tool "${name}" is deprecated and no longer functional — ${message}`);
3517
+ }
3518
+ return { success: false, deprecated: true, message };
3519
+ }
3486
3520
  var SIGNED_UPLOAD_TIMEOUT_MS = 30000;
3487
3521
  async function putToSignedUrl(uploadUrl, bytes, contentType) {
3488
3522
  let res;
@@ -3810,44 +3844,29 @@ var TOOLS = {
3810
3844
  }
3811
3845
  },
3812
3846
  harmony_get_card: {
3813
- description: "Get detailed information about a specific card by UUID",
3847
+ description: "Fetch one or many cards. Provide exactly one of: `cardId` (UUID, full detail), " + "`shortId` (e.g. 42, full detail), or `shortIds` (array, max 100 — compact " + "summaries: id, shortId, title, column, priority, assignee, labels, archived, " + "plus any short ids not found). `shortId`/`shortIds` need project context. " + "Prefer one `shortIds` call over repeated single fetches for multiple cards.",
3814
3848
  inputSchema: {
3815
3849
  type: "object",
3816
3850
  properties: {
3817
- cardId: { type: "string", description: "Card UUID" }
3818
- },
3819
- required: ["cardId"]
3820
- }
3821
- },
3822
- harmony_get_card_by_short_id: {
3823
- description: "Get a card by its short ID (e.g., #42) within a project",
3824
- inputSchema: {
3825
- type: "object",
3826
- properties: {
3827
- projectId: {
3851
+ cardId: {
3828
3852
  type: "string",
3829
- description: "Project ID (optional if context set)"
3853
+ description: "Card UUID full detail for one card."
3830
3854
  },
3831
3855
  shortId: {
3832
3856
  type: "number",
3833
- description: "Short ID number (e.g., 42 for card #42)"
3834
- }
3835
- },
3836
- required: ["shortId"]
3837
- }
3838
- },
3839
- harmony_bulk_get_cards: {
3840
- description: "Fetch multiple cards by short id in one call. Returns compact summaries " + "(id, shortId, title, column, priority, assignee, labels, archived) plus " + "any short ids not found. Requires project context. Prefer this over " + "repeated harmony_get_card_by_short_id when referencing multiple cards.",
3841
- inputSchema: {
3842
- type: "object",
3843
- properties: {
3857
+ description: "Short ID (e.g. 42 for card #42) — full detail, project-scoped."
3858
+ },
3844
3859
  shortIds: {
3845
3860
  type: "array",
3846
3861
  items: { type: "number" },
3847
- description: "Card short ids, e.g. [400, 401, 402]. Max 100 per call."
3862
+ description: "Short IDs, e.g. [400, 401, 402] compact summaries in one call. Max 100."
3863
+ },
3864
+ projectId: {
3865
+ type: "string",
3866
+ description: "Project ID for shortId/shortIds (optional if context set)."
3848
3867
  }
3849
3868
  },
3850
- required: ["shortIds"]
3869
+ required: []
3851
3870
  }
3852
3871
  },
3853
3872
  harmony_bulk_archive_cards: {
@@ -4002,174 +4021,144 @@ var TOOLS = {
4002
4021
  required: ["cardId"]
4003
4022
  }
4004
4023
  },
4005
- harmony_upload_card_attachment: {
4006
- description: "Upload a file attachment to a card (e.g. a pasted screenshot or a document). Provide the file either as `filePath` (a local path the MCP server can read works in local/stdio mode) or as `base64Data` (raw base64 bytes works everywhere, including remote mode). With `filePath` the server uploads direct-to-storage (no base64 through the model context); `base64Data` is the small-file fallback. Max 5MB. Allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT, CSV. Returns the stored attachment with a short-lived signed URL. For large files on the hosted MCP server (where only base64Data works), prefer the two-step harmony_request_card_attachment_upload_url + harmony_finalize_card_attachment handshake, which keeps bytes out of the model context.",
4024
+ harmony_upload: {
4025
+ description: 'Upload a file in one call. `target: "card_attachment"` attaches a file to a card ' + "(max 5MB; PNG/JPEG/GIF/WebP/HEIC/HEIF/PDF/DOC(X)/XLS(X)/TXT/CSV) requires cardId. " + '`target: "artifact"` hosts a self-contained HTML doc (text/html, max 2MB) linked to ' + "exactly one of cardId/planId/workspaceId, rendered in-app in a sandboxed iframe. Provide " + "the bytes as `filePath` (local, direct-to-storage) or `base64Data` (small-file fallback). " + "Returns the attachment/artifact + a signed URL (artifact: use harmony_share_artifact for a " + "public link). Large files on the hosted MCP server: use harmony_request_upload_url + " + "harmony_finalize_upload instead.",
4007
4026
  inputSchema: {
4008
4027
  type: "object",
4009
4028
  properties: {
4010
- cardId: { type: "string", description: "Card UUID" },
4029
+ target: {
4030
+ type: "string",
4031
+ enum: ["card_attachment", "artifact"],
4032
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4033
+ },
4034
+ cardId: {
4035
+ type: "string",
4036
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4037
+ },
4038
+ planId: {
4039
+ type: "string",
4040
+ description: "Plan UUID (artifact only — link the artifact to a plan)."
4041
+ },
4042
+ workspaceId: {
4043
+ type: "string",
4044
+ description: "Workspace UUID (artifact only — standalone artifact)."
4045
+ },
4011
4046
  filePath: {
4012
4047
  type: "string",
4013
- description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
4048
+ description: "Absolute path to a local file the server can read. Mutually exclusive with base64Data."
4014
4049
  },
4015
4050
  base64Data: {
4016
4051
  type: "string",
4017
- description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
4052
+ description: "Base64-encoded bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4018
4053
  },
4019
4054
  fileName: {
4020
4055
  type: "string",
4021
- description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
4056
+ description: "File name including extension (card_attachment: required with base64Data, else defaults to the filePath basename)."
4057
+ },
4058
+ title: {
4059
+ type: "string",
4060
+ description: "Artifact display title (defaults to the file basename)."
4022
4061
  },
4023
4062
  contentType: {
4024
4063
  type: "string",
4025
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4064
+ description: "Optional MIME type (card_attachment; inferred from the extension when omitted)."
4026
4065
  }
4027
4066
  },
4028
- required: ["cardId"]
4067
+ required: ["target"]
4029
4068
  }
4030
4069
  },
4031
- harmony_request_card_attachment_upload_url: {
4032
- description: "Step 1 of the agent-driven card-attachment upload use this for large files or when running against the hosted MCP server (which cannot read your local disk). Mints a one-shot signed Supabase Storage upload URL for a server-chosen path under the card. Provide cardId, fileName (with extension), the byte size, and optionally fileType. Returns { uploadUrl, token, storagePath, fileType }. Next: PUT the raw bytes straight to uploadUrl (e.g. `curl -X PUT --data-binary @file.png '<uploadUrl>'`) — no bytes pass through the model context then call harmony_finalize_card_attachment with the returned storagePath. Max 5MB; allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT, CSV.",
4070
+ harmony_request_upload_url: {
4071
+ description: "Step 1 of the large-file / hosted-MCP upload handshake (use when the server can't read " + 'local disk). Mints a one-shot signed Storage upload URL. `target: "card_attachment"` ' + '(requires cardId, fileName, size; max 5MB) or `target: "artifact"` (one of ' + "cardId/planId/workspaceId; text/html, max 2MB). Returns { uploadUrl, token, storagePath, " + "[fileType] }. Then PUT the raw bytes to uploadUrl (no bytes through the model context) and " + "call harmony_finalize_upload with the storagePath.",
4033
4072
  inputSchema: {
4034
4073
  type: "object",
4035
4074
  properties: {
4036
- cardId: { type: "string", description: "Card UUID" },
4037
- fileName: {
4075
+ target: {
4038
4076
  type: "string",
4039
- description: "File name including extension (e.g. 'screenshot.png')."
4077
+ enum: ["card_attachment", "artifact"],
4078
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4040
4079
  },
4041
- fileType: {
4080
+ cardId: {
4042
4081
  type: "string",
4043
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4082
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4044
4083
  },
4045
- size: {
4046
- type: "number",
4047
- description: "File size in bytes (rejected early if over 5MB)."
4048
- }
4049
- },
4050
- required: ["cardId", "fileName", "size"]
4051
- }
4052
- },
4053
- harmony_finalize_card_attachment: {
4054
- description: "Step 2 of the agent-driven card-attachment upload. After PUTting the bytes to the signed uploadUrl from harmony_request_card_attachment_upload_url, call this with the returned storagePath to validate and register the attachment. The server re-downloads the object and enforces size, an allowlisted content-type (magic-byte sniff, never the declared type), and — when you pass sha256 — an integrity check, deleting the object and failing on any mismatch. Returns the stored attachment with a short-lived signed URL.",
4055
- inputSchema: {
4056
- type: "object",
4057
- properties: {
4058
- cardId: { type: "string", description: "Card UUID" },
4059
- storagePath: {
4084
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4085
+ workspaceId: {
4060
4086
  type: "string",
4061
- description: "The storagePath returned by harmony_request_card_attachment_upload_url."
4087
+ description: "Workspace UUID (artifact only)."
4062
4088
  },
4063
4089
  fileName: {
4064
4090
  type: "string",
4065
- description: "File name including extension (e.g. 'screenshot.png')."
4091
+ description: "File name including extension (required for card_attachment)."
4066
4092
  },
4093
+ title: { type: "string", description: "Artifact display title." },
4067
4094
  fileType: {
4068
4095
  type: "string",
4069
- description: "Optional MIME type; inferred from the extension when omitted."
4096
+ description: "card_attachment MIME type (inferred from the extension when omitted)."
4070
4097
  },
4071
- sha256: {
4098
+ contentType: {
4072
4099
  type: "string",
4073
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4100
+ description: "artifact MIME type; only 'text/html' is accepted (the default)."
4074
4101
  },
4075
4102
  size: {
4076
4103
  type: "number",
4077
- description: "Optional byte size (advisory; re-validated server-side)."
4104
+ description: "File size in bytes (rejected early if over the target's limit)."
4078
4105
  }
4079
4106
  },
4080
- required: ["cardId", "storagePath", "fileName"]
4081
- }
4082
- },
4083
- harmony_classify_card: {
4084
- description: "Classify a card with the LLM classifier: sets `intent` (plan/think/implement/review), `complexity_score` (0-10), and `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the canonical type label (feature/bug/idea). Use this right after creating a card (e.g. in the `hmy-new` flow) so it's classified in-flow instead of waiting for it to surface on the web board. Idempotent — safe to re-run. Never touches the user-owned `model_override`.",
4085
- inputSchema: {
4086
- type: "object",
4087
- properties: {
4088
- cardId: { type: "string", description: "Card UUID" }
4089
- },
4090
- required: ["cardId"]
4107
+ required: ["target"]
4091
4108
  }
4092
4109
  },
4093
- harmony_upload_artifact: {
4094
- description: "Host a self-contained HTML document (e.g. a visual design draft or diagram) in Harmony and link it to a card, a plan, or a workspace. The file is stored privately and rendered in-app inside a sandboxed cross-origin iframe. Provide exactly one of cardId, planId, or workspaceId. Supply the HTML as `filePath` (a local path the MCP server can read uploaded direct-to-storage, no base64 through the model context) or `base64Data` (the small-file fallback). Only text/html, max 2MB. Returns the stored artifact with a short-lived signed URL; call harmony_share_artifact to mint a public link. For large files on the hosted MCP server (where only base64Data works), prefer the two-step harmony_request_artifact_upload_url + harmony_finalize_artifact handshake, which keeps bytes out of the model context.",
4110
+ harmony_finalize_upload: {
4111
+ description: "Step 2 of the upload handshake. After PUTting the bytes to the signed uploadUrl, call this " + "with the storagePath to validate and register. The server re-downloads the object and " + "enforces size + content-type (magic-byte sniff, never the declared type) + an optional " + 'sha256 integrity check, deleting and failing on any mismatch. `target: "card_attachment"` ' + '(cardId, storagePath, fileName) or `target: "artifact"` (storagePath + the same one of ' + "cardId/planId/workspaceId used for the upload URL). Returns the attachment/artifact + a signed URL.",
4095
4112
  inputSchema: {
4096
4113
  type: "object",
4097
4114
  properties: {
4098
- title: {
4115
+ target: {
4099
4116
  type: "string",
4100
- description: "Display title (defaults to the file basename)."
4117
+ enum: ["card_attachment", "artifact"],
4118
+ description: "What to finalize: a card file attachment or a hosted HTML artifact."
4101
4119
  },
4102
- cardId: { type: "string", description: "Link to this card (UUID)." },
4103
- planId: { type: "string", description: "Link to this plan (UUID)." },
4104
- workspaceId: {
4120
+ storagePath: {
4105
4121
  type: "string",
4106
- description: "Attach to this workspace as a standalone artifact (UUID)."
4122
+ description: "The storagePath returned by harmony_request_upload_url."
4107
4123
  },
4108
- filePath: {
4124
+ cardId: {
4109
4125
  type: "string",
4110
- description: "Absolute path to a local .html file the MCP server process can read. Mutually exclusive with base64Data."
4126
+ description: "Card UUID. Required for card_attachment; scope for artifact."
4111
4127
  },
4112
- base64Data: {
4128
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4129
+ workspaceId: {
4113
4130
  type: "string",
4114
- description: "Base64-encoded HTML bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4115
- }
4116
- },
4117
- required: []
4118
- }
4119
- },
4120
- harmony_request_artifact_upload_url: {
4121
- description: "Step 1 of the agent-driven artifact upload — use this for large files or when running against the hosted MCP server (which cannot read your local disk), instead of streaming base64 through the model context. Mints a one-shot signed Supabase Storage upload URL for a server-chosen path. Provide exactly one of cardId/planId/workspaceId, optionally a title and the byte size. Returns { uploadUrl, token, storagePath }. Next: PUT the raw HTML bytes straight to uploadUrl (e.g. `curl -X PUT --data-binary @doc.html '<uploadUrl>'`), then call harmony_finalize_artifact with the returned storagePath. Only text/html, max 2MB.",
4122
- inputSchema: {
4123
- type: "object",
4124
- properties: {
4125
- title: {
4131
+ description: "Workspace UUID (artifact only)."
4132
+ },
4133
+ fileName: {
4126
4134
  type: "string",
4127
- description: "Display title (defaults to the file basename at finalize)."
4135
+ description: "File name including extension (required for card_attachment)."
4128
4136
  },
4129
- cardId: { type: "string", description: "Link to this card (UUID)." },
4130
- planId: { type: "string", description: "Link to this plan (UUID)." },
4131
- workspaceId: {
4137
+ title: { type: "string", description: "Artifact display title." },
4138
+ fileType: {
4132
4139
  type: "string",
4133
- description: "Attach to this workspace as a standalone artifact (UUID)."
4140
+ description: "card_attachment MIME type; inferred from the extension when omitted."
4134
4141
  },
4135
- contentType: {
4142
+ sha256: {
4136
4143
  type: "string",
4137
- description: "MIME type; only 'text/html' is accepted (the default)."
4144
+ description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4138
4145
  },
4139
4146
  size: {
4140
4147
  type: "number",
4141
- description: "File size in bytes (rejected early if over 2MB)."
4148
+ description: "Optional byte size (advisory; re-validated server-side)."
4142
4149
  }
4143
4150
  },
4144
- required: []
4151
+ required: ["target", "storagePath"]
4145
4152
  }
4146
4153
  },
4147
- harmony_finalize_artifact: {
4148
- description: "Step 2 of the agent-driven artifact upload. After PUTting the HTML bytes to the signed uploadUrl from harmony_request_artifact_upload_url, call this with the returned storagePath to validate and register the artifact. The server re-downloads the object and enforces size, the text/html content-type (magic-byte sniff), and — when you pass sha256 — an integrity check, deleting the object and failing on any mismatch. Provide the same one of cardId/planId/workspaceId used for the upload URL. Returns the stored artifact with a short-lived signed URL; call harmony_share_artifact to mint a public link.",
4154
+ harmony_classify_card: {
4155
+ description: "Classify a card with the LLM classifier: sets `intent` (plan/think/implement/review), `complexity_score` (0-10), `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the type label (feature/bug/idea). Call right after creating a card to classify it in-flow. Idempotent; never touches the user-owned `model_override`.",
4149
4156
  inputSchema: {
4150
4157
  type: "object",
4151
4158
  properties: {
4152
- storagePath: {
4153
- type: "string",
4154
- description: "The storagePath returned by harmony_request_artifact_upload_url."
4155
- },
4156
- title: { type: "string", description: "Display title." },
4157
- cardId: { type: "string", description: "Link to this card (UUID)." },
4158
- planId: { type: "string", description: "Link to this plan (UUID)." },
4159
- workspaceId: {
4160
- type: "string",
4161
- description: "Attach to this workspace as a standalone artifact (UUID)."
4162
- },
4163
- sha256: {
4164
- type: "string",
4165
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4166
- },
4167
- size: {
4168
- type: "number",
4169
- description: "Optional byte size (advisory; re-validated server-side)."
4170
- }
4159
+ cardId: { type: "string", description: "Card UUID" }
4171
4160
  },
4172
- required: ["storagePath"]
4161
+ required: ["cardId"]
4173
4162
  }
4174
4163
  },
4175
4164
  harmony_share_artifact: {
@@ -4199,6 +4188,27 @@ var TOOLS = {
4199
4188
  required: ["cardId"]
4200
4189
  }
4201
4190
  },
4191
+ harmony_add_external_link: {
4192
+ description: "Attach an external reference URL (e.g. a PR/MR link, doc, or dashboard) to a card. Durable — survives description edits. Use this in addition to writing the PR link into the description.",
4193
+ inputSchema: {
4194
+ type: "object",
4195
+ properties: {
4196
+ cardId: {
4197
+ type: "string",
4198
+ description: "Card UUID"
4199
+ },
4200
+ url: {
4201
+ type: "string",
4202
+ description: "The URL to attach"
4203
+ },
4204
+ title: {
4205
+ type: "string",
4206
+ description: "Optional human-readable title for the link"
4207
+ }
4208
+ },
4209
+ required: ["cardId", "url"]
4210
+ }
4211
+ },
4202
4212
  harmony_create_subtask: {
4203
4213
  description: "Create a subtask on a card",
4204
4214
  inputSchema: {
@@ -4250,7 +4260,7 @@ var TOOLS = {
4250
4260
  }
4251
4261
  },
4252
4262
  harmony_add_comment: {
4253
- description: "Post a comment on a card as the agent. Use this to converse with the human in the open: report progress, ask a question, record a decision, or note a finding instead of editing the card description. Set supersedesId to correct an earlier comment, confirmsId to reaffirm one. When the thread conflicts, prefer the latest comment unless a later one confirms an earlier finding; cite the comment id(s) you relied on.",
4263
+ description: "Post a comment on a card as the agent converse with the human in the open: report progress, ask a question, record a decision, or note a finding, instead of editing the card description. Set supersedesId to correct an earlier comment, confirmsId to reaffirm one. To answer an open question, reply to it with replyToId.",
4254
4264
  inputSchema: {
4255
4265
  type: "object",
4256
4266
  properties: {
@@ -4276,6 +4286,10 @@ var TOOLS = {
4276
4286
  confirmsId: {
4277
4287
  type: "string",
4278
4288
  description: "Comment id this comment reaffirms"
4289
+ },
4290
+ replyToId: {
4291
+ type: "string",
4292
+ description: "Comment id this is a one-level reply to. To answer an open question, reply to it. If you also set supersedesId/confirmsId, it must equal replyToId."
4279
4293
  }
4280
4294
  },
4281
4295
  required: ["cardId", "body"]
@@ -4747,7 +4761,7 @@ var TOOLS = {
4747
4761
  limit: { type: "number", description: "Max results (default: 20)" },
4748
4762
  top_k: {
4749
4763
  type: "number",
4750
- description: "After Park rescoring (relevance × recency × importance per plan §6), keep this many top entries. Defaults to `limit`. Useful when you want to over-fetch (large `limit`) so the rescorer has more candidates and then trim to a tight top-k."
4764
+ description: "After rescoring (relevance × recency × importance), keep this many top entries. Defaults to `limit`. Over-fetch with a large `limit` then trim to a tight top-k."
4751
4765
  },
4752
4766
  budget_tokens: {
4753
4767
  type: "number",
@@ -4862,7 +4876,7 @@ var TOOLS = {
4862
4876
  }
4863
4877
  },
4864
4878
  harmony_suggest_relations: {
4865
- description: "Suggest existing memory entities that look related to a given entity, using the same hybrid vector+FTS similarity as recall (card #281). Read-only: it NEVER creates relations — it only proposes candidates for a caller (human/agent) to relate explicitly via harmony_relate. Use to surface 'these look related — link?' candidates that would activate graph-walk retrieval.",
4879
+ description: "Suggest existing memory entities that look related to a given entity, using the same hybrid vector+FTS similarity as recall. Read-only: NEVER creates relations — only proposes candidates to relate explicitly via harmony_relate. Surfaces 'these look related — link?' candidates that would activate graph-walk retrieval.",
4866
4880
  inputSchema: {
4867
4881
  type: "object",
4868
4882
  properties: {
@@ -5146,7 +5160,7 @@ var TOOLS = {
5146
5160
  }
5147
5161
  },
5148
5162
  harmony_list_playbook: {
5149
- description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, steps_version, and state. Read-only.",
5163
+ description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, and state. Read-only.",
5150
5164
  inputSchema: {
5151
5165
  type: "object",
5152
5166
  properties: {
@@ -5168,21 +5182,8 @@ var TOOLS = {
5168
5182
  required: ["playbookId"]
5169
5183
  }
5170
5184
  },
5171
- harmony_run_playbook: {
5172
- description: "Run a playbook server-side and return the finalized run. Only legacy automation playbooks (steps_version 1) are runnable; stage playbooks (steps_version 2) are rejected. The server drives every step to completion.",
5173
- inputSchema: {
5174
- type: "object",
5175
- properties: {
5176
- playbookId: {
5177
- type: "string",
5178
- description: "Playbook ID to run (UUID)"
5179
- }
5180
- },
5181
- required: ["playbookId"]
5182
- }
5183
- },
5184
5185
  harmony_create_playbook: {
5185
- description: "Create a new playbook in a workspace. Default steps_version 1 is a legacy automation macro (an array of tool steps); steps_version 2 is the Method stage model (an array of stage objects).",
5186
+ description: "Create a new playbook (a reusable process definition) in a workspace. A playbook is an ordered set of stages bound to board columns, run by people and the agent daemon as a card moves through them.",
5186
5187
  inputSchema: {
5187
5188
  type: "object",
5188
5189
  properties: {
@@ -5192,14 +5193,9 @@ var TOOLS = {
5192
5193
  },
5193
5194
  name: { type: "string", description: "Playbook name" },
5194
5195
  description: { type: "string", description: "Playbook description" },
5195
- stepsVersion: {
5196
- type: "number",
5197
- enum: [1, 2],
5198
- description: "1 = automation macro (tool steps), 2 = Method stage model (stage objects). Default 1."
5199
- },
5200
5196
  steps: {
5201
5197
  type: "array",
5202
- description: "Steps (steps_version 1: tool-step objects) or stages (steps_version 2: stage objects).",
5198
+ description: "The playbook's ordered stage objects.",
5203
5199
  items: { type: "object" }
5204
5200
  }
5205
5201
  },
@@ -5219,7 +5215,7 @@ var TOOLS = {
5219
5215
  description: { type: "string", description: "New description" },
5220
5216
  steps: {
5221
5217
  type: "array",
5222
- description: "New steps (v1) or stages (v2) array.",
5218
+ description: "New ordered stage objects.",
5223
5219
  items: { type: "object" }
5224
5220
  },
5225
5221
  enabled: {
@@ -5235,20 +5231,6 @@ var TOOLS = {
5235
5231
  required: ["playbookId"]
5236
5232
  }
5237
5233
  },
5238
- harmony_save_card_as_playbook: {
5239
- description: "Save an existing card as a new steps_version 1 (automation) playbook, seeding one create-card step from the card. Returns the created playbook.",
5240
- inputSchema: {
5241
- type: "object",
5242
- properties: {
5243
- cardId: { type: "string", description: "Card ID to template (UUID)" },
5244
- name: {
5245
- type: "string",
5246
- description: "Name for the new playbook (defaults to the card title)"
5247
- }
5248
- },
5249
- required: ["cardId"]
5250
- }
5251
- },
5252
5234
  harmony_signup: {
5253
5235
  description: "Create a new user account. Returns a JWT session for subsequent authenticated calls. No API key required.",
5254
5236
  inputSchema: {
@@ -5648,23 +5630,33 @@ async function handleToolCall(name, args, deps) {
5648
5630
  });
5649
5631
  return { success: true, ...result, count: result.cards.length };
5650
5632
  }
5633
+ case "harmony_get_card_by_short_id":
5634
+ case "harmony_bulk_get_cards":
5651
5635
  case "harmony_get_card": {
5636
+ if (name !== "harmony_get_card")
5637
+ warnDeprecatedTool(name);
5638
+ const hasShortIds = args.shortIds != null;
5639
+ const hasShortId = args.shortId != null;
5640
+ const hasCardId = args.cardId != null;
5641
+ if ([hasShortIds, hasShortId, hasCardId].filter(Boolean).length !== 1) {
5642
+ throw new Error("Provide exactly one of: cardId (UUID), shortId (number), or shortIds (number[]).");
5643
+ }
5644
+ if (hasShortIds) {
5645
+ const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5646
+ const projectId = getProjectId();
5647
+ const result2 = await client3.bulkGetCards(projectId, shortIds);
5648
+ return { success: true, ...result2 };
5649
+ }
5650
+ if (hasShortId) {
5651
+ const shortId = z.number().int().positive().parse(args.shortId);
5652
+ const projectId = args.projectId || getProjectId();
5653
+ const result2 = await client3.getCardByShortId(projectId, shortId);
5654
+ return { success: true, ...result2 };
5655
+ }
5652
5656
  const cardId = z.string().uuid().parse(args.cardId);
5653
5657
  const result = await client3.getCard(cardId);
5654
5658
  return { success: true, ...result };
5655
5659
  }
5656
- case "harmony_get_card_by_short_id": {
5657
- const shortId = z.number().int().positive().parse(args.shortId);
5658
- const projectId = args.projectId || getProjectId();
5659
- const result = await client3.getCardByShortId(projectId, shortId);
5660
- return { success: true, ...result };
5661
- }
5662
- case "harmony_bulk_get_cards": {
5663
- const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5664
- const projectId = getProjectId();
5665
- const result = await client3.bulkGetCards(projectId, shortIds);
5666
- return { success: true, ...result };
5667
- }
5668
5660
  case "harmony_bulk_archive_cards": {
5669
5661
  const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5670
5662
  const projectId = getProjectId();
@@ -5764,58 +5756,58 @@ async function handleToolCall(name, args, deps) {
5764
5756
  const result = await client3.getCardAttachments(cardId);
5765
5757
  return result;
5766
5758
  }
5767
- case "harmony_upload_card_attachment": {
5768
- const cardId = z.string().uuid().parse(args.cardId);
5759
+ case "harmony_upload_card_attachment":
5760
+ case "harmony_upload_artifact":
5761
+ case "harmony_upload": {
5762
+ if (name !== "harmony_upload")
5763
+ warnDeprecatedTool(name);
5764
+ const target = name === "harmony_upload_card_attachment" ? "card_attachment" : name === "harmony_upload_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5769
5765
  const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5770
5766
  const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5771
- const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5772
- const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5773
5767
  if (filePath && base64Data) {
5774
5768
  throw new Error("Provide either filePath or base64Data, not both.");
5775
5769
  }
5776
- if (filePath) {
5777
- const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5778
- const resolvedName = fileName || basename(filePath);
5779
- const signed = await client3.requestCardAttachmentUploadUrl(cardId, {
5780
- fileName: resolvedName,
5781
- fileType: contentType,
5782
- size: bytes.byteLength
5783
- });
5784
- await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5785
- return await client3.finalizeCardAttachment(cardId, {
5786
- storagePath: signed.storagePath,
5787
- fileName: resolvedName,
5788
- fileType: contentType || signed.fileType,
5789
- sha256: sha256Hex(bytes),
5790
- size: bytes.byteLength
5791
- });
5792
- }
5793
- if (base64Data) {
5794
- if (!fileName) {
5795
- throw new Error("fileName is required when using base64Data.");
5770
+ if (target === "card_attachment") {
5771
+ const cardId2 = z.string().uuid().parse(args.cardId);
5772
+ const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5773
+ const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5774
+ if (filePath) {
5775
+ const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5776
+ const resolvedName = fileName || basename(filePath);
5777
+ const signed = await client3.requestCardAttachmentUploadUrl(cardId2, {
5778
+ fileName: resolvedName,
5779
+ fileType: contentType,
5780
+ size: bytes.byteLength
5781
+ });
5782
+ await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5783
+ return await client3.finalizeCardAttachment(cardId2, {
5784
+ storagePath: signed.storagePath,
5785
+ fileName: resolvedName,
5786
+ fileType: contentType || signed.fileType,
5787
+ sha256: sha256Hex(bytes),
5788
+ size: bytes.byteLength
5789
+ });
5796
5790
  }
5797
- if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5798
- throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_card_attachment_upload_url + harmony_finalize_card_attachment handshake for large files.`);
5791
+ if (base64Data) {
5792
+ if (!fileName) {
5793
+ throw new Error("fileName is required when using base64Data.");
5794
+ }
5795
+ if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5796
+ throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5797
+ }
5798
+ return await client3.uploadCardAttachment(cardId2, {
5799
+ fileName,
5800
+ data: base64Data,
5801
+ fileType: contentType
5802
+ });
5799
5803
  }
5800
- return await client3.uploadCardAttachment(cardId, {
5801
- fileName,
5802
- data: base64Data,
5803
- fileType: contentType
5804
- });
5804
+ throw new Error("Provide either filePath or base64Data.");
5805
5805
  }
5806
- throw new Error("Provide either filePath or base64Data.");
5807
- }
5808
- case "harmony_upload_artifact": {
5809
5806
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5810
5807
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5811
5808
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
5812
5809
  const workspaceId = args.workspaceId != null ? z.string().uuid().parse(args.workspaceId) : undefined;
5813
5810
  requireExactlyOneScope({ cardId, planId, workspaceId });
5814
- const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5815
- const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5816
- if (filePath && base64Data) {
5817
- throw new Error("Provide either filePath or base64Data, not both.");
5818
- }
5819
5811
  if (filePath) {
5820
5812
  const bytes = await readFileForUpload(filePath, MAX_ARTIFACT_SIZE, "artifact");
5821
5813
  const resolvedTitle = title || basename(filePath);
@@ -5840,7 +5832,7 @@ async function handleToolCall(name, args, deps) {
5840
5832
  }
5841
5833
  if (base64Data) {
5842
5834
  if (base64ByteLength(base64Data) > MAX_ARTIFACT_SIZE) {
5843
- throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_artifact_upload_url + harmony_finalize_artifact handshake for large files.`);
5835
+ throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5844
5836
  }
5845
5837
  return await client3.uploadArtifact({
5846
5838
  title,
@@ -5852,7 +5844,26 @@ async function handleToolCall(name, args, deps) {
5852
5844
  }
5853
5845
  throw new Error("Provide either filePath or base64Data.");
5854
5846
  }
5855
- case "harmony_request_artifact_upload_url": {
5847
+ case "harmony_request_artifact_upload_url":
5848
+ case "harmony_request_card_attachment_upload_url":
5849
+ case "harmony_request_upload_url": {
5850
+ if (name !== "harmony_request_upload_url")
5851
+ warnDeprecatedTool(name);
5852
+ const target = name === "harmony_request_card_attachment_upload_url" ? "card_attachment" : name === "harmony_request_artifact_upload_url" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5853
+ if (target === "card_attachment") {
5854
+ const cardId2 = z.string().uuid().parse(args.cardId);
5855
+ const fileName = z.string().parse(args.fileName);
5856
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5857
+ const size2 = z.number().positive().parse(args.size);
5858
+ if (size2 > MAX_ATTACHMENT_SIZE) {
5859
+ throw new Error(`Declared size ${size2} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5860
+ }
5861
+ return await client3.requestCardAttachmentUploadUrl(cardId2, {
5862
+ fileName,
5863
+ fileType,
5864
+ size: size2
5865
+ });
5866
+ }
5856
5867
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5857
5868
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5858
5869
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5872,8 +5883,27 @@ async function handleToolCall(name, args, deps) {
5872
5883
  size
5873
5884
  });
5874
5885
  }
5875
- case "harmony_finalize_artifact": {
5886
+ case "harmony_finalize_artifact":
5887
+ case "harmony_finalize_card_attachment":
5888
+ case "harmony_finalize_upload": {
5889
+ if (name !== "harmony_finalize_upload")
5890
+ warnDeprecatedTool(name);
5891
+ const target = name === "harmony_finalize_card_attachment" ? "card_attachment" : name === "harmony_finalize_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5876
5892
  const storagePath = z.string().parse(args.storagePath);
5893
+ if (target === "card_attachment") {
5894
+ const cardId2 = z.string().uuid().parse(args.cardId);
5895
+ const fileName = z.string().parse(args.fileName);
5896
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5897
+ const sha2562 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5898
+ const size2 = args.size != null ? z.number().positive().parse(args.size) : undefined;
5899
+ return await client3.finalizeCardAttachment(cardId2, {
5900
+ storagePath,
5901
+ fileName,
5902
+ fileType,
5903
+ sha256: sha2562,
5904
+ size: size2
5905
+ });
5906
+ }
5877
5907
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5878
5908
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5879
5909
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5891,35 +5921,6 @@ async function handleToolCall(name, args, deps) {
5891
5921
  workspaceId
5892
5922
  });
5893
5923
  }
5894
- case "harmony_request_card_attachment_upload_url": {
5895
- const cardId = z.string().uuid().parse(args.cardId);
5896
- const fileName = z.string().parse(args.fileName);
5897
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5898
- const size = z.number().positive().parse(args.size);
5899
- if (size > MAX_ATTACHMENT_SIZE) {
5900
- throw new Error(`Declared size ${size} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5901
- }
5902
- return await client3.requestCardAttachmentUploadUrl(cardId, {
5903
- fileName,
5904
- fileType,
5905
- size
5906
- });
5907
- }
5908
- case "harmony_finalize_card_attachment": {
5909
- const cardId = z.string().uuid().parse(args.cardId);
5910
- const storagePath = z.string().parse(args.storagePath);
5911
- const fileName = z.string().parse(args.fileName);
5912
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5913
- const sha256 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5914
- const size = args.size != null ? z.number().positive().parse(args.size) : undefined;
5915
- return await client3.finalizeCardAttachment(cardId, {
5916
- storagePath,
5917
- fileName,
5918
- fileType,
5919
- sha256,
5920
- size
5921
- });
5922
- }
5923
5924
  case "harmony_share_artifact": {
5924
5925
  const artifactId = z.string().uuid().parse(args.artifactId);
5925
5926
  const expiresInDays = args.expiresInDays != null ? z.number().positive().parse(args.expiresInDays) : undefined;
@@ -5932,6 +5933,13 @@ async function handleToolCall(name, args, deps) {
5932
5933
  const result = await client3.getCardExternalLinks(cardId);
5933
5934
  return result;
5934
5935
  }
5936
+ case "harmony_add_external_link": {
5937
+ const cardId = z.string().uuid().parse(args.cardId);
5938
+ const url = z.string().min(1).max(2048).parse(args.url);
5939
+ const title = args.title ? z.string().max(200).parse(args.title) : undefined;
5940
+ const result = await client3.addExternalLink(cardId, url, title);
5941
+ return { success: true, ...result };
5942
+ }
5935
5943
  case "harmony_classify_card": {
5936
5944
  const cardId = z.string().uuid().parse(args.cardId);
5937
5945
  const result = await client3.classifyCard(cardId);
@@ -5985,10 +5993,12 @@ async function handleToolCall(name, args, deps) {
5985
5993
  ]).parse(args.commentType) : undefined;
5986
5994
  const supersedesId = args.supersedesId !== undefined ? z.string().uuid().parse(args.supersedesId) : undefined;
5987
5995
  const confirmsId = args.confirmsId !== undefined ? z.string().uuid().parse(args.confirmsId) : undefined;
5996
+ const replyToId = args.replyToId !== undefined ? z.string().uuid().parse(args.replyToId) : undefined;
5988
5997
  const result = await client3.addComment(cardId, body, {
5989
5998
  commentType,
5990
5999
  supersedesId,
5991
- confirmsId
6000
+ confirmsId,
6001
+ replyToId
5992
6002
  });
5993
6003
  return { success: true, ...result };
5994
6004
  }
@@ -6848,21 +6858,16 @@ async function handleToolCall(name, args, deps) {
6848
6858
  const result = await client3.getPlaybook(playbookId);
6849
6859
  return { success: true, playbook: result.playbook, runs: result.runs };
6850
6860
  }
6851
- case "harmony_run_playbook": {
6852
- const playbookId = z.string().uuid().parse(args.playbookId);
6853
- const result = await client3.runPlaybook(playbookId);
6854
- return { success: true, run: result.run };
6855
- }
6861
+ case "harmony_run_playbook":
6862
+ return deprecatedRemovedToolResult("harmony_run_playbook");
6856
6863
  case "harmony_create_playbook": {
6857
6864
  const workspaceId = args.workspaceId || getWorkspaceId();
6858
6865
  const name2 = z.string().min(1).max(200).parse(args.name);
6859
- const stepsVersion = args.stepsVersion !== undefined ? z.union([z.literal(1), z.literal(2)]).parse(args.stepsVersion) : undefined;
6860
6866
  const result = await client3.createPlaybook({
6861
6867
  workspaceId,
6862
6868
  name: name2,
6863
6869
  description: args.description,
6864
- steps: args.steps,
6865
- stepsVersion
6870
+ steps: args.steps
6866
6871
  });
6867
6872
  return { success: true, playbook: result.playbook };
6868
6873
  }
@@ -6877,14 +6882,8 @@ async function handleToolCall(name, args, deps) {
6877
6882
  });
6878
6883
  return { success: true, playbook: result.playbook };
6879
6884
  }
6880
- case "harmony_save_card_as_playbook": {
6881
- const cardId = z.string().uuid().parse(args.cardId);
6882
- const result = await client3.savePlaybookFromCard({
6883
- cardId,
6884
- name: args.name
6885
- });
6886
- return { success: true, playbook: result.playbook };
6887
- }
6885
+ case "harmony_save_card_as_playbook":
6886
+ return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
6888
6887
  case "harmony_signup": {
6889
6888
  const email = z.string().email().max(254).parse(args.email);
6890
6889
  const password = z.string().min(8).max(128).parse(args.password);