Wrike MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Wrike MCP Server@Wrike MCP Server list tasks in my project"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Wrike MCP Server
A Model Context Protocol (MCP) server for the Wrike API v4, designed for organisation-level deployment on the web: each user connects their own Wrike account, and every token and secret stays encrypted at rest and invisible over the wire.
Built with TypeScript, @modelcontextprotocol/sdk (Streamable HTTP transport), Express, and Zod. Tested with Vitest (157 tests) plus an end-to-end smoke script.
How it works (per-user auth)
Admin (once) User (self-service) MCP client (Claude etc.)
────────── ──────────────────── ───────────────────────
deploy server with visits <host>/wrike/connect ───► Wrike consent page
OAuth app credentials logs in with THEIR Wrike acct (Wrike, not this server)
◄── redirected back with code
server exchanges code, encrypts
user's tokens, shows ONE-TIME
connection token (wmc_...) add server URL +
Authorization: Bearer wmc_...
→ MCP session bound to userNobody shares credentials. Each user authorizes with their personal Wrike login on Wrike's own pages; the server never sees a password.
Users only see their own data. Every MCP session is bound to one user; tool calls run against that user's Wrike tokens, data-center host included (US/EU resolved per user from Wrike's token response).
Self-service lifecycle. Users connect at
/connect, get a one-time connection token, and can revoke themselves at/revoke.Token refresh is automatic and single-flight per user (Wrike rotates refresh tokens; concurrent refreshes would lock the user out).
Related MCP server: Google Workspace MCP Server
MCP tools (26)
Area | Tools |
Core |
|
Spaces |
|
Folders |
|
Tasks |
|
Comments |
|
Timelogs |
|
Attachments |
|
get_attachment takes a mode: 'metadata' (default, no file bytes), 'url' (a short-lived signed link on this server for a person to open in a browser — requires PUBLIC_BASE_URL), or 'download' (base64 file content inline, for a program to consume — never use it to relay a file to someone in a chat reply, since an LLM cannot reproduce a long base64 string verbatim).
Full object support: create_task/update_task accept the complete Wrike task object — dates (type, start, due, duration, workOnWeekends), effortAllocation (mode, totalEffort, allocatedEffort, dailyAllocationPercentage, responsibleAllocation[]), custom fields, metadata, responsibles, followers, superTasks, priority, billing type, custom statuses. Schemas mirror the official OpenAPI definitions at developers.wrike.com.
Setup (admin, once)
npm install
npm run build
cp .env.example .env # app-level credentials only — no user tokens
npm startConfiguration
Variable | Mode | Description |
| both | Listen address. Keep |
| both |
|
| pat | Permanent token (single account only). |
| oauth | App credentials from the Wrike App Console — these identify the app, not any user. |
| both | 64 hex chars (32 bytes) — AES-256-GCM key for the encrypted token store. Generate: |
| both | Encrypted store location (default |
| both | Public origin exactly as MCP clients see it, including any path prefix and no trailing slash — normally |
User flow (oauth mode)
Header-token flow (any MCP client):
User visits
https://mcp.example.com/wrike/connect(optionally?user=their-handle). A handle that is already connected is refused with 409 — pick another, or revoke the existing connection first, since reusing it would repoint every connection token already issued for that handle at the new Wrike account.They approve access on Wrike's own consent page.
The page shows their one-time connection token:
Authorization: Bearer wmc_....They paste that into their MCP client (most clients support custom headers on remote MCP servers).
Done — all 26 tools now operate on their Wrike data.
Native sign-in (no token copying)
With PUBLIC_BASE_URL set, the server exposes the MCP OAuth discovery and
authorization endpoints (/.well-known/oauth-protected-resource,
/.well-known/oauth-authorization-server, /oauth/authorize, /oauth/token,
/oauth/register, /oauth/revoke-token). Metadata is served both at those
paths and at the RFC 8414 §3.1 / RFC 9728 §3.1 issuer-suffixed form
(/.well-known/oauth-authorization-server/<issuer path>) — behind a
path-prefixed proxy that form is host-rooted and needs its own proxy route, so
see deploy/README.md. MCP clients like Claude then
handle everything in-app:
the user clicks Connect, confirms which MCP client is asking on this server's
consent screen, approves on Wrike's consent page, and the client receives the
token itself — same per-user storage and isolation, no wmc_... pasting.
Registration is open (any client may self-register via DCR), so the consent
screen names the requesting client and its redirect URI before the user reaches
Wrike. client_name is supplied by the client and is not verified — it is
shown so the user can spot a client they did not start, and the screen says so.
Confirming the screen posts to /connect/confirm; the flow cannot be skipped
by a cross-site form (see Security model).
Claude setup: Settings → Connectors → Add custom connector →
URL https://mcp.example.com/wrike/mcp (or https://wrike.example.com/mcp if
the server has its own hostname) → Authentication "Always required" →
OAuth client "No client ID — register one automatically" (dynamic client
registration). The user signs in through the browser once and is done.
The header-token flow above keeps working in parallel for clients without OAuth support.
Deployment (DigitalOcean droplet, Docker)
Recommended layout: one host with a shared proxy, one path per MCP server
(https://mcp.example.com/wrike, https://mcp.example.com/github, …), so
adding a server is a route rather than a new certificate. That layout — and the
PUBLIC_BASE_URL, WRIKE_REDIRECT_URI and /.well-known/ proxy routes it
needs — is in deploy/README.md.
The walkthrough below is the simpler case: this server alone on its own
hostname, with the bundled proxy. Swap wrike.example.com for your own.
A Dockerfile + docker-compose.yml are included: the app runs as a non-root
user in a minimal image, Caddy terminates TLS and obtains its own certificate
from Let's Encrypt, and the only published ports are 80/443 on Caddy. The app
container is reachable solely over the private compose network.
# 1. On a fresh Ubuntu droplet, install Docker
apt update && apt install -y ca-certificates curl git
curl -fsSL https://get.docker.com | sh
# 2. Get the code
git clone <your-repo> /opt/wrike-mcp && cd /opt/wrike-mcp
# 3. Point a DNS A record at the droplet, then set the hostname and ACME
# email in the Caddyfile (both are placeholders in the checked-in copy):
nano Caddyfile # wrike.example.com, admin@example.com
# 4. Configure (app-level credentials only — no user tokens)
cp .env.example .env
nano .env # WRIKE_CLIENT_ID/SECRET/REDIRECT_URI, TOKEN_ENCRYPTION_KEY, PUBLIC_BASE_URL
# 5. Launch — Caddy gets the certificate on first start
docker compose up -d --build
docker compose ps # wrike-mcp healthy, caddy up
curl https://wrike.example.com/healthzThere is no certbot step and no renewal timer: Caddy renews automatically.
Certificates live in the caddy-data volume, so keep it across rebuilds.
Users then connect at https://wrike.example.com/connect.
Running more MCP servers on the same droplet? Use the shared-proxy layout
described above: one Caddy container fronts everything by path, and each MCP
server attaches to the common network with no published ports of its own.
See deploy/README.md,
deploy/caddy/ and
docker-compose.override.shared-proxy.yml.example.
Updating: git pull && docker compose up -d --build (the encrypted token
volume survives rebuilds, so users keep their connections).
What runs where
Concern | Handled by |
TLS (automatic Let's Encrypt), SSE-friendly proxying, 60 MB body limit | Caddy container (published 80/443 only) |
Process isolation, non-root (uid 1001), memory cap | app container on the private network |
Secrets |
|
Encrypted token store | named volume |
Restart policy |
|
Certificates | named volume |
Firewall (optional hardening, since only 80/443 are published anyway):
ufw allow OpenSSH && ufw allow 443 && ufw allow 80 && ufw enableNote: sessions and rate limiting are in-memory, so this stack is single-instance. For multiple replicas you'd need shared session/rate state (or sticky sessions) — one droplet is fine as is.
apt update && apt install -y caddy git curl
adduser --disabled-password mcp
npm ci --omit=dev && npm run build && chown -R mcp:mcp /opt/wrike-mcp
cat >/etc/wrike-mcp.env <<'EOF'
PORT=3000
HOST=127.0.0.1
WRIKE_CLIENT_ID=<app client id>
WRIKE_CLIENT_SECRET=<app client secret>
WRIKE_REDIRECT_URI=https://wrike.example.com/oauth/callback
WRIKE_SCOPES=Default,wsReadWrite
TOKEN_ENCRYPTION_KEY=<64 hex chars>
TOKEN_STORE_PATH=/var/lib/wrike-mcp/tokens.json
EOF
chmod 600 /etc/wrike-mcp.env
systemd unit with NoNewPrivileges/PrivateTmp/ProtectSystem=strict,
then Caddy as in ./Caddyfile but with `reverse_proxy 127.0.0.1:3000`.Security model
Secrets never touch the repo or users — the admin's env file holds only app-level OAuth credentials; every user token lives server-side, AES-256-GCM encrypted (
IV ‖ auth-tag ‖ ciphertext), key never on disk, tamper-detecting, atomic0600writes, serialized writes (no concurrent-write races).Connection tokens (the per-user MCP credentials) are stored only as HMAC-SHA256 hashes, compared in constant time, shown to the user exactly once.
CSRF-safe OAuth — HMAC-signed, expiring
statebound to the pending user; forged/replayed states rejected.MCP client consent — registration is open (DCR), so
/connectshows which client is asking, with itsclient_idand redirect URI, before the user reaches Wrike;client_nameis client-supplied and shown as unverified. Confirmation carries a nonce bound to aSameSite=Laxcookie,__Host-prefixed over HTTPS, so the screen survives both a cross-site auto-submitted form and a cookie planted from a sibling subdomain.No handle takeover — user slots are claimed atomically; a handle that is already connected is refused (409) rather than overwritten, since overwriting would repoint connection tokens already issued for it at another person's Wrike account.
Codes and tokens — authorization codes are single-use, short-lived and PKCE-bound (S256 required); token responses and the one-time token page send
Cache-Control: no-store.No cross-user access — sessions are bound to a single user at creation; tools run only against that user's client.
Secrets never cross the wire — central
redact()on all responses; Wrike access/refresh tokens and connection tokens never appear in endpoints, errors, or logs (asserted by tests).Rate limiting on every public endpoint:
/mcp,/connect,/connect/confirm,/oauth/authorize,/oauth/token,/oauth/register,/oauth/revoke-token,/oauth/callback,/revoke.
Tests
npm test # 157 unit/integration tests
node scripts/e2e-peruser.cjs # end-to-end smoke (build first)Coverage: config validation; token-store crypto (round-trip, tamper, wrong key, 0600, write-queue serialization, multi-user isolation); connection-token hashing/resolution; OAuth state signing/expiry/user-binding; per-user auth manager (multi-user hosts, refresh single-flight per user, isolation between users, revocation); client (per-user bearer/host, 401→refresh→retry, 429 backoff, multipart upload); full-object tool validation; HTTP endpoints via supertest (401/WWW-Authenticate, connect flow with mocked Wrike, revoke, secret-leak assertions); MCP-native OAuth (discovery at both metadata locations, DCR validation, PKCE, single-use codes, resource indicators, error-redirect vs JSON, consent screen and its nonce cookie, handle-collision races, revocation); and redaction of both shape-matched and registered literal secrets.
Development
npm run dev # run via tsx
npx tsc --noEmit # type-checkNotes
PAT mode is single-user by nature (one token = one Wrike account); organisation deployments should use OAuth mode.
If a user's Wrike tokens are revoked in Wrike (password reset, deactivation), that user revisits
/connect; other users are unaffected.
This server cannot be deployed
Maintenance
Related MCP Connectors
Authenticated, user-scoped MCP connectors for 30+ business systems.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Unified MCP Server is a remote MCP connector for AI agents and vertical AI products that provides access to 22,000+ authorized SaaS tools across 400+ integrations and 24 categories directly inside LLMs (Claude, GPT, Gemini, Cohere). Tools operate only on explicitly authorized customer connections, enabling agents to safely read and write against live third-party systems.
Related MCP Servers
- FlicenseBqualityDmaintenanceComplete MCP server for Wrike project management with 70+ tools and 20 interactive React apps, enabling task, project, and workflow management via natural language.88-
- AlicenseBqualityDmaintenanceProvides natural language control over Google Workspace services (Calendar, Drive, Gmail, Docs, Sheets, Slides, Forms, Tasks, Chat) via MCP, with OAuth 2.1 multi-user authentication.100MIT
- AlicenseBqualityDmaintenanceConnects AI assistants like Claude to productivity tools such as Notion, Monday, Jira, and Slack via a remote MCP integration hub.7128 npmMIT
- AlicenseCqualityDmaintenanceEnables connecting MCP hosts like Claude to various productivity tools (Notion, Monday, Jira, etc.) through a secure no-code integration hub.7128 npmMIT