Templatetypescriptintermediateโœ“ Production ReadyFree

TypeScript MCP Server with GitHub Integration

A production-ready TypeScript MCP server with HTTP transport and API key authentication, featuring GitHub integration tools for repository management, issue tracking, and pull request workflows. Includes structured Pino logging with correlation IDs, health monitoring, multi-stage Docker builds, and comprehensive error handling.

๐Ÿ“– 12 min readโš™๏ธ Setup: 20 minutesutilities

Files(10)

{
  "name": "github-mcp-server",
  "version": "1.0.0",
  "description": "Production-ready TypeScript MCP server with GitHub integration, HTTP transport, and API key auth",
  "main": "dist/server.js",
  "scripts": {
    "build": "tsc --project tsconfig.json",
    "start": "node dist/server.js",
    "dev": "ts-node-dev --respawn --transpile-only src/server.ts",
    "typecheck": "tsc --noEmit",
    "clean": "rm -rf dist",
    "prebuild": "npm run clean"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "1.10.2",
    "@octokit/rest": "21.0.2",
    "@octokit/types": "13.6.2",
    "express": "4.21.2",
    "pino": "9.5.0",
    "pino-http": "10.3.0",
    "uuid": "11.0.3",
    "zod": "3.24.1"
  },
  "devDependencies": {
    "@types/express": "4.17.21",
    "@types/node": "22.10.2",
    "@types/uuid": "10.0.0",
    "ts-node-dev": "2.0.0",
    "typescript": "5.7.2"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "license": "MIT"
}

TypeScript MCP Server with GitHub Integration

Overview

This template provides a fully production-ready Model Context Protocol (MCP) server built with TypeScript, using HTTP transport and API key authentication. It integrates directly with the GitHub REST API to expose powerful repository management capabilities as MCP tools that Claude and other AI assistants can invoke.

The server is designed for engineering teams who want to give their AI assistants real access to GitHub workflows โ€” searching repositories, managing issues, reviewing pull requests, and inspecting commit history โ€” all through a secure, observable, and deployable service. It follows the MCP specification precisely while layering in production concerns like structured logging, health monitoring, and container-based deployment.

Whether you are building an internal developer assistant, automating triage workflows, or exploring AI-native DevOps tooling, this template gives you a solid, extensible foundation. Every component โ€” authentication middleware, structured logging with Pino, health checks, and Docker configuration โ€” is wired together and ready to deploy.

What You'll Learn

  • How to implement an MCP server using the @modelcontextprotocol/sdk with HTTP/SSE transport
  • How to register and validate MCP tools using Zod schemas with full TypeScript inference
  • How to implement API key authentication as Express middleware protecting all MCP endpoints
  • How to integrate with the GitHub REST API using Octokit with proper pagination and error handling
  • How to set up Pino for structured JSON logging with request correlation IDs and configurable log levels
  • How to build a /health endpoint that checks each external dependency and returns structured JSON status
  • How to write a multi-stage Dockerfile that produces a minimal, secure production image
  • How to configure docker-compose for local development with live environment variable injection
  • How to handle and surface GitHub API errors (rate limits, permissions, not-found) as structured MCP errors
  • How to load and validate all configuration from environment variables with typed defaults
  • How to structure a TypeScript MCP project for maintainability and extensibility
  • How to test MCP tools locally using curl and via Claude Desktop configuration

Architecture

Claude Desktop / MCP Client
        โ”‚
        โ”‚  HTTP POST /mcp  (Bearer API Key)
        โ–ผ
   Express Server
        โ”‚
        โ”œโ”€โ”€ API Key Auth Middleware  (src/auth.ts)
        โ”‚         โ”‚
        โ”‚    [401 if invalid]
        โ”‚
        โ”œโ”€โ”€ Correlation ID Middleware  (src/logger.ts)
        โ”‚
        โ”œโ”€โ”€ MCP HTTP Transport  (src/server.ts)
        โ”‚         โ”‚
        โ”‚    MCP Protocol Layer (@modelcontextprotocol/sdk)
        โ”‚         โ”‚
        โ”‚    Tool Router
        โ”‚    โ”œโ”€โ”€ github_search_repos
        โ”‚    โ”œโ”€โ”€ github_list_issues
        โ”‚    โ”œโ”€โ”€ github_get_pull_request
        โ”‚    โ””โ”€โ”€ github_list_commits
        โ”‚         โ”‚
        โ”‚    Octokit GitHub Client
        โ”‚         โ”‚
        โ”‚         โ–ผ
        โ”‚    GitHub REST API (api.github.com)
        โ”‚
        โ””โ”€โ”€ GET /health  (src/health.ts)
                  โ”‚
             Checks: GitHub API reachability
             Returns: { status, version, checks, uptime }

Project Structure

github-mcp-server/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ server.ts          # Main MCP server, tool registration, Express app
โ”‚   โ”œโ”€โ”€ config.ts          # Environment variable loading and validation
โ”‚   โ”œโ”€โ”€ auth.ts            # API key authentication middleware
โ”‚   โ”œโ”€โ”€ logger.ts          # Pino structured logger with correlation IDs
โ”‚   โ””โ”€โ”€ health.ts          # /health endpoint with dependency checks
โ”œโ”€โ”€ Dockerfile             # Multi-stage production Docker build
โ”œโ”€โ”€ docker-compose.yml     # Local development with env file support
โ”œโ”€โ”€ package.json           # Dependencies with pinned versions
โ”œโ”€โ”€ tsconfig.json          # TypeScript compiler configuration
โ”œโ”€โ”€ .env.example           # All environment variables documented
โ””โ”€โ”€ .gitignore             # TypeScript/Node.js gitignore

Prerequisites

  • Node.js 20 or later
  • npm 9 or later
  • A GitHub Personal Access Token (classic or fine-grained) with repo and read:org scopes
  • Docker and Docker Compose (for containerised development/deployment)
  • A Claude Desktop installation (for end-to-end testing)

Quick Start

# 1. Clone or download the template
git clone https://github.com/your-org/github-mcp-server.git
cd github-mcp-server

# 2. Install dependencies
npm install

# 3. Configure environment
cp .env.example .env
# Edit .env โ€” set GITHUB_TOKEN and MCP_API_KEY at minimum

# 4. Build TypeScript
npm run build

# 5. Start the server
npm start
# Server is now running on http://localhost:3000

# 6. Verify health
curl http://localhost:3000/health

# 7. Test a tool call (replace YOUR_API_KEY)
curl -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# --- OR run with Docker Compose ---
docker compose up --build

Features

  • 4 GitHub MCP tools: search repositories, list issues, inspect pull requests, view commit history
  • API key authentication: every MCP request validated via Bearer token middleware
  • Pino structured logging: JSON output, configurable log level, per-request correlation IDs
  • Health endpoint: /health checks GitHub API reachability and returns structured JSON
  • Multi-stage Docker build: build stage compiles TypeScript, production stage runs minimal Node.js Alpine image
  • Docker Compose: one-command local development with .env file injection
  • Zod input validation: all tool inputs validated with descriptive error messages before calling GitHub
  • GitHub error handling: rate limits, 404s, permission errors surfaced as structured MCP errors
  • Graceful shutdown: SIGTERM/SIGINT handlers close connections cleanly
  • Typed configuration: all environment variables loaded and typed via src/config.ts

Works With

  • Claude Desktop โ€” add the server to claude_desktop_config.json using the url transport
  • Claude Code โ€” use as a remote MCP server with --mcp-server flag
  • Cursor โ€” configure in Cursor MCP settings with the HTTP endpoint
  • Windsurf โ€” add as an HTTP MCP server in Windsurf plugin configuration
  • Continue โ€” register as an MCP server in config.json under mcpServers

Configuration

VariableDescriptionRequired
PORTHTTP port the server listens onOptional (default: 3000)
HOSTHost/interface to bind toOptional (default: 0.0.0.0)
MCP_API_KEYSecret API key clients must send as Bearer tokenRequired
GITHUB_TOKENGitHub Personal Access Token (classic or fine-grained)Required
GITHUB_DEFAULT_ORGDefault GitHub org used when org is not provided in tool callOptional
LOG_LEVELPino log level: trace, debug, info, warn, errorOptional (default: info)
NODE_ENVRuntime environment: development or productionOptional (default: production)
GITHUB_API_TIMEOUT_MSTimeout in ms for GitHub API requestsOptional (default: 10000)

Deployment

Docker (standalone)

# Build the production image
docker build -t github-mcp-server:latest .

# Run with environment variables
docker run -d \
  --name github-mcp-server \
  -p 3000:3000 \
  -e MCP_API_KEY=your_secret_key \
  -e GITHUB_TOKEN=ghp_your_token \
  -e LOG_LEVEL=info \
  github-mcp-server:latest

# Check logs
docker logs -f github-mcp-server

Docker Compose (local development)

# Start with hot-reload friendly config
docker compose up --build

# Stop
docker compose down

Railway

# Install Railway CLI
npm install -g @railway/cli

# Login and initialise
railway login
railway init

# Set environment variables
railway variables set MCP_API_KEY=your_secret_key
railway variables set GITHUB_TOKEN=ghp_your_token
railway variables set LOG_LEVEL=info

# Deploy
railway up

Fly.io

# Install flyctl and login
curl -L https://fly.io/install.sh | sh
fly auth login

# Launch app (follow prompts, choose the Dockerfile option)
fly launch --name github-mcp-server

# Set secrets
fly secrets set MCP_API_KEY=your_secret_key
fly secrets set GITHUB_TOKEN=ghp_your_token

# Deploy
fly deploy

# Check status
fly status
fly logs

Production Checklist

  • MCP_API_KEY is a cryptographically random string (minimum 32 characters)
  • GITHUB_TOKEN uses a fine-grained PAT scoped to only the required repositories
  • NODE_ENV is set to production in all deployment environments
  • LOG_LEVEL is set to info or warn in production (not debug or trace)
  • Health endpoint /health is monitored by an uptime service (e.g. UptimeRobot, Better Uptime)
  • Docker image is built from the multi-stage Dockerfile (not the dev stage)
  • Container runs as a non-root user (configured in Dockerfile)
  • API key is stored in a secrets manager (Railway secrets, Fly secrets, AWS Secrets Manager)
  • GitHub token is rotated on a schedule and has minimal required scopes
  • HTTPS/TLS is terminated at the load balancer or reverse proxy in front of this service
  • Rate limiting is configured at the ingress level to protect against abuse
  • Structured logs are being shipped to a log aggregation platform (Datadog, Loki, CloudWatch)
  • /health endpoint is NOT protected by API key auth (so monitoring tools can access it)
  • Graceful shutdown is tested โ€” server drains in-flight requests before exiting
  • Docker image is scanned for vulnerabilities before deployment (e.g. docker scout, Trivy)

Testing

Manual testing with curl

# List all available tools
curl -s -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | jq

# Search GitHub repositories
curl -s -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "github_search_repos",
      "arguments": { "query": "mcp server language:typescript", "per_page": 5 }
    }
  }' | jq

# List open issues for a repository
curl -s -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "github_list_issues",
      "arguments": { "owner": "microsoft", "repo": "vscode", "state": "open", "per_page": 10 }
    }
  }' | jq

# Verify authentication is enforced (should return 401)
curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:3000/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Testing with Claude Desktop

Add the following to your claude_desktop_config.json (found at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "github-mcp": {
      "url": "http://localhost:3000/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Restart Claude Desktop. You can then ask Claude: "Search GitHub for TypeScript MCP server examples" or "List the open issues in microsoft/vscode" and Claude will invoke the tools automatically.