Work · MCP · TypeScript

Wrike MCP — conversation tools over a live project API

Production MCP servers for Wrike, built in TypeScript on the official SDK. The tool set is conversation-shaped: resolve a permalink, load an item with its custom fields and workflow status, create the right child type. It does not mirror the REST API. A sibling server is locked to one Wrike space.

My role Builder of the servers: tool surface, item-type registry, and the space-locked variant used on live delivery work.

Stack TypeScript · Bun · MCP SDK · Zod · Wrike API · YAML item-type registry · stdio and streamable HTTP

Context
In a multi-site hospitality IT/PMO environment, project truth lived in Wrike, but keeping it current was a human bottleneck: PMs chased updates across chat and spreadsheets, then re-typed status into the system of record. Stakeholders (support, ops, vendors, finance-facing programs) needed trustworthy live status without adding headcount. Constraint: agents could help only if writes were governed—no unbounded mutations in production Wrike.
Intervention

Shipped conversation-driven Wrike MCP servers so an agent loads and updates items through a few tools, with item types and workflows generated from the API into YAML.

Phase 1 — Tool shape

One tool per file. get_wrike_item accepts a permalink or id and returns title, description, custom item type, task status, and surface custom fields. convert_wrike_link translates a browser URL to an API id. list_folder_contents and get_custom_item_types cover navigation. Creation prefers add_item_to_parent, which picks the child type from the hierarchy, including natural-language aliases.

Phase 2 — Registry, not a hand-maintained ID list

wrike:bootstrap writes item types from the API; wrike:bootstrap-workflows writes workflows and custom statuses. An overlay YAML holds hierarchy and aliases and is validated on its own. The server merges generated config and overlay. Mutations are narrow: rename, description, status or customStatusId (not both), folder parents.

Phase 3 — Two transports, one space lock

The same tool idea ships as stdio and as streamable HTTP. A sibling server is locked to a single Wrike space so item types, workflows, and custom fields cannot drift onto another account. Agents draft and flag; a person still accepts the change in Wrike.

Risks & how they were handled
  • Wrong writes in production Wrike → mitigated with allow-lists, dry-run/review habits, and audit logs.
  • PM distrust of “AI status” → kept humans accountable; agents draft/flag, PMs accept.
  • API rate limits / outages → retries and fail-closed behaviour when Wrike was unavailable.
What I'd do differently
  • Start with an even thinner tool surface (read + flag only) before allowing drafts.
  • Codify eval fixtures earlier from real anonymized task samples.
Outcome
Before: PMs acted as status relays—typically on the order of ~15–30 min per covered project just to chase and paste updates into Wrike (est.). After: production MCP lets agents read/update live tasks, draft Definition of Done, and flag blockers under governance—so the PM pass is mostly review in the system of record (roughly a few minutes per agent batch when the project is covered, est.). Status no longer depends on chat archaeology.

System map

PM / Ops need

live delivery

Conversation tools

not 1:1 REST

YAML item types

API bootstrap

Wrike MCP

Space-locked sibling

one space

stdio and HTTP

two transports

What it does

Loads live Wrike items with custom fields

Drafts and flags under governance

Leaves final accept to a person in Wrike

Agents act through a thin MCP surface; PMs stay accountable in the system of record.

What shipped

  • Production MCP servers Conversation-shaped tools on live Wrike — resolve a permalink, load an item, create a typed draft.
  • YAML type registry Item types and workflows bootstrapped from the API, with a validated overlay.
  • Space-locked sibling A variant pinned to one Wrike space so types cannot drift across accounts.

Stack TypeScript Bun MCP SDK Zod Wrike API YAML stdio HTTP

← AI_devs Next: LocalOutbound →