@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/cli.js CHANGED
@@ -1457,6 +1457,8 @@ function serializeCommentThread(comments, options = {}) {
1457
1457
  const tags = [];
1458
1458
  if (c.edited_at)
1459
1459
  tags.push("edited");
1460
+ if (c.reply_to_id)
1461
+ tags.push(`reply to ${ref(c.reply_to_id)}`);
1460
1462
  if (c.supersedes_id)
1461
1463
  tags.push(`supersedes ${ref(c.supersedes_id)}`);
1462
1464
  if (c.confirms_id)
@@ -1868,6 +1870,12 @@ class HarmonyApiClient {
1868
1870
  async getCardExternalLinks(cardId) {
1869
1871
  return this.request("GET", `/cards/${cardId}/external-links`);
1870
1872
  }
1873
+ async addExternalLink(cardId, url, title) {
1874
+ return this.request("POST", `/cards/${cardId}/external-links`, {
1875
+ url,
1876
+ title
1877
+ });
1878
+ }
1871
1879
  async uploadArtifact(data) {
1872
1880
  return this.request("POST", "/artifacts", data);
1873
1881
  }
@@ -1919,6 +1927,7 @@ class HarmonyApiClient {
1919
1927
  commentType: opts?.commentType,
1920
1928
  supersedesId: opts?.supersedesId,
1921
1929
  confirmsId: opts?.confirmsId,
1930
+ replyToId: opts?.replyToId,
1922
1931
  agentSessionId: opts?.agentSessionId
1923
1932
  });
1924
1933
  }
@@ -2371,12 +2380,6 @@ ${planContent.trim()}`;
2371
2380
  async updatePlaybook(playbookId, updates) {
2372
2381
  return this.request("PATCH", `/playbooks/${playbookId}`, updates);
2373
2382
  }
2374
- async runPlaybook(playbookId) {
2375
- return this.request("POST", `/playbooks/${playbookId}/run`);
2376
- }
2377
- async savePlaybookFromCard(data) {
2378
- return this.request("POST", "/playbooks/from-card", data);
2379
- }
2380
2383
  }
2381
2384
  var _promptModules = null;
2382
2385
  async function loadPromptModules() {
@@ -3240,7 +3243,7 @@ Start work on a Harmony card. Card reference: $ARGUMENTS
3240
3243
  ## 1. Find & Fetch Card
3241
3244
 
3242
3245
  Parse the reference and fetch the card:
3243
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
3246
+ - \`#42\` or \`42\` → \`harmony_get_card\` with \`shortId: 42\`
3244
3247
  - UUID → \`harmony_get_card\` with \`cardId\`
3245
3248
  - Name/text → \`harmony_search_cards\` with \`query\`
3246
3249
 
@@ -3294,7 +3297,7 @@ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
3294
3297
 
3295
3298
  ## Key Tools Reference
3296
3299
 
3297
- **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\`
3300
+ **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\`
3298
3301
 
3299
3302
  **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
3300
3303
 
@@ -3488,6 +3491,37 @@ function requireExactlyOneScope(scope) {
3488
3491
  throw new Error("Provide exactly one of cardId, planId, or workspaceId.");
3489
3492
  }
3490
3493
  }
3494
+ var DEPRECATED_TOOL_ALIASES = {
3495
+ harmony_get_card_by_short_id: "harmony_get_card",
3496
+ harmony_bulk_get_cards: "harmony_get_card",
3497
+ harmony_upload_artifact: "harmony_upload",
3498
+ harmony_upload_card_attachment: "harmony_upload",
3499
+ harmony_request_artifact_upload_url: "harmony_request_upload_url",
3500
+ harmony_request_card_attachment_upload_url: "harmony_request_upload_url",
3501
+ harmony_finalize_artifact: "harmony_finalize_upload",
3502
+ harmony_finalize_card_attachment: "harmony_finalize_upload"
3503
+ };
3504
+ var _warnedDeprecatedTools = new Set;
3505
+ function warnDeprecatedTool(oldName) {
3506
+ if (_warnedDeprecatedTools.has(oldName))
3507
+ return;
3508
+ _warnedDeprecatedTools.add(oldName);
3509
+ const replacement = DEPRECATED_TOOL_ALIASES[oldName];
3510
+ 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.`);
3511
+ }
3512
+ var DEPRECATED_REMOVED_TOOLS = {
3513
+ 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.",
3514
+ harmony_save_card_as_playbook: "The steps_version=1 automation macro was removed. Build a stage playbook with harmony_create_playbook instead."
3515
+ };
3516
+ var _warnedRemovedTools = new Set;
3517
+ function deprecatedRemovedToolResult(name) {
3518
+ const message = DEPRECATED_REMOVED_TOOLS[name];
3519
+ if (!_warnedRemovedTools.has(name)) {
3520
+ _warnedRemovedTools.add(name);
3521
+ console.error(`[harmony-mcp] Tool "${name}" is deprecated and no longer functional — ${message}`);
3522
+ }
3523
+ return { success: false, deprecated: true, message };
3524
+ }
3491
3525
  var SIGNED_UPLOAD_TIMEOUT_MS = 30000;
3492
3526
  async function putToSignedUrl(uploadUrl, bytes, contentType) {
3493
3527
  let res;
@@ -3815,44 +3849,29 @@ var TOOLS = {
3815
3849
  }
3816
3850
  },
3817
3851
  harmony_get_card: {
3818
- description: "Get detailed information about a specific card by UUID",
3819
- inputSchema: {
3820
- type: "object",
3821
- properties: {
3822
- cardId: { type: "string", description: "Card UUID" }
3823
- },
3824
- required: ["cardId"]
3825
- }
3826
- },
3827
- harmony_get_card_by_short_id: {
3828
- description: "Get a card by its short ID (e.g., #42) within a project",
3852
+ 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.",
3829
3853
  inputSchema: {
3830
3854
  type: "object",
3831
3855
  properties: {
3832
- projectId: {
3856
+ cardId: {
3833
3857
  type: "string",
3834
- description: "Project ID (optional if context set)"
3858
+ description: "Card UUID full detail for one card."
3835
3859
  },
3836
3860
  shortId: {
3837
3861
  type: "number",
3838
- description: "Short ID number (e.g., 42 for card #42)"
3839
- }
3840
- },
3841
- required: ["shortId"]
3842
- }
3843
- },
3844
- harmony_bulk_get_cards: {
3845
- 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.",
3846
- inputSchema: {
3847
- type: "object",
3848
- properties: {
3862
+ description: "Short ID (e.g. 42 for card #42) — full detail, project-scoped."
3863
+ },
3849
3864
  shortIds: {
3850
3865
  type: "array",
3851
3866
  items: { type: "number" },
3852
- description: "Card short ids, e.g. [400, 401, 402]. Max 100 per call."
3867
+ description: "Short IDs, e.g. [400, 401, 402] compact summaries in one call. Max 100."
3868
+ },
3869
+ projectId: {
3870
+ type: "string",
3871
+ description: "Project ID for shortId/shortIds (optional if context set)."
3853
3872
  }
3854
3873
  },
3855
- required: ["shortIds"]
3874
+ required: []
3856
3875
  }
3857
3876
  },
3858
3877
  harmony_bulk_archive_cards: {
@@ -4007,174 +4026,144 @@ var TOOLS = {
4007
4026
  required: ["cardId"]
4008
4027
  }
4009
4028
  },
4010
- harmony_upload_card_attachment: {
4011
- 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.",
4029
+ harmony_upload: {
4030
+ 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.",
4012
4031
  inputSchema: {
4013
4032
  type: "object",
4014
4033
  properties: {
4015
- cardId: { type: "string", description: "Card UUID" },
4034
+ target: {
4035
+ type: "string",
4036
+ enum: ["card_attachment", "artifact"],
4037
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4038
+ },
4039
+ cardId: {
4040
+ type: "string",
4041
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4042
+ },
4043
+ planId: {
4044
+ type: "string",
4045
+ description: "Plan UUID (artifact only — link the artifact to a plan)."
4046
+ },
4047
+ workspaceId: {
4048
+ type: "string",
4049
+ description: "Workspace UUID (artifact only — standalone artifact)."
4050
+ },
4016
4051
  filePath: {
4017
4052
  type: "string",
4018
- description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
4053
+ description: "Absolute path to a local file the server can read. Mutually exclusive with base64Data."
4019
4054
  },
4020
4055
  base64Data: {
4021
4056
  type: "string",
4022
- description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
4057
+ description: "Base64-encoded bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4023
4058
  },
4024
4059
  fileName: {
4025
4060
  type: "string",
4026
- description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
4061
+ description: "File name including extension (card_attachment: required with base64Data, else defaults to the filePath basename)."
4062
+ },
4063
+ title: {
4064
+ type: "string",
4065
+ description: "Artifact display title (defaults to the file basename)."
4027
4066
  },
4028
4067
  contentType: {
4029
4068
  type: "string",
4030
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4069
+ description: "Optional MIME type (card_attachment; inferred from the extension when omitted)."
4031
4070
  }
4032
4071
  },
4033
- required: ["cardId"]
4072
+ required: ["target"]
4034
4073
  }
4035
4074
  },
4036
- harmony_request_card_attachment_upload_url: {
4037
- 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.",
4075
+ harmony_request_upload_url: {
4076
+ 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.",
4038
4077
  inputSchema: {
4039
4078
  type: "object",
4040
4079
  properties: {
4041
- cardId: { type: "string", description: "Card UUID" },
4042
- fileName: {
4080
+ target: {
4043
4081
  type: "string",
4044
- description: "File name including extension (e.g. 'screenshot.png')."
4082
+ enum: ["card_attachment", "artifact"],
4083
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4045
4084
  },
4046
- fileType: {
4085
+ cardId: {
4047
4086
  type: "string",
4048
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4087
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4049
4088
  },
4050
- size: {
4051
- type: "number",
4052
- description: "File size in bytes (rejected early if over 5MB)."
4053
- }
4054
- },
4055
- required: ["cardId", "fileName", "size"]
4056
- }
4057
- },
4058
- harmony_finalize_card_attachment: {
4059
- 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.",
4060
- inputSchema: {
4061
- type: "object",
4062
- properties: {
4063
- cardId: { type: "string", description: "Card UUID" },
4064
- storagePath: {
4089
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4090
+ workspaceId: {
4065
4091
  type: "string",
4066
- description: "The storagePath returned by harmony_request_card_attachment_upload_url."
4092
+ description: "Workspace UUID (artifact only)."
4067
4093
  },
4068
4094
  fileName: {
4069
4095
  type: "string",
4070
- description: "File name including extension (e.g. 'screenshot.png')."
4096
+ description: "File name including extension (required for card_attachment)."
4071
4097
  },
4098
+ title: { type: "string", description: "Artifact display title." },
4072
4099
  fileType: {
4073
4100
  type: "string",
4074
- description: "Optional MIME type; inferred from the extension when omitted."
4101
+ description: "card_attachment MIME type (inferred from the extension when omitted)."
4075
4102
  },
4076
- sha256: {
4103
+ contentType: {
4077
4104
  type: "string",
4078
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4105
+ description: "artifact MIME type; only 'text/html' is accepted (the default)."
4079
4106
  },
4080
4107
  size: {
4081
4108
  type: "number",
4082
- description: "Optional byte size (advisory; re-validated server-side)."
4109
+ description: "File size in bytes (rejected early if over the target's limit)."
4083
4110
  }
4084
4111
  },
4085
- required: ["cardId", "storagePath", "fileName"]
4086
- }
4087
- },
4088
- harmony_classify_card: {
4089
- 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`.",
4090
- inputSchema: {
4091
- type: "object",
4092
- properties: {
4093
- cardId: { type: "string", description: "Card UUID" }
4094
- },
4095
- required: ["cardId"]
4112
+ required: ["target"]
4096
4113
  }
4097
4114
  },
4098
- harmony_upload_artifact: {
4099
- 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.",
4115
+ harmony_finalize_upload: {
4116
+ 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.",
4100
4117
  inputSchema: {
4101
4118
  type: "object",
4102
4119
  properties: {
4103
- title: {
4120
+ target: {
4104
4121
  type: "string",
4105
- description: "Display title (defaults to the file basename)."
4122
+ enum: ["card_attachment", "artifact"],
4123
+ description: "What to finalize: a card file attachment or a hosted HTML artifact."
4106
4124
  },
4107
- cardId: { type: "string", description: "Link to this card (UUID)." },
4108
- planId: { type: "string", description: "Link to this plan (UUID)." },
4109
- workspaceId: {
4125
+ storagePath: {
4110
4126
  type: "string",
4111
- description: "Attach to this workspace as a standalone artifact (UUID)."
4127
+ description: "The storagePath returned by harmony_request_upload_url."
4112
4128
  },
4113
- filePath: {
4129
+ cardId: {
4114
4130
  type: "string",
4115
- description: "Absolute path to a local .html file the MCP server process can read. Mutually exclusive with base64Data."
4131
+ description: "Card UUID. Required for card_attachment; scope for artifact."
4116
4132
  },
4117
- base64Data: {
4133
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4134
+ workspaceId: {
4118
4135
  type: "string",
4119
- description: "Base64-encoded HTML bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4120
- }
4121
- },
4122
- required: []
4123
- }
4124
- },
4125
- harmony_request_artifact_upload_url: {
4126
- 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.",
4127
- inputSchema: {
4128
- type: "object",
4129
- properties: {
4130
- title: {
4136
+ description: "Workspace UUID (artifact only)."
4137
+ },
4138
+ fileName: {
4131
4139
  type: "string",
4132
- description: "Display title (defaults to the file basename at finalize)."
4140
+ description: "File name including extension (required for card_attachment)."
4133
4141
  },
4134
- cardId: { type: "string", description: "Link to this card (UUID)." },
4135
- planId: { type: "string", description: "Link to this plan (UUID)." },
4136
- workspaceId: {
4142
+ title: { type: "string", description: "Artifact display title." },
4143
+ fileType: {
4137
4144
  type: "string",
4138
- description: "Attach to this workspace as a standalone artifact (UUID)."
4145
+ description: "card_attachment MIME type; inferred from the extension when omitted."
4139
4146
  },
4140
- contentType: {
4147
+ sha256: {
4141
4148
  type: "string",
4142
- description: "MIME type; only 'text/html' is accepted (the default)."
4149
+ description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4143
4150
  },
4144
4151
  size: {
4145
4152
  type: "number",
4146
- description: "File size in bytes (rejected early if over 2MB)."
4153
+ description: "Optional byte size (advisory; re-validated server-side)."
4147
4154
  }
4148
4155
  },
4149
- required: []
4156
+ required: ["target", "storagePath"]
4150
4157
  }
4151
4158
  },
4152
- harmony_finalize_artifact: {
4153
- 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.",
4159
+ harmony_classify_card: {
4160
+ 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`.",
4154
4161
  inputSchema: {
4155
4162
  type: "object",
4156
4163
  properties: {
4157
- storagePath: {
4158
- type: "string",
4159
- description: "The storagePath returned by harmony_request_artifact_upload_url."
4160
- },
4161
- title: { type: "string", description: "Display title." },
4162
- cardId: { type: "string", description: "Link to this card (UUID)." },
4163
- planId: { type: "string", description: "Link to this plan (UUID)." },
4164
- workspaceId: {
4165
- type: "string",
4166
- description: "Attach to this workspace as a standalone artifact (UUID)."
4167
- },
4168
- sha256: {
4169
- type: "string",
4170
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4171
- },
4172
- size: {
4173
- type: "number",
4174
- description: "Optional byte size (advisory; re-validated server-side)."
4175
- }
4164
+ cardId: { type: "string", description: "Card UUID" }
4176
4165
  },
4177
- required: ["storagePath"]
4166
+ required: ["cardId"]
4178
4167
  }
4179
4168
  },
4180
4169
  harmony_share_artifact: {
@@ -4204,6 +4193,27 @@ var TOOLS = {
4204
4193
  required: ["cardId"]
4205
4194
  }
4206
4195
  },
4196
+ harmony_add_external_link: {
4197
+ 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.",
4198
+ inputSchema: {
4199
+ type: "object",
4200
+ properties: {
4201
+ cardId: {
4202
+ type: "string",
4203
+ description: "Card UUID"
4204
+ },
4205
+ url: {
4206
+ type: "string",
4207
+ description: "The URL to attach"
4208
+ },
4209
+ title: {
4210
+ type: "string",
4211
+ description: "Optional human-readable title for the link"
4212
+ }
4213
+ },
4214
+ required: ["cardId", "url"]
4215
+ }
4216
+ },
4207
4217
  harmony_create_subtask: {
4208
4218
  description: "Create a subtask on a card",
4209
4219
  inputSchema: {
@@ -4255,7 +4265,7 @@ var TOOLS = {
4255
4265
  }
4256
4266
  },
4257
4267
  harmony_add_comment: {
4258
- 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.",
4268
+ 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.",
4259
4269
  inputSchema: {
4260
4270
  type: "object",
4261
4271
  properties: {
@@ -4281,6 +4291,10 @@ var TOOLS = {
4281
4291
  confirmsId: {
4282
4292
  type: "string",
4283
4293
  description: "Comment id this comment reaffirms"
4294
+ },
4295
+ replyToId: {
4296
+ type: "string",
4297
+ 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."
4284
4298
  }
4285
4299
  },
4286
4300
  required: ["cardId", "body"]
@@ -4752,7 +4766,7 @@ var TOOLS = {
4752
4766
  limit: { type: "number", description: "Max results (default: 20)" },
4753
4767
  top_k: {
4754
4768
  type: "number",
4755
- 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."
4769
+ 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."
4756
4770
  },
4757
4771
  budget_tokens: {
4758
4772
  type: "number",
@@ -4867,7 +4881,7 @@ var TOOLS = {
4867
4881
  }
4868
4882
  },
4869
4883
  harmony_suggest_relations: {
4870
- 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.",
4884
+ 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.",
4871
4885
  inputSchema: {
4872
4886
  type: "object",
4873
4887
  properties: {
@@ -5151,7 +5165,7 @@ var TOOLS = {
5151
5165
  }
5152
5166
  },
5153
5167
  harmony_list_playbook: {
5154
- description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, steps_version, and state. Read-only.",
5168
+ description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, and state. Read-only.",
5155
5169
  inputSchema: {
5156
5170
  type: "object",
5157
5171
  properties: {
@@ -5173,21 +5187,8 @@ var TOOLS = {
5173
5187
  required: ["playbookId"]
5174
5188
  }
5175
5189
  },
5176
- harmony_run_playbook: {
5177
- 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.",
5178
- inputSchema: {
5179
- type: "object",
5180
- properties: {
5181
- playbookId: {
5182
- type: "string",
5183
- description: "Playbook ID to run (UUID)"
5184
- }
5185
- },
5186
- required: ["playbookId"]
5187
- }
5188
- },
5189
5190
  harmony_create_playbook: {
5190
- 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).",
5191
+ 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.",
5191
5192
  inputSchema: {
5192
5193
  type: "object",
5193
5194
  properties: {
@@ -5197,14 +5198,9 @@ var TOOLS = {
5197
5198
  },
5198
5199
  name: { type: "string", description: "Playbook name" },
5199
5200
  description: { type: "string", description: "Playbook description" },
5200
- stepsVersion: {
5201
- type: "number",
5202
- enum: [1, 2],
5203
- description: "1 = automation macro (tool steps), 2 = Method stage model (stage objects). Default 1."
5204
- },
5205
5201
  steps: {
5206
5202
  type: "array",
5207
- description: "Steps (steps_version 1: tool-step objects) or stages (steps_version 2: stage objects).",
5203
+ description: "The playbook's ordered stage objects.",
5208
5204
  items: { type: "object" }
5209
5205
  }
5210
5206
  },
@@ -5224,7 +5220,7 @@ var TOOLS = {
5224
5220
  description: { type: "string", description: "New description" },
5225
5221
  steps: {
5226
5222
  type: "array",
5227
- description: "New steps (v1) or stages (v2) array.",
5223
+ description: "New ordered stage objects.",
5228
5224
  items: { type: "object" }
5229
5225
  },
5230
5226
  enabled: {
@@ -5240,20 +5236,6 @@ var TOOLS = {
5240
5236
  required: ["playbookId"]
5241
5237
  }
5242
5238
  },
5243
- harmony_save_card_as_playbook: {
5244
- 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.",
5245
- inputSchema: {
5246
- type: "object",
5247
- properties: {
5248
- cardId: { type: "string", description: "Card ID to template (UUID)" },
5249
- name: {
5250
- type: "string",
5251
- description: "Name for the new playbook (defaults to the card title)"
5252
- }
5253
- },
5254
- required: ["cardId"]
5255
- }
5256
- },
5257
5239
  harmony_signup: {
5258
5240
  description: "Create a new user account. Returns a JWT session for subsequent authenticated calls. No API key required.",
5259
5241
  inputSchema: {
@@ -5653,23 +5635,33 @@ async function handleToolCall(name, args, deps) {
5653
5635
  });
5654
5636
  return { success: true, ...result, count: result.cards.length };
5655
5637
  }
5638
+ case "harmony_get_card_by_short_id":
5639
+ case "harmony_bulk_get_cards":
5656
5640
  case "harmony_get_card": {
5641
+ if (name !== "harmony_get_card")
5642
+ warnDeprecatedTool(name);
5643
+ const hasShortIds = args.shortIds != null;
5644
+ const hasShortId = args.shortId != null;
5645
+ const hasCardId = args.cardId != null;
5646
+ if ([hasShortIds, hasShortId, hasCardId].filter(Boolean).length !== 1) {
5647
+ throw new Error("Provide exactly one of: cardId (UUID), shortId (number), or shortIds (number[]).");
5648
+ }
5649
+ if (hasShortIds) {
5650
+ const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5651
+ const projectId = getProjectId();
5652
+ const result2 = await client3.bulkGetCards(projectId, shortIds);
5653
+ return { success: true, ...result2 };
5654
+ }
5655
+ if (hasShortId) {
5656
+ const shortId = z.number().int().positive().parse(args.shortId);
5657
+ const projectId = args.projectId || getProjectId();
5658
+ const result2 = await client3.getCardByShortId(projectId, shortId);
5659
+ return { success: true, ...result2 };
5660
+ }
5657
5661
  const cardId = z.string().uuid().parse(args.cardId);
5658
5662
  const result = await client3.getCard(cardId);
5659
5663
  return { success: true, ...result };
5660
5664
  }
5661
- case "harmony_get_card_by_short_id": {
5662
- const shortId = z.number().int().positive().parse(args.shortId);
5663
- const projectId = args.projectId || getProjectId();
5664
- const result = await client3.getCardByShortId(projectId, shortId);
5665
- return { success: true, ...result };
5666
- }
5667
- case "harmony_bulk_get_cards": {
5668
- const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5669
- const projectId = getProjectId();
5670
- const result = await client3.bulkGetCards(projectId, shortIds);
5671
- return { success: true, ...result };
5672
- }
5673
5665
  case "harmony_bulk_archive_cards": {
5674
5666
  const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5675
5667
  const projectId = getProjectId();
@@ -5769,58 +5761,58 @@ async function handleToolCall(name, args, deps) {
5769
5761
  const result = await client3.getCardAttachments(cardId);
5770
5762
  return result;
5771
5763
  }
5772
- case "harmony_upload_card_attachment": {
5773
- const cardId = z.string().uuid().parse(args.cardId);
5764
+ case "harmony_upload_card_attachment":
5765
+ case "harmony_upload_artifact":
5766
+ case "harmony_upload": {
5767
+ if (name !== "harmony_upload")
5768
+ warnDeprecatedTool(name);
5769
+ const target = name === "harmony_upload_card_attachment" ? "card_attachment" : name === "harmony_upload_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5774
5770
  const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5775
5771
  const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5776
- const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5777
- const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5778
5772
  if (filePath && base64Data) {
5779
5773
  throw new Error("Provide either filePath or base64Data, not both.");
5780
5774
  }
5781
- if (filePath) {
5782
- const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5783
- const resolvedName = fileName || basename(filePath);
5784
- const signed = await client3.requestCardAttachmentUploadUrl(cardId, {
5785
- fileName: resolvedName,
5786
- fileType: contentType,
5787
- size: bytes.byteLength
5788
- });
5789
- await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5790
- return await client3.finalizeCardAttachment(cardId, {
5791
- storagePath: signed.storagePath,
5792
- fileName: resolvedName,
5793
- fileType: contentType || signed.fileType,
5794
- sha256: sha256Hex(bytes),
5795
- size: bytes.byteLength
5796
- });
5797
- }
5798
- if (base64Data) {
5799
- if (!fileName) {
5800
- throw new Error("fileName is required when using base64Data.");
5775
+ if (target === "card_attachment") {
5776
+ const cardId2 = z.string().uuid().parse(args.cardId);
5777
+ const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5778
+ const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5779
+ if (filePath) {
5780
+ const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5781
+ const resolvedName = fileName || basename(filePath);
5782
+ const signed = await client3.requestCardAttachmentUploadUrl(cardId2, {
5783
+ fileName: resolvedName,
5784
+ fileType: contentType,
5785
+ size: bytes.byteLength
5786
+ });
5787
+ await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5788
+ return await client3.finalizeCardAttachment(cardId2, {
5789
+ storagePath: signed.storagePath,
5790
+ fileName: resolvedName,
5791
+ fileType: contentType || signed.fileType,
5792
+ sha256: sha256Hex(bytes),
5793
+ size: bytes.byteLength
5794
+ });
5801
5795
  }
5802
- if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5803
- 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.`);
5796
+ if (base64Data) {
5797
+ if (!fileName) {
5798
+ throw new Error("fileName is required when using base64Data.");
5799
+ }
5800
+ if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5801
+ throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5802
+ }
5803
+ return await client3.uploadCardAttachment(cardId2, {
5804
+ fileName,
5805
+ data: base64Data,
5806
+ fileType: contentType
5807
+ });
5804
5808
  }
5805
- return await client3.uploadCardAttachment(cardId, {
5806
- fileName,
5807
- data: base64Data,
5808
- fileType: contentType
5809
- });
5809
+ throw new Error("Provide either filePath or base64Data.");
5810
5810
  }
5811
- throw new Error("Provide either filePath or base64Data.");
5812
- }
5813
- case "harmony_upload_artifact": {
5814
5811
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5815
5812
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5816
5813
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
5817
5814
  const workspaceId = args.workspaceId != null ? z.string().uuid().parse(args.workspaceId) : undefined;
5818
5815
  requireExactlyOneScope({ cardId, planId, workspaceId });
5819
- const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5820
- const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5821
- if (filePath && base64Data) {
5822
- throw new Error("Provide either filePath or base64Data, not both.");
5823
- }
5824
5816
  if (filePath) {
5825
5817
  const bytes = await readFileForUpload(filePath, MAX_ARTIFACT_SIZE, "artifact");
5826
5818
  const resolvedTitle = title || basename(filePath);
@@ -5845,7 +5837,7 @@ async function handleToolCall(name, args, deps) {
5845
5837
  }
5846
5838
  if (base64Data) {
5847
5839
  if (base64ByteLength(base64Data) > MAX_ARTIFACT_SIZE) {
5848
- throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_artifact_upload_url + harmony_finalize_artifact handshake for large files.`);
5840
+ throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5849
5841
  }
5850
5842
  return await client3.uploadArtifact({
5851
5843
  title,
@@ -5857,7 +5849,26 @@ async function handleToolCall(name, args, deps) {
5857
5849
  }
5858
5850
  throw new Error("Provide either filePath or base64Data.");
5859
5851
  }
5860
- case "harmony_request_artifact_upload_url": {
5852
+ case "harmony_request_artifact_upload_url":
5853
+ case "harmony_request_card_attachment_upload_url":
5854
+ case "harmony_request_upload_url": {
5855
+ if (name !== "harmony_request_upload_url")
5856
+ warnDeprecatedTool(name);
5857
+ 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);
5858
+ if (target === "card_attachment") {
5859
+ const cardId2 = z.string().uuid().parse(args.cardId);
5860
+ const fileName = z.string().parse(args.fileName);
5861
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5862
+ const size2 = z.number().positive().parse(args.size);
5863
+ if (size2 > MAX_ATTACHMENT_SIZE) {
5864
+ throw new Error(`Declared size ${size2} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5865
+ }
5866
+ return await client3.requestCardAttachmentUploadUrl(cardId2, {
5867
+ fileName,
5868
+ fileType,
5869
+ size: size2
5870
+ });
5871
+ }
5861
5872
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5862
5873
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5863
5874
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5877,8 +5888,27 @@ async function handleToolCall(name, args, deps) {
5877
5888
  size
5878
5889
  });
5879
5890
  }
5880
- case "harmony_finalize_artifact": {
5891
+ case "harmony_finalize_artifact":
5892
+ case "harmony_finalize_card_attachment":
5893
+ case "harmony_finalize_upload": {
5894
+ if (name !== "harmony_finalize_upload")
5895
+ warnDeprecatedTool(name);
5896
+ const target = name === "harmony_finalize_card_attachment" ? "card_attachment" : name === "harmony_finalize_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5881
5897
  const storagePath = z.string().parse(args.storagePath);
5898
+ if (target === "card_attachment") {
5899
+ const cardId2 = z.string().uuid().parse(args.cardId);
5900
+ const fileName = z.string().parse(args.fileName);
5901
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5902
+ const sha2562 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5903
+ const size2 = args.size != null ? z.number().positive().parse(args.size) : undefined;
5904
+ return await client3.finalizeCardAttachment(cardId2, {
5905
+ storagePath,
5906
+ fileName,
5907
+ fileType,
5908
+ sha256: sha2562,
5909
+ size: size2
5910
+ });
5911
+ }
5882
5912
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5883
5913
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5884
5914
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5896,35 +5926,6 @@ async function handleToolCall(name, args, deps) {
5896
5926
  workspaceId
5897
5927
  });
5898
5928
  }
5899
- case "harmony_request_card_attachment_upload_url": {
5900
- const cardId = z.string().uuid().parse(args.cardId);
5901
- const fileName = z.string().parse(args.fileName);
5902
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5903
- const size = z.number().positive().parse(args.size);
5904
- if (size > MAX_ATTACHMENT_SIZE) {
5905
- throw new Error(`Declared size ${size} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5906
- }
5907
- return await client3.requestCardAttachmentUploadUrl(cardId, {
5908
- fileName,
5909
- fileType,
5910
- size
5911
- });
5912
- }
5913
- case "harmony_finalize_card_attachment": {
5914
- const cardId = z.string().uuid().parse(args.cardId);
5915
- const storagePath = z.string().parse(args.storagePath);
5916
- const fileName = z.string().parse(args.fileName);
5917
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5918
- const sha256 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5919
- const size = args.size != null ? z.number().positive().parse(args.size) : undefined;
5920
- return await client3.finalizeCardAttachment(cardId, {
5921
- storagePath,
5922
- fileName,
5923
- fileType,
5924
- sha256,
5925
- size
5926
- });
5927
- }
5928
5929
  case "harmony_share_artifact": {
5929
5930
  const artifactId = z.string().uuid().parse(args.artifactId);
5930
5931
  const expiresInDays = args.expiresInDays != null ? z.number().positive().parse(args.expiresInDays) : undefined;
@@ -5937,6 +5938,13 @@ async function handleToolCall(name, args, deps) {
5937
5938
  const result = await client3.getCardExternalLinks(cardId);
5938
5939
  return result;
5939
5940
  }
5941
+ case "harmony_add_external_link": {
5942
+ const cardId = z.string().uuid().parse(args.cardId);
5943
+ const url = z.string().min(1).max(2048).parse(args.url);
5944
+ const title = args.title ? z.string().max(200).parse(args.title) : undefined;
5945
+ const result = await client3.addExternalLink(cardId, url, title);
5946
+ return { success: true, ...result };
5947
+ }
5940
5948
  case "harmony_classify_card": {
5941
5949
  const cardId = z.string().uuid().parse(args.cardId);
5942
5950
  const result = await client3.classifyCard(cardId);
@@ -5990,10 +5998,12 @@ async function handleToolCall(name, args, deps) {
5990
5998
  ]).parse(args.commentType) : undefined;
5991
5999
  const supersedesId = args.supersedesId !== undefined ? z.string().uuid().parse(args.supersedesId) : undefined;
5992
6000
  const confirmsId = args.confirmsId !== undefined ? z.string().uuid().parse(args.confirmsId) : undefined;
6001
+ const replyToId = args.replyToId !== undefined ? z.string().uuid().parse(args.replyToId) : undefined;
5993
6002
  const result = await client3.addComment(cardId, body, {
5994
6003
  commentType,
5995
6004
  supersedesId,
5996
- confirmsId
6005
+ confirmsId,
6006
+ replyToId
5997
6007
  });
5998
6008
  return { success: true, ...result };
5999
6009
  }
@@ -6853,21 +6863,16 @@ async function handleToolCall(name, args, deps) {
6853
6863
  const result = await client3.getPlaybook(playbookId);
6854
6864
  return { success: true, playbook: result.playbook, runs: result.runs };
6855
6865
  }
6856
- case "harmony_run_playbook": {
6857
- const playbookId = z.string().uuid().parse(args.playbookId);
6858
- const result = await client3.runPlaybook(playbookId);
6859
- return { success: true, run: result.run };
6860
- }
6866
+ case "harmony_run_playbook":
6867
+ return deprecatedRemovedToolResult("harmony_run_playbook");
6861
6868
  case "harmony_create_playbook": {
6862
6869
  const workspaceId = args.workspaceId || getWorkspaceId();
6863
6870
  const name2 = z.string().min(1).max(200).parse(args.name);
6864
- const stepsVersion = args.stepsVersion !== undefined ? z.union([z.literal(1), z.literal(2)]).parse(args.stepsVersion) : undefined;
6865
6871
  const result = await client3.createPlaybook({
6866
6872
  workspaceId,
6867
6873
  name: name2,
6868
6874
  description: args.description,
6869
- steps: args.steps,
6870
- stepsVersion
6875
+ steps: args.steps
6871
6876
  });
6872
6877
  return { success: true, playbook: result.playbook };
6873
6878
  }
@@ -6882,14 +6887,8 @@ async function handleToolCall(name, args, deps) {
6882
6887
  });
6883
6888
  return { success: true, playbook: result.playbook };
6884
6889
  }
6885
- case "harmony_save_card_as_playbook": {
6886
- const cardId = z.string().uuid().parse(args.cardId);
6887
- const result = await client3.savePlaybookFromCard({
6888
- cardId,
6889
- name: args.name
6890
- });
6891
- return { success: true, playbook: result.playbook };
6892
- }
6890
+ case "harmony_save_card_as_playbook":
6891
+ return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
6893
6892
  case "harmony_signup": {
6894
6893
  const email = z.string().email().max(254).parse(args.email);
6895
6894
  const password = z.string().min(8).max(128).parse(args.password);