Skip to content

feat(server): space bundle export/import — Genesis .tsk app kits (66 → 68) - #71

Open
johnxie wants to merge 5 commits into
feat/v2-webhook-crud-toolsfrom
feat/v1-bundle-tools
Open

feat(server): space bundle export/import — Genesis .tsk app kits (66 → 68)#71
johnxie wants to merge 5 commits into
feat/v2-webhook-crud-toolsfrom
feat/v1-bundle-tools

Conversation

@johnxie

@johnxie johnxie commented Jul 28, 2026

Copy link
Copy Markdown
Member

Stacked PR — based on feat/v2-webhook-crud-tools (signed webhook tools, 62 → 66). Merge that first; this PR's own diff is the last commit.

Partially addresses #12.

What

Refreshes the vendored v1 OpenAPI spec — 14 months stale (last synced 2025-05, live spec now has 64 operations vs the vendored 57) — and enables the two JSON space-bundle operations as tools:

Tool Op Endpoint Hints
bundleExport GET /bundles/{spaceId}/export Export a space (subspace / app) as a SpaceBundleData v1 JSON bundle — agents, automations, projects, templates, apps (media excluded) readOnlyHint: true
bundleImport POST /bundles/{workspaceId}/import Validate a SpaceBundleData v1 bundle and install all items into the target workspace write, non-destructive

66 → 68 tools (59 v1 + 9 v2). Bundles are the "Workspace DNA" format behind Genesis .tsk app kits (the ZIP export endpoint supports format=tsk).

Not enabled (out of scope — raw binary request/response bodies need a handwritten wrapper, MCP tools here are JSON-only): bundleExportZip, bundleImportZip, mediaUpload, mediaDownload, mediaDownloadAll.

Plan-quota note: bundleImport installs projects/agents/automations into the target workspace; the API enforces plan limits server-side, so an over-quota import failing with a 4XX error is expected behavior, not a tool bug. The spec documents a generic 4XX error response for both ops. (The spec makes no claim about secret handling in exports, so the README doesn't either.)

Upstream spec-generator bug: 12 malformed $refs (patched here, to be filed internally)

The live YAML from https://www.taskade.com/api/documentation/v1/yaml contains 12 $refs that are relative to the enclosing JSON-schema root instead of the OpenAPI document root (they start with #/properties/...). They crash @readme/openapi-parser with MissingPointerError, so yarn build fails on the raw fetch. This PR patches each to the equivalent absolute pointer (resolved against the nearest ancestor node where the pointer is valid — verified unique for all 12). A fully-dereferenced deep-compare of the patched spec vs an independently patched copy (inlining instead of repointing) is byte-identical, and the full v1 codegen runs clean.

Until the upstream generator is fixed, re-running yarn fetch:openapi will re-introduce the broken refs — the 12 patches below must be re-applied after any refetch.

# Location (JSON pointer) Malformed $ref
1 components/schemas/Field/…/fillerConfig/anyOf/1/properties/sourceRef #/properties/data/anyOf/1/properties/fillerConfig/anyOf/0/properties/sourceRef
2 components/schemas/Field/…/fillerConfig/anyOf/2/properties/sourceRef same as 1
3 components/schemas/Field/…/fillerConfig/anyOf/4/properties/sourceRef same as 1
4 components/schemas/Field/properties/data/anyOf/5/properties/fillerConfig #/properties/data/anyOf/1/properties/fillerConfig
5 components/schemas/Field/properties/data/anyOf/7/properties/fillerConfig same as 4
6 GET /agents/{agentId}/public-agent 200 → …/preferences/meta/image/allOf/0 #/properties/item/properties/data/properties/avatar/anyOf/0/properties/data/properties/file
7 GET /agents/{agentId}/public-agent 200 → …/preferences/launcherIcon/anyOf/1/image same as 6
8 PATCH /agents/{agentId}/public-agent requestBody → …/preferences/launcherIcon/anyOf/1/image #/properties/preferences/properties/meta/properties/image
9 PATCH /agents/{agentId}/public-agent 200 → …/preferences/meta/image/allOf/0 same as 6
10 PATCH /agents/{agentId}/public-agent 200 → …/preferences/launcherIcon/anyOf/1/image same as 6
11 GET /public-agents/{publicAgentId} 200 → …/preferences/meta/image/allOf/0 same as 6
12 GET /public-agents/{publicAgentId} 200 → …/preferences/launcherIcon/anyOf/1/image same as 6

(5 under components/schemas/Field, 7 under the /agents/{agentId}/public-agent + /public-agents/{publicAgentId} paths.)

Schema drift in existing tools (intended — this PR is not additive-only)

Regenerating from the fresh spec updates the input schemas of 6 existing tools (a year of Field/Block/Task/Date/SpaceAgent evolution):

Tool Drift
projectBlocksGet pagination cursors after/before lose .uuid() (now plain strings)
projectTasksGet same cursor change + limit gains .lte(1000)
taskPutDate Date schema: date/time gain format descriptions; timezone (IANA) documented on start/end
folderCreateAgent SpaceAgent: new persona enum values (Designer, Brainstormer, Builder, ResearchAnalyst, …), skill description field, inputPlaceholder now nullable
agentUpdate same SpaceAgent evolution
agentPublicUpdate public-agent preferences drift (nullable fields, richer descriptions)

Two tools the drift was expected to touch — agentPublicGet, publicAgentGet — are unchanged: their inputs are path-params only, and their drift (the avatar/file response schemas, incl. refs 6–12 above) lives in response schemas, which MCP tool definitions don't embed.

Assertions

  • A1 (name-set diff vs base): 57 → 59 v1 tools; added exactly ["bundleExport","bundleImport"]; removed []; zero renames. bundleExport has readOnlyHint: true.
  • A2 (schema drift): exactly the 6 tools tabled above changed; the two "missing" expected tools are explained (response-side drift only).
  • A3 (dereference proof): tsx scripts/gen-taskade-mcp-tools.ts completes with no MissingPointerError; yarn buildLerna (powered by Nx) Successfully ran target build for project @taskade/mcp-server, and a post-commit rebuild leaves git status --porcelain empty. Dereferenced patched spec is deep-equal to the independently verified fixed spec.
  • A4 (count consistency): git grep -nE '6[26] tools' finds no live claims (only the previous PR's own changeset entry "62 → 66 tools", which is that change's accurate history); "68 tools" present in root README.md (3×), packages/server/README.md, and server.json.

CI gates

yarn build (regen both tool files + esbuild + lint:fix, tree stays clean) · yarn lint · yarn test (23/23) — all green locally.

Manual live-QA checklist (needs a real TASKADE_API_KEY)

  • bundleExport a small test space → response ok: true, item.version === "1", item.items keyed map with space-bundle-*-item types
  • bundleImport that bundle into a scratch workspace → ok: true + BundleInstallation counts match the exported items
  • Verify the round-tripped projects/agents/automations open correctly in the scratch workspace
  • Over-quota import on a free workspace → clean 4XX error surfaced as a tool error (not a crash)

…→ 68)

