Skip to content
← Back to Blog

Deploying the MCP Research Assistant: Docker, CI/CD, and Client Integrations

2026-09-18

Deploying the MCP Research Assistant

We have a working MCP server and client. Now let's package it, deploy it, and connect it to AI clients.

Previous: Building the MCP Research Server ←

Step 1: Containerize with Docker

Development Container

For local development with live reloading:

# docker/Dockerfile.dev
FROM python:3.12-slim

WORKDIR /app

RUN pip install uv

COPY pyproject.toml uv.lock ./
RUN uv pip install --system .

COPY src/ ./src/

EXPOSE 8080
ENTRYPOINT ["uv", "run", "src/research_server.py"]

Production Container

Multi-stage build for smaller, secure images:

# docker/Dockerfile.prod
FROM python:3.12-slim AS builder

WORKDIR /app

# Copy uv from official image
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

# Install dependencies into virtual environment
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project

FROM python:3.12-slim AS runner

WORKDIR /app

# Security settings
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/app/.venv/bin:$PATH" \
    PORT=8080

# Create non-root user for security
RUN groupadd -r appgroup && useradd -r -g appgroup -u 10001 appuser

# Copy virtual environment from builder
COPY --from=builder --chown=appuser:appgroup /app/.venv /app/.venv

# Copy application source code
COPY --chown=appuser:appgroup src/ ./src/

# Create data directory
RUN mkdir -p /app/papers && chown -R appuser:appgroup /app

# Switch to non-root user
USER appuser

EXPOSE 8080
ENTRYPOINT ["python", "src/research_server.py"]

Docker Compose for Development

# docker/docker-compose.yml
services:
  research-mcp:
    container_name: research-mcp
    build:
      context: ..
      dockerfile: docker/Dockerfile.dev
    ports:
      - "8080:8080"
    volumes:
      - ../papers:/app/papers
      - ../src:/app/src
    restart: unless-stopped

Run it:

docker compose -f docker/docker-compose.yml up -d

Step 2: CI/CD with GitHub Actions

The project uses two workflows:

Main Orchestrator

# .github/workflows/main.yml
name: CI/CD Orchestrator

on:
  push:
    branches: [main]
    tags: ['v*.*.*']
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  changes:
    name: Detect Changed Projects
    runs-on: ubuntu-latest
    outputs:
      learn_mcp: ${{ steps.filter.outputs.learn_mcp }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            learn_mcp:
              - 'learn_mcp/**'
              - '.github/workflows/build-push-research-mcp.yml'

  build-research-mcp:
    name: Research MCP Server
    needs: changes
    if: |
      needs.changes.outputs.learn_mcp == 'true' ||
      startsWith(github.ref, 'refs/tags/v') ||
      github.event_name == 'workflow_dispatch'
    uses: ./.github/workflows/build-push-research-mcp.yml

Build and Push Workflow

# .github/workflows/build-push-research-mcp.yml
name: Build and Push Research MCP Docker Image to GHCR

on:
  workflow_call:
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}/research-mcp

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-qemu-action@v3
      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        if: github.event_name != 'pull_request'
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=raw,value=latest,enable={{is_default_branch}}
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha,format=short

      - uses: docker/build-push-action@v6
        with:
          context: ./learn_mcp
          file: ./learn_mcp/docker/Dockerfile.prod
          platforms: linux/amd64,linux/arm64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Key features:

Generated tags:

Step 3: Deploy to Render.com

  1. Create a new Web Service on Render
  2. Connect your GitHub repository
  3. Configure the service:
    • Name: research-mcp
    • Region: Choose closest to users
    • Branch: main
    • Root Directory: learn_mcp
    • Dockerfile Path: docker/Dockerfile.prod
  4. Set Auto-Deploy: Enable
  5. Add environment variables if needed

Important configuration:

Live endpoint: https://research-mcp-sha-a64029e.onrender.com/mcp

Note: Free tier services sleep after inactivity. First request may take 30-60 seconds to wake up.

Step 4: Connect to AI Clients

Now for the fun part - connecting to various AI clients!

MCP Inspector

Web-based tool for exploring MCP servers:

npx @modelcontextprotocol/inspector

Connect to:

Features:

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "research-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://research-mcp-sha-a64029e.onrender.com/mcp"
      ]
    }
  }
}

Note: mcp-remote bridges HTTP-based MCP servers to Claude Desktop's stdio expectation.

Restart Claude Desktop after saving. You'll see the 🔨 tools icon with search_papers and extract_info ready to use!

Cursor

Add to .cursor/mcp.json:

{
  "mcpServers": {
    "research-mcp": {
      "url": "https://research-mcp-sha-a64029e.onrender.com/mcp"
    }
  }
}

VS Code Extensions

For Cline or Roo Code, use similar HTTP configuration:

{
  "mcpServers": {
    "research-mcp": {
      "url": "https://research-mcp-sha-a64029e.onrender.com/mcp"
    }
  }
}

ChatGPT / LibreChat / OpenWebUI / Dify

Configure with:

mcp_servers:
  research:
    url: https://research-mcp-sha-a64029e.onrender.com/mcp
    transport: http

Step 5: Test with Different LLM Providers

The chatbot supports Ollama, Claude, and Gemini.

Edit .env:

# Choose provider
LLM_PROVIDER=ollama        # or anthropic, google_genai
LLM_MODEL=llama3.2         # or claude-3-7-sonnet, gemini-2.0-flash

# Provider-specific keys (uncomment as needed)
# ANTHROPIC_API_KEY=your_key_here
# GOOGLE_API_KEY=your_key_here
# OLLAMA_BASE_URL=http://localhost:11434

Ollama (Local)

# Install
curl -fsSL https://ollama.com/install.sh | sh

# Pull a model
ollama pull llama3.2

# Configure
LLM_PROVIDER=ollama
LLM_MODEL=llama3.2

Pros: Free, local, private, no API keys needed

Anthropic Claude

Get API key from Anthropic Console

LLM_PROVIDER=anthropic
LLM_MODEL=claude-3-7-sonnet-20250219
ANTHROPIC_API_KEY=your_key_here

Pros: State-of-the-art reasoning, reliable, production-ready

Google Gemini

Get API key from Google Cloud Console

LLM_PROVIDER=google_genai
LLM_MODEL=gemini-2.0-flash
GOOGLE_API_KEY=your_key_here

Pros: Fast, capable, competitive pricing

Quick Start

# Clone and setup
git clone https://github.com/nosadaniel/learn_ai.git
cd learn_ai/learn_mcp
uv sync
cp .env.example .env

# Terminal 1: Start server
uv run src/research_server.py

# Terminal 2: Start chatbot
uv run src/mcp_chatbot.py

# Ask a question:
# "Find papers on machine learning"
# "What are recent trends in AI security?"

Or connect directly to the live server:

npx @modelcontextprotocol/inspector
# Connect to: https://research-mcp-sha-a64029e.onrender.com/mcp

Project Links

Key Takeaways

  1. MCP decouples AI from tools - Build once, use everywhere
  2. Standardization enables reuse - Same server works with multiple clients
  3. Full workflow demonstrated - Server → Container → CI/CD → Deployment → Integration
  4. Multiple LLM options - Test locally with Ollama or use cloud providers
  5. Production ready - Docker, CI/CD, and deployment all set up

The research assistant shows that you don't need to be an AI expert to build powerful AI tools. With MCP, you can focus on what you want to accomplish, not the integration plumbing.


Series Complete!

  1. Understanding MCP: Tools, Prompts, and Resources
  2. Building the MCP Research Server
  3. Deploying the MCP Research Assistant ← You are here