Remote MCP server to manage Linear via HTTP, deployed on Cloudflare Workers. Built with TypeScript, Hono, and the MCP SDK.
- MCP HTTP endpoint at
/mcpcompatible with clients like Claude Desktop - Health endpoint at
/health - Linear tools:
linearCreateIssue: create issues (supportsstateby name/type)linearUpdateIssue: update title, description, state (UUID or alias), assignee, due datelinearComment: add Markdown comments by issue id or key (e.g.,ENG-123)linearDeleteIssue: delete issues by id or keylinearWebhookCreate/linearWebhookDeletelinearListIssues: filter by team, assignee, and date rangeslinearListIssuesToday: issues updated todaylinearGetIssue: detailed issue view by id/keylinearListTeams: list team keys and nameslinearListStates: list workflow states for a team
- Node 18+
- Cloudflare account + Wrangler
npm iwrangler secret put MCP_SERVER_NAME # e.g., linear-mcp
wrangler secret put LINEAR_GRAPHQL_URL # https://api.linear.app/graphql
wrangler secret put LINEAR_API_KEY # personal key (Authorization: <KEY>)
# Optional for webhooks/forwarding (not required for MCP tools)
wrangler secret put LINEAR_WEBHOOK_SECRET
wrangler secret put FORWARD_URL
wrangler secret put FORWARD_SIGNING_SECRETnpm run dev
curl -s http://127.0.0.1:8787/health | jqList tools:
curl -s -X POST http://127.0.0.1:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/list"}' | jqCreate issue:
curl -s -X POST http://127.0.0.1:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"2","method":"tools/call",
"params":{"name":"linearCreateIssue",
"arguments":{"teamKey":"ENG","title":"Test","dueToday":true}}}' | jqUpdate issue to active (name/type alias resolution):
curl -s -X POST http://127.0.0.1:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"3","method":"tools/call",
"params":{"name":"linearUpdateIssue",
"arguments":{"idOrKey":"ENG-123","state":"active"}}}' | jqList issues today for a team:
curl -s -X POST http://127.0.0.1:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"4","method":"tools/call",
"params":{"name":"linearListIssuesToday",
"arguments":{"teamKey":"ENG"}}}' | jqnpm run deployWorker URL example:
https://<your-worker>.workers.dev
You can call /mcp directly:
curl -s -X POST https://<your-worker>.workers.dev/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/list"}' | jq
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"linear": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://<your-worker>.workers.dev/mcp"],
"url": "https://<your-worker>.workers.dev/mcp",
"name": "Linear MCP",
"version": "1.0.0"
}
}
}Restart Claude. The tools will appear automatically.
Receiver: POST /webhooks/linear validates HMAC (linear-signature) and optionally forwards signed payloads to FORWARD_URL with x-mcp-signature and x-mcp-timestamp.
Create via tool:
curl -s -X POST http://127.0.0.1:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"5","method":"tools/call",
"params":{"name":"linearWebhookCreate",
"arguments":{"url":"https://<your-worker>.workers.dev/webhooks/linear",
"allPublicTeams":true}}}' | jq- Codebase
src/index.ts: Hono app, MCP mount/mcp, webhook receiver, HMAC verificationsrc/factory.ts: tool registrationsrc/tools/linear.ts: GraphQL helpers + queries/mutations- Tools in
src/tools/*
- Durable Object
- Defined as
LinearMCPinwrangler.jsoncwith bindingMCP_OBJECT
- Defined as
- Type generation
npm run cf-typegenwritesworker-configuration.d.ts
- All credentials via Cloudflare Secrets
- HMAC verification for incoming webhooks
- Signed forwarding with
x-mcp-signature
- 401/403 from Linear: verify
LINEAR_API_KEYandLINEAR_GRAPHQL_URL = https://api.linear.app/graphql - “Team no encontrado”: check
teamKeyvalue in Linear - State updates:
stateIdmust be UUID; tools accept aliases and resolve automatically
MIT