Refresh the 14-month-stale vendored v1 OpenAPI spec (64 ops live vs 57
vendored), patch 12 malformed root-relative $refs from the upstream spec
generator to absolute pointers so the spec dereferences cleanly, and enable
the two JSON bundle operations as tools: bundleExport + bundleImport.
Schema drift from a year of API evolution updates 6 existing tools'
input schemas (intended). Zip/media raw-binary ops stay disabled.
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9dae1f5

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

This PR includes changesets to release 1 package
Name Type
@taskade/mcp-server Minor

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

@johnxie

johnxie commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Inherited the #70 review fixes via merge (7b0c4e7 merges b64aa52), plus one branch-local cleanup (de4de0a):

  • Webhook id double-encoding fix carried over: tools.v2.generated.ts on this branch now emits the corrected getWebhook/deleteWebhook descriptions ("Pass the webhook’s raw target URL as id — do NOT URL-encode it; encoding is applied automatically"). Post-merge yarn build regen was byte-stable.
  • server.json registry-safe here too: conflict resolved to Connect Taskade to any AI assistant — 68 tools: projects, tasks, agents, agent chat, webhooks.94 chars (≤ the registry's 100-char cap), python-verified.
  • Stub README is now count-free (de4de0a): the hardcoded "68 tools" in packages/server/README.md is gone — the stub no longer carries a number that every tool-count change must remember to bump (grep -E '[0-9]+ tools' packages/server/README.md → no matches). Counts live only in the canonical root README + server.json.

Gates: yarn build green with clean tree, yarn lint green, yarn test green (4 files, 24 tests). Tool sets verified: v1 = 59 (incl. bundleExport/bundleImport), v2 = 9, total 68.

@johnxie

johnxie commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Code review

Found 1 issue:

  1. The 'Space Bundles' README note lists only four excluded raw-binary endpoints (bundleExportZip, bundleImportZip, mediaUpload, mediaDownload), silently omitting mediaDownloadAll — which the PR body and the changeset both correctly include as the fifth excluded operation (bug due to self-inconsistency between README.md and .changeset/spec-refresh-bundle-tools.md; mediaDownloadAll is a distinct operationId in taskade-public.yaml)

mcp/README.md

Lines 270 to 272 in d051fc7

> **Note:** imports are subject to your plan's server-side limits — an over-quota import fails with a `4XX` error. The raw ZIP/`.tsk` and media file endpoints (`bundleExportZip`, `bundleImportZip`, `mediaUpload`, `mediaDownload`) exchange binary payloads and are not exposed as tools yet.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

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.

1 participant