Skip to content

feat(server): add Taskade API v2 tool layer — agent chat + webhooks - #55

Merged
johnxie merged 3 commits into
mainfrom
feat/v2-tool-layer
Jun 27, 2026
Merged

feat(server): add Taskade API v2 tool layer — agent chat + webhooks#55
johnxie merged 3 commits into
mainfrom
feat/v2-tool-layer

Conversation

@johnxie

@johnxie johnxie commented Jun 27, 2026

Copy link
Copy Markdown
Member

Ports Taskade API v2 into the MCP server as an additive layer next to v1. This is the capability gap that matters: v1 can't do agent chat or webhooks; v2 can.

What you can now do from Claude/Cursor

  • Chat with an AI agentpromptAgent, listConversations, getConversation
  • Real-time eventssubscribeWebhook, unsubscribeWebhook

How (least-effort, zero-regression)

  • v2 is registered via a second setupToolsV2(this, { url: '…/api/v2', … }) after the existing v1 setupToolsv1's 57 tools are byte-identical (verified zero drift).
  • codegen gains an exportName option (default setupTools) so the two generated tool sets coexist without an export collision.
  • gen-taskade-mcp-tools-v2.ts reads the committed taskade-public.v2.json, prunes to the enabled ops + their reachable schemas before dereferencing — sidestepping a broken self-$ref in the v2 Field schema that otherwise crashes dereference(). (Upstream spec bug; remove the prune-workaround once fixed.)
  • Relies on the operationId→path fallback from feat(codegen): derive tool name from path when operationId is absent #53 (v2 ops have no operationId).

Verification (local)

build + lint + vitest: green
smoke: 62 tools register = 57 v1 + 5 v2
v2 present: promptAgent, listConversations, getConversation, subscribeWebhook, unsubscribeWebhook
v1 intact:  taskCreate, taskDelete, workspacesGet  ·  v1 tools.generated.ts drift: NONE

Scope / follow-ups

  • Out of scope (need runtime work): binary GET endpoints (export/zip, media content) and raw-body uploads (uploadMedia, importBundleZip).
  • Enabled set is intentionally small (highest-value first); grows as v2 leaves beta.

Stacked on

@johnxie
johnxie requested a review from Copilot June 27, 2026 08:35
@changeset-bot

changeset-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bbd3230

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@taskade/mcp-server Minor
@taskade/mcp-openapi-codegen Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@johnxie
johnxie changed the base branch from feat/codegen-operationid-fallback to main June 27, 2026 08:53
Additive v2 layer registered alongside v1 — v1's 57 tools are byte-identical
(verified zero drift). Adds the capabilities v1 cannot do: agent chat (promptAgent,
listConversations, getConversation) and webhooks (subscribe/unsubscribe), generated
from the live v2 spec into tools.v2.generated.ts and registered via setupToolsV2 on
the /api/v2 base URL with the same bearer token.

- codegen: new exportName option (default setupTools) so two tool sets coexist.
- gen-taskade-mcp-tools-v2.ts prunes the v2 spec to the enabled ops + their reachable
  schemas before dereferencing, sidestepping a broken $ref in the v2 Field schema.
Smoke-tested: 62 tools register (57 v1 + 5 v2); build + lint + tests green.
@johnxie
johnxie force-pushed the feat/v2-tool-layer branch from 7a949a8 to 9f5e20f Compare June 27, 2026 08:56
johnxie added 2 commits June 27, 2026 02:08
…ets its params

Code review of the v2 layer found the flagship `promptAgent` tool registering with
an empty input schema. A request body marked `nullable: true` is rewritten by the
schema converter to `type: ['object','null']`, which the strict `=== 'object'` guard
in the parser skipped — dropping spaceId/agentId/prompt. Accept object types whether
scalar or in a nullable union. v1 `tools.generated.ts` is byte-identical (no v1 body
is a nullable object), so this is purely additive for v2.

Hardening from the same review:
- gen-taskade-mcp-tools-v2: throw if an allow-listed action has no matching spec path
  (was a silent under-generation), and wrap dereference() with a pointed error for the
  known upstream Field self-$ref.
- constants.v2: key HUMANIZED_TASKADE_V2_ACTIONS to the allow-list union so the two
  cannot drift (compile-time enforced).
- parser tests: nullable body, kebab/snake segments, empty-description fallback.
Code-review follow-up: the JSDoc said 'root path' but the fallback (and its test)
also covers param-only paths like /{id}. No behavior change.
@johnxie

johnxie commented Jun 27, 2026

Copy link
Copy Markdown
Member Author

Review follow-up — multi-agent review (1 critical found + fixed)

Ran /pr-review-toolkit:review-pr (code-reviewer, silent-failure-hunter, type-design-analyzer) and the /code-review 5-agent scored pass. cc @deanzaka @lxcid.

One critical issue was found and fixed in-branch; the PR is now clean.

Critical — fixed in 6141019

promptAgent (the flagship "chat with an AI agent" tool) was registering with an empty input schema (z.object({}).shape) — the model couldn't pass prompt / agentId / spaceId, so every call would fail at the API. Root cause: the v2 spec marks the promptAgent request body "nullable": true, which convertOpenApiSchemaToJsonSchema rewrites to type: ['object','null']; the strict bodySchema.type === 'object' guard in the shared parser then skipped the property-copy loop. Fix: accept an object type whether scalar or in a nullable union (found independently by two reviewers).

parser.ts fixpromptAgent now exposes { spaceId, agentId, prompt }.

Because the fix is in the shared codegen path, I re-verified v1: tools.generated.ts is byte-identical (no v1 request body is a nullable object), and CI's full-tree verify gate is green.

Hardening applied from the same review

  • Loud-fail on allow-list drift — the v2 generator now throws if an ENABLED_TASKADE_V2_ACTIONS entry has no matching spec path (previously a silent under-generation). gen-taskade-mcp-tools-v2.ts
  • Type-enforced syncHUMANIZED_TASKADE_V2_ACTIONS is keyed to the allow-list union, so a missing/typo'd title is now a compile error. constants.v2.ts
  • Clearer dereference error for the known upstream Field self-$ref, plus a doc-comment fix and 3 new parser tests (nullable body, kebab/snake, empty description).

Deferred — out of scope (pre-existing, affects v1 too)

The shared runtime returns non-2xx API responses as successful tool results (no response.ok check, no isError). This predates v2 and applies to all 57 v1 tools; v2's promptAgent normalizer just makes it more visible. Recommend a separate PR to gate on response.ok and set isError once in the runtime so both tool sets benefit — flagging it here rather than folding it in, to keep this PR strictly zero-regression.

Verified

57 v1 + 5 v2 = 62 tools register · v1 tools.generated.ts byte-identical · 13 unit tests pass · lint + CI Verify green.

@johnxie
johnxie merged commit f4c9cf5 into main Jun 27, 2026
1 check passed
@johnxie
johnxie deleted the feat/v2-tool-layer branch June 27, 2026 09:17
johnxie added a commit that referenced this pull request Jun 27, 2026
…ooks (#57)

The v2 layer (#55, shipped in 0.1.0) added 5 tools but the README still said 57.
Update the count in all 5 places (headline, TOC anchor, Tools header, diagram,
positioning line) and add an 'Agent Chat & Webhooks (API v2, beta)' category listing
promptAgent / listConversations / getConversation / subscribeWebhook / unsubscribeWebhook.
Docs only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants