# MCP Deployment & Distribution **When to use this skill:** Packaging and distributing MCP servers. Use when preparing servers for production, Docker containers, Railway deployment, or GitHub publishing. **What this covers:** Deployment patterns from 30+ production MCP servers including Docker, Railway, npm publishing, and GitHub repository setup. --- ## 1. Deployment Overview ### Common Deployment Targets 1. **Local (Claude Desktop)** — Development + personal use 2. **Docker Container** — Portable, isolated environment 3. **Railway.app** — Hosted deployment (for web-accessible MCPs) 4. **npm Registry** — Public distribution 5. **GitHub** — Source code + documentation --- ## 2. Local Deployment (Claude Desktop) ### Standard Configuration **Location:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) ```json { "mcpServers": { "myservice": { "command": "node", "args": [ "/absolute/path/to/mcp-server-myservice/dist/index.js" ], "env": { "MY_SERVICE_API_KEY": "your_api_key_here", "MY_SERVICE_API_SECRET": "your_secret_here" } } } } ``` **Key points:** - Use absolute paths for `args` - Environment variables in `env` object - Server name (`myservice`) appears in Claude Desktop - Restart Claude Desktop after config changes ### Alternative: npx Installation ```json { "mcpServers": { "myservice": { "command": "npx", "args": ["-y", "mcp-server-myservice"], "env": { "MY_SERVICE_API_KEY": "your_api_key_here" } } } } ``` **Requires:** - Package published to npm - `bin` field in package.json pointing to executable --- ## 3. Docker Containerization ### Dockerfile Template ```dockerfile # Multi-stage build for smaller final image FROM node:20-alpine AS builder # Set working directory WORKDIR /app # Copy package files COPY package*.json ./ # Install dependencies RUN npm ci # Copy source code COPY . . # Build TypeScript RUN npm run build # Production stage FROM node:20-alpine WORKDIR /app # Copy package files COPY package*.json ./ # Install production dependencies only RUN npm ci --production # Copy built files from builder COPY --from=builder /app/dist ./dist # Copy UI files (if using MCP Apps) COPY --from=builder /app/dist/app-ui ./dist/app-ui # Expose port (if using HTTP transport) # EXPOSE 3000 # Set environment variable defaults ENV NODE_ENV=production # Run the MCP server CMD ["node", "dist/index.js"] ``` **Key features:** - Multi-stage build → Smaller final image - `npm ci` → Faster, more reliable than `npm install` - `--production` → Excludes devDependencies - `node:20-alpine` → Lightweight base image ### .dockerignore ``` node_modules dist .env .git .gitignore *.md npm-debug.log ``` **Why:** Prevents unnecessary files from being copied into image ### Build & Run ```bash # Build image docker build -t mcp-server-myservice . # Run container docker run -it --rm \ -e MY_SERVICE_API_KEY=your_key \ -e MY_SERVICE_API_SECRET=your_secret \ mcp-server-myservice # Run with env file docker run -it --rm \ --env-file .env \ mcp-server-myservice ``` ### Docker Compose (Optional) ```yaml # docker-compose.yml version: '3.8' services: mcp-server: build: . environment: - MY_SERVICE_API_KEY=${MY_SERVICE_API_KEY} - MY_SERVICE_API_SECRET=${MY_SERVICE_API_SECRET} restart: unless-stopped ``` ```bash # Run with docker-compose docker-compose up -d ``` --- ## 4. Railway Deployment ### railway.json ```json { "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "NIXPACKS", "buildCommand": "npm run build" }, "deploy": { "startCommand": "node dist/index.js", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 } } ``` **Key fields:** - `buildCommand` → Compile TypeScript - `startCommand` → Run compiled server - `restartPolicyType` → Auto-restart on failure ### Environment Variables **In Railway Dashboard:** 1. Go to project → Variables 2. Add all required environment variables: - `MY_SERVICE_API_KEY` - `MY_SERVICE_API_SECRET` - `NODE_ENV=production` ### Deployment Commands ```bash # Install Railway CLI npm install -g @railway/cli # Login railway login # Link to project railway link # Deploy railway up # View logs railway logs ``` ### railway.toml (Alternative) ```toml [build] builder = "NIXPACKS" buildCommand = "npm ci && npm run build" [deploy] startCommand = "node dist/index.js" restartPolicyType = "ON_FAILURE" restartPolicyMaxRetries = 10 [env] NODE_ENV = "production" ``` --- ## 5. npm Publishing ### package.json Configuration ```json { "name": "mcp-server-myservice", "version": "1.0.0", "description": "MCP server for MyService integration", "type": "module", "main": "dist/index.js", "bin": { "mcp-server-myservice": "dist/index.js" }, "files": [ "dist", "README.md", "LICENSE" ], "keywords": [ "mcp", "mcp-server", "model-context-protocol", "myservice", "claude-desktop" ], "author": "Your Name ", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/yourusername/mcp-server-myservice.git" }, "bugs": { "url": "https://github.com/yourusername/mcp-server-myservice/issues" }, "homepage": "https://github.com/yourusername/mcp-server-myservice#readme" } ``` **Key fields:** - `bin` → Makes package executable via `npx` - `files` → Only include necessary files in package - `keywords` → Helps with npm search - `repository` → Links to GitHub ### .npmignore ``` src *.ts tsconfig.json .env .env.example node_modules .git .DS_Store ``` **Why:** Prevents source files from being published (only `dist/` is needed) ### Publishing Workflow ```bash # 1. Ensure you're logged in to npm npm login # 2. Build the project npm run build # 3. Test locally before publishing npm pack # This creates a .tgz file - inspect it to verify contents # 4. Publish to npm npm publish # For scoped packages (e.g., @yourorg/mcp-server-myservice) npm publish --access public ``` ### Versioning ```bash # Patch release (1.0.0 -> 1.0.1) npm version patch # Minor release (1.0.0 -> 1.1.0) npm version minor # Major release (1.0.0 -> 2.0.0) npm version major # Then publish npm publish ``` --- ## 6. GitHub Repository Setup ### File Structure ``` mcp-server-myservice/ ├── .github/ │ └── workflows/ │ ├── build.yml # CI/CD │ └── publish.yml # npm publish automation ├── src/ │ └── index.ts ├── dist/ # gitignored ├── .env.example # Template for env vars ├── .gitignore ├── .npmignore ├── Dockerfile ├── docker-compose.yml ├── railway.json ├── package.json ├── tsconfig.json ├── README.md ├── LICENSE └── CHANGELOG.md ``` ### .gitignore ``` # Dependencies node_modules/ # Build output dist/ # Environment variables .env .env.local # Logs *.log npm-debug.log* # OS files .DS_Store Thumbs.db # IDE .vscode/ .idea/ *.swp *.swo # Misc .cache/ ``` ### README.md Template ```markdown # MCP Server for MyService MCP (Model Context Protocol) server integration for MyService. Enables Claude Desktop to interact with MyService API. ## Features - ✅ List and search contacts - ✅ Get contact details - ✅ Create and update contacts - ✅ View dashboard metrics - ✅ Rich UI components (contact grid, dashboard) ## Installation ### Claude Desktop Add to your `claude_desktop_config.json`: \`\`\`json { "mcpServers": { "myservice": { "command": "npx", "args": ["-y", "mcp-server-myservice"], "env": { "MY_SERVICE_API_KEY": "your_api_key_here" } } } } \`\`\` ### Manual Installation \`\`\`bash git clone https://github.com/yourusername/mcp-server-myservice.git cd mcp-server-myservice npm install npm run build \`\`\` Add to `claude_desktop_config.json`: \`\`\`json { "mcpServers": { "myservice": { "command": "node", "args": ["/absolute/path/to/mcp-server-myservice/dist/index.js"], "env": { "MY_SERVICE_API_KEY": "your_api_key_here" } } } } \`\`\` ## Configuration ### Required Environment Variables - `MY_SERVICE_API_KEY` — Your MyService API key ([get one here](https://myservice.com/api-keys)) - `MY_SERVICE_API_SECRET` — Your MyService API secret (optional) ### Optional Environment Variables - `MY_SERVICE_BASE_URL` — Override API base URL (default: `https://api.myservice.com`) - `LOG_LEVEL` — Logging level: `debug`, `info`, `warn`, `error` (default: `info`) ## Available Tools ### Core Tools - `list_contacts` — List contacts with pagination and filters - `get_contact` — Get detailed contact information - `create_contact` — Create a new contact - `update_contact` — Update existing contact - `delete_contact` — Delete a contact ### App Tools (Rich UI) - `view_contact_grid` — Display contact search results in a data grid - `show_dashboard` — Display dashboard with metrics and KPIs ## Usage Examples ### List contacts \`\`\` Can you show me all active contacts? \`\`\` ### Search and display \`\`\` Search for contacts with "john" in their name and show me the grid \`\`\` ### Create contact \`\`\` Create a new contact: Name: Jane Smith Email: jane@example.com Phone: 555-1234 \`\`\` ## Development \`\`\`bash # Install dependencies npm install # Run in development mode npm run dev # Build for production npm run build # Run tests npm test \`\`\` ## Docker \`\`\`bash # Build image docker build -t mcp-server-myservice . # Run container docker run -it --rm \ -e MY_SERVICE_API_KEY=your_key \ mcp-server-myservice \`\`\` ## Railway Deployment 1. Fork this repository 2. Connect to Railway 3. Add environment variables in Railway dashboard 4. Deploy ## Contributing Pull requests are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details. ## License MIT License - see [LICENSE](LICENSE) file for details. ## Support - [Open an issue](https://github.com/yourusername/mcp-server-myservice/issues) - [MyService API Documentation](https://myservice.com/docs) - [MCP Documentation](https://modelcontextprotocol.io) ``` ### LICENSE (MIT Template) ``` MIT License Copyright (c) 2026 Your Name Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` --- ## 7. GitHub Actions CI/CD ### .github/workflows/build.yml ```yaml name: Build and Test on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Run tests run: npm test if: ${{ hashFiles('**/*.test.ts') != '' }} - name: Verify dist exists run: test -d dist && test -f dist/index.js ``` ### .github/workflows/publish.yml ```yaml name: Publish to npm on: release: types: [created] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v3 with: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Publish to npm run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ``` **Setup:** 1. Go to npmjs.com → Account Settings → Access Tokens 2. Create new token (Automation or Publish) 3. Add to GitHub repo → Settings → Secrets → `NPM_TOKEN` --- ## 8. Distribution Checklist Before publishing/deploying: ### Code Quality - [ ] All TypeScript compiles without errors - [ ] No console.logs in production code (use proper logging) - [ ] Error handling implemented for all tools - [ ] Environment variables validated on startup ### Documentation - [ ] README.md with installation instructions - [ ] .env.example with all required variables - [ ] Tool descriptions are clear and helpful - [ ] Examples provided in README ### Package Configuration - [ ] `package.json` has correct `name`, `version`, `description` - [ ] `files` field only includes necessary files - [ ] `keywords` added for npm search - [ ] `repository`, `bugs`, `homepage` URLs set - [ ] License file included ### Testing - [ ] Tested locally in Claude Desktop - [ ] All tools work as expected - [ ] Apps render correctly (if applicable) - [ ] Error cases handled gracefully ### Security - [ ] No API keys hardcoded - [ ] `.env` in `.gitignore` - [ ] Sensitive data not logged - [ ] Dependencies up to date (`npm audit`) ### Deployment - [ ] Dockerfile builds successfully - [ ] Docker container runs without errors - [ ] Railway deployment works (if applicable) - [ ] npm package installs and runs via `npx` --- ## 9. Version Management ### Semantic Versioning - **Patch (1.0.0 → 1.0.1):** Bug fixes, no API changes - **Minor (1.0.0 → 1.1.0):** New features, backward compatible - **Major (1.0.0 → 2.0.0):** Breaking changes ### CHANGELOG.md ```markdown # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] ### Added - New `search_contacts` tool with full-text search ### Changed - Improved error messages for API failures ### Fixed - Fixed pagination issue in `list_contacts` ## [1.1.0] - 2026-02-03 ### Added - Contact grid MCP app - Dashboard MCP app - Docker support ### Changed - Updated dependencies to latest versions ## [1.0.0] - 2026-01-15 ### Added - Initial release - Basic CRUD tools for contacts - MyService API integration ``` --- ## 10. Multi-Platform Distribution ### npm + Docker + GitHub **Best practice:** Offer multiple installation methods **README.md section:** ```markdown ## Installation Methods ### 1. npx (Easiest) \`\`\`bash # Add to claude_desktop_config.json { "mcpServers": { "myservice": { "command": "npx", "args": ["-y", "mcp-server-myservice"] } } } \`\`\` ### 2. npm Global Install \`\`\`bash npm install -g mcp-server-myservice # Then reference in claude_desktop_config.json { "mcpServers": { "myservice": { "command": "mcp-server-myservice" } } } \`\`\` ### 3. Docker \`\`\`bash docker run -it --rm \ -e MY_SERVICE_API_KEY=your_key \ ghcr.io/yourusername/mcp-server-myservice:latest \`\`\` ### 4. From Source \`\`\`bash git clone https://github.com/yourusername/mcp-server-myservice.git cd mcp-server-myservice npm install && npm run build # Reference dist/index.js in claude_desktop_config.json \`\`\` ``` --- ## 11. Common Deployment Issues ### Issue: "Cannot find module" **Cause:** Missing dependencies or incorrect path **Fix:** Run `npm ci` and use absolute paths in config ### Issue: "Environment variable not set" **Cause:** Missing env vars **Fix:** Add to `env` object in Claude Desktop config or `.env` file ### Issue: "UI files not found" **Cause:** `dist/app-ui/` not copied during build **Fix:** Add `build:ui` script to copy HTML files ### Issue: "ENOENT: no such file or directory" **Cause:** Path resolution fails in compiled code **Fix:** Use `fileURLToPath` for ESM `__dirname` equivalent ### Issue: Docker build fails **Cause:** Missing build step or dependencies **Fix:** Ensure `npm run build` runs in Dockerfile and all deps installed --- ## 12. Resources - **MCP Deployment Guide:** https://modelcontextprotocol.io/docs/deployment - **Railway Docs:** https://docs.railway.app - **npm Publishing Guide:** https://docs.npmjs.com/creating-and-publishing-scoped-public-packages - **Docker Best Practices:** https://docs.docker.com/develop/dev-best-practices --- ## Summary **Distribution workflow:** 1. Build: `npm run build` 2. Test locally in Claude Desktop 3. Create README.md with installation instructions 4. Add Dockerfile + railway.json (if deploying) 5. Publish to npm: `npm publish` 6. Push to GitHub with proper README 7. Tag releases for versioning 8. Automate with GitHub Actions **Key files:** - `package.json` → npm distribution - `Dockerfile` → Docker containerization - `railway.json` → Railway deployment - `README.md` → User documentation - `.env.example` → Configuration template - `.github/workflows/` → CI/CD automation Follow these patterns and your MCP servers will be production-ready and easy to distribute.