Linear MCP Server Template
Overview
This template provides a fully production-ready Model Context Protocol (MCP) server built in TypeScript, using HTTP transport and API key authentication. It integrates with the Linear project management platform, exposing tools that allow AI assistants like Claude to query issues, create tasks, update statuses, and search across your Linear workspace โ all through a secure, observable, and containerised service.
The server is designed for teams who want to give AI coding assistants real access to their project management workflow without sacrificing security or observability. Every request is authenticated via API key, traced with a correlation ID through structured JSON logs (Pino), and exposed through a health endpoint that reports upstream Linear API reachability.
This is an intermediate-level template, appropriate for developers who are comfortable with TypeScript and Node.js and want to learn how to build maintainable MCP servers that are ready to deploy to production on day one.
What You'll Learn
- How to scaffold and register MCP tools using the official
@modelcontextprotocol/sdkpackage - How to implement HTTP transport for MCP with Express, including proper SSE and POST endpoint wiring
- How to validate and enforce API key authentication via Express middleware
- How to use Pino for structured JSON logging with per-request correlation IDs
- How to expose a
/healthendpoint that checks live connectivity to an external API (Linear) - How to use Zod for runtime input validation on every tool invocation
- How to write a multi-stage Dockerfile that produces a minimal production image
- How to configure a docker-compose environment for local development with hot reload
- How to write Jest + Supertest integration tests that cover auth, health, and tool execution
- How to manage all configuration through environment variables with documented defaults
- How to handle upstream API errors gracefully and return well-structured MCP error responses
- How to model Linear domain objects (issues, projects, teams) with full TypeScript types
Architecture
Claude Desktop / Claude Code
โ
โ HTTP POST /mcp (MCP JSON-RPC)
โผ
Express HTTP Server
โ
โโโ API Key Middleware (validates X-API-Key header)
โโโ Correlation ID Middleware (injects req-id into Pino child logger)
โ
โโโ GET /health โโโบ HealthChecker โโโบ Linear API ping
โ
โโโ POST /mcp โโโโโโบ MCP SDK Server
โ
โโโโโโโโโโโดโโโโโโโโโโโ
โ Tool Router โ
โโโโโโโโโโโฌโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โผ โผ โผ
list_issues create_issue update_issue_status
โ โ โ
โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโ
โ
Linear REST API
(api.linear.app/graphql)
Project Structure
linear-mcp-server/
โโโ src/
โ โโโ __tests__/
โ โ โโโ setup.ts
โ โ โโโ health.test.ts
โ โ โโโ server.test.ts
โ โโโ tools/
โ โ โโโ listIssues.ts
โ โ โโโ createIssue.ts
โ โ โโโ updateIssueStatus.ts
โ โโโ server.ts
โ โโโ config.ts
โ โโโ auth.ts
โ โโโ logger.ts
โ โโโ health.ts
โโโ Dockerfile
โโโ docker-compose.yml
โโโ package.json
โโโ tsconfig.json
โโโ jest.config.ts
โโโ .env.example
โโโ .gitignore
Prerequisites
- Node.js 20+ and npm 10+
- A Linear account with an API key (Settings โ API โ Personal API keys)
- Docker Desktop (optional, for container workflow)
- Claude Desktop or another MCP-compatible client for end-to-end testing
Quick Start
# 1. Clone or copy this template
git clone https://github.com/your-org/linear-mcp-server.git
cd linear-mcp-server
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env โ set LINEAR_API_KEY and MCP_API_KEY at minimum
# 4. Run in development mode (ts-node with watch)
npm run dev
# 5. Verify the server is healthy
curl http://localhost:3000/health
# 6. Run tests
npm test
Features
- Three production-quality Linear tools:
list_issues,create_issue,update_issue_statuswith Zod input validation and full TypeScript types - API key authentication: Every MCP request requires a valid
X-API-Keyheader; the health endpoint is publicly accessible - Structured Pino logging: JSON output with
req_idcorrelation on every log line; level controlled byLOG_LEVELenv var - Health monitoring:
/healthreturns{ status, uptime, linear: { reachable } }and checks live Linear API connectivity - Multi-stage Docker build:
builderstage compiles TypeScript;productionstage runs only compiled JS with no dev dependencies - docker-compose: One-command local stack with volume mounts for hot reload via
ts-node - Jest + Supertest tests: Auth rejection, health endpoint, and tool execution all covered
- Zod configuration validation: Server refuses to start if required environment variables are missing or malformed
- Graceful shutdown: SIGTERM/SIGINT handlers drain in-flight requests before exiting
Works With
- Claude Desktop โ add server to
claude_desktop_config.jsonusing the HTTP transport URL - Claude Code โ reference via
--mcp-serverflag pointing tohttp://localhost:3000/mcp - Cursor โ configure in MCP settings panel with URL and API key header
- Windsurf โ add as a remote MCP server in workspace settings
- Continue โ add MCP server block in
config.jsonwith transport: http
Configuration
| Variable | Description | Required | Default |
|---|---|---|---|
PORT | HTTP port the server listens on | Optional | 3000 |
MCP_API_KEY | Secret key clients must send in X-API-Key | Required | โ |
LINEAR_API_KEY | Your Linear personal API key | Required | โ |
LINEAR_TEAM_ID | Default Linear team ID for issue creation | Optional | โ |
LOG_LEVEL | Pino log level: trace debug info warn error | Optional | info |
NODE_ENV | development or production | Optional | development |
HEALTH_CHECK_TIMEOUT_MS | Timeout for Linear connectivity check in ms | Optional | 5000 |
Deployment
Docker (Recommended)
# Build production image
docker build -t linear-mcp-server:latest .
# Run with environment file
docker run -d \
--name linear-mcp \
-p 3000:3000 \
--env-file .env \
linear-mcp-server:latest
# Check logs
docker logs -f linear-mcp
Railway
# Install Railway CLI
npm install -g @railway/cli
railway login
railway new linear-mcp-server
railway up
# Set environment variables in Railway dashboard or via CLI
railway variables set MCP_API_KEY=your-secret LINEAR_API_KEY=lin_api_xxx
Fly.io
# Install flyctl and authenticate
curl -L https://fly.io/install.sh | sh
fly auth login
# Launch app (uses existing Dockerfile)
fly launch --name linear-mcp-server --region lax
# Set secrets
fly secrets set MCP_API_KEY=your-secret LINEAR_API_KEY=lin_api_xxx
# Deploy
fly deploy
Production Checklist
-
MCP_API_KEYis a cryptographically random string (at least 32 characters) -
LINEAR_API_KEYis stored as a secret, not committed to version control -
NODE_ENV=productionis set in your deployment environment -
LOG_LEVEL=infoorwarnin production (avoiddebug/trace) - Health endpoint is reachable by your load balancer or uptime monitor
- Docker image is built from the
productionstage only -
.envfile is listed in.gitignoreand never committed - HTTPS/TLS is terminated at the load balancer or reverse proxy in front of this server
- Rate limiting is applied at the gateway layer before reaching this server
-
LINEAR_TEAM_IDis set to avoid requiring it on every tool call - Container restart policy is set to
unless-stoppedor equivalent - Logs are forwarded to a centralised log aggregator (Datadog, Logtail, etc.)
-
/healthis monitored every 30 seconds with alerting on failure - Image is pinned to a specific tag in production, not
latest - npm audit is run and all high/critical vulnerabilities are resolved
Testing
Automated Tests
# Run full test suite
npm test
# Watch mode during development
npm run test:watch
# With coverage report
npm run test:coverage
Manual curl Testing
# Health check (no auth required)
curl -s http://localhost:3000/health | jq .
# Rejected request โ missing API key
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/mcp
# Expected: 401
# List issues via MCP JSON-RPC
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "X-API-Key: your-mcp-api-key" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_issues",
"arguments": { "limit": 5, "stateFilter": "started" }
}
}' | jq .
Claude Desktop Integration
Add this to your ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"linear": {
"transport": "http",
"url": "http://localhost:3000/mcp",
"headers": {
"X-API-Key": "your-mcp-api-key"
}
}
}
}
Then restart Claude Desktop and ask: "Show me my open Linear issues" or "Create a Linear issue titled 'Fix login bug' in the backend team".