18 KiB

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)

{
  "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

{
  "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

# 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

# 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)

# 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
# Run with docker-compose
docker-compose up -d

4. Railway Deployment

railway.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

# 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)

[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

{
  "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 <your.email@example.com>",
  "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

# 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

# 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

# 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

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

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

# 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:

## 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


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.