Skip to main content
Articles/ai agents/expose-a-skill-as-mcp-server-step-by-step
Series: Model Context Protocol (MCP)β€’Part 1 of 3
Scenario Article

Step-by-Step: How to Expose a Skill as an MCP Server

The Copy-Paste Problem: Why Skills Die at IDE Boundaries

Abstract Algorithmsβ€’Mar 28, 2026
XLinkedInReddit
πŸ’‘ Executive Summary

Turn any Python function into a multi-client MCP server in 11 steps β€” from annotation to Docker.

🎯 System Scenario & Requirements

System Goal

Turn any Python function into a multi-client MCP server in 11 steps β€” from annotation to Docker.

Actors & Components

  • Application or API tier
  • Core service under design
  • Operator or SRE observer

Functional Requirements

  • TLDR: Turn any Python function into a multi-client MCP server in 11 steps β€” from annotation to Docker.
  • A developer pastes their summarize pr diff function into a Slack message because their teammate uses Cursor and can't call a Copilot skill.
  • The sharing mechanism is broken.

Constraints & Assumptions

  • Every cross-client MCP deployment surfaces failure modes that do not appear in local testing.
  • The table below covers the six most common, with the exact symptom you will see in each client, the underlying cause, and the fix.

⚑ Execution Stages & Decision Trace

1

Stage 1 β€” Before You Start: What You Need and How MCP Registration Works

Action / Decision:

Before running a single command, it helps to understand what the end state looks like so each step has a clear purpose.

Component Behavior:

What you need installed: - Python 3.11+ with pip - Docker Desktop (for Step 7) - Claude Desktop, Cursor, or VS Code with GitHub Copilot (at least one to test registration) - curl for HTTP transport testing What "registe…

Observable Output: When you open the client, it reads this config, attempts to start or connect to every listed server, and calls tools/list to discover available tools.
Read Full Stage in Deep Dive ↓
πŸ“–

Deep Dive

Detailed Architectural Analysis

Scenario context

Turn any Python function into a multi-client MCP server in 11 steps β€” from annotation to Docker.


A developer pastes their summarize_pr_diff function into a Slack message because their teammate uses Cursor and can't call a Copilot skill. The function works perfectly. The sharing mechanism is broken. By the end of this post, that same function runs as an MCP server β€” callable from Cursor, Claude Desktop, and V…

This walkthrough focuses on The Copy-Paste Problem: Why Skills Die at IDE Boundaries.

Before making a design choice, clarify:

  • A developer pastes their summarize pr diff function into a Slack message because their teammate uses Cursor and can't call a Copilot skill.
  • The sharing mechanism is broken.
  • By the end of this post, that same function runs as an MCP server β€” callable from Cursor, Claude Desktop, and VS Code Copilot simultaneously, with no copy-paste required.

Concepts in play: llm-agents-and-langgraph, system-design-hld-architectures, jvm-internals-and-garbage-collection, low-level-design-systems

Stage 1 β€” Before You Start: What You Need and How MCP Registration Works

  • Before running a single command, it helps to understand what the end state looks like so each step has a clear purpose.
  • What you need installed: - Python 3.11+ with pip - Docker Desktop (for Step 7) - Claude Desktop, Cursor, or VS Code with GitHub Copilot (at least one to test registration) - curl for HTTP transport testing What "registe…
  • When you open the client, it reads this config, attempts to start or connect to every listed server, and calls tools/list to discover available tools.
  • If your server is running and returns a valid schema, the tools appear in the client's tool picker within seconds.

Observable outcome: If your server is running and returns a valid schema, the tools appear in the client's tool picker within seconds.

Stage 2 β€” Steps 1–4: From Python Function to Locally Tested MCP Server

  • These four steps take you from an empty directory to a fully verified local MCP server.
  • Every subsequent step builds on this foundation.
  • Step 1 β€” Set Up the Project Structure Create the directory layout and install dependencies: Your directory should look like this: The pyproject.toml declares the package and its runtime dependencies: FastMCP vs.
  • bare mcp.server : FastMCP is a thin decorator layer over the official MCP Python SDK.
mkdir mcp-pr-summarizer && cd mcp-pr-summarizer
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install "mcp>=1.0" fastmcp openai
mcp-pr-summarizer/
β”œβ”€β”€ server.py
β”œβ”€β”€ pyproject.toml
└── Dockerfile

Observable outcome: bare mcp.server : FastMCP is a thin decorator layer over the official MCP Python SDK.

Stage 3 β€” The Registration Journey: From Local Dev to Three Live Clients

  • The diagram below shows the complete path from a working Python function to a tool registered across all three clients.
  • Each arrow represents a concrete step in this post.
  • The decision diamond at the centre is the key branch: stdio registration skips the Docker steps entirely and goes straight to client config files.
  • SSE registration requires the running server (Steps 5–7) before the config files can point anywhere useful.
flowchart TD
    A["✏ Write tool function (Steps 1–2)"] --> B["οΏ½ Add error handling (Step 3)"]
    B --> C["οΏ½ Test with MCP Inspector (Step 4)"]
    C --> D{Which transport?}
    D -->|"Local / single dev"| E["οΏ½ Register via stdio (Step 8, 9, 10)"]
    D -->|"Shared / remote"| F["οΏ½ Switch to HTTP+SSE (Step 5)"]
    F --> G[" Add bearer token auth (Step 6)"]
    G --> H["οΏ½ Package as Docker container (Step 7)"]
    H --> I[" docker run -p 8080: 8080"]
    I --> J["οΏ½ Register via SSE URL (Step 8, 9, 10)"]
    E --> K[" Claude Desktop sees tool"]
    J --> K
    K --> L[" Cursor sees tool"]
    L --> M[" VS Code Copilot sees tool"]
    M --> N["οΏ½ Debug failures (Step 11)"]

Observable outcome: SSE registration requires the running server (Steps 5–7) before the config files can point anywhere useful.

Stage 4 β€” Real-World Applications: How Teams Are Sharing Skills Across IDEs Today

  • The platform-agnostic code review assistant.
  • A mid-size engineering team has developers split across Cursor, Claude Desktop, and VS Code.
  • They built a single MCP server that wraps three tools: summarize pr diff (this post's example), lint findings summary (summarizes ESLint/Flake8 output), and test coverage report (describes coverage gaps in plain English…
  • The server runs as a Railway-hosted Docker container.

Observable outcome: The server runs as a Railway-hosted Docker container.

Stage 5 β€” MCP Inspector: The Debugging Tool You'll Use Every Day

  • The MCP Inspector ( mcp dev ) is the single most useful tool in the MCP development workflow.
  • It is open-source, ships with the mcp[cli] package, and runs entirely locally β€” no cloud account required.
  • What the Inspector UI shows: - Tools tab: Every tool your server advertises via list tools , with the full JSON Schema rendered as a human-readable form.
  • If a tool is missing here, the client will never see it.

Observable outcome: If a tool is missing here, the client will never see it.

Stage 6 β€” Practice Quiz

  • Test your understanding of the MCP server deployment workflow.
  • You run mcp dev server.py and your tool appears in the Inspector's Tools tab.
  • You then add it to Claude Desktop's config file, fully restart the app, and the tool does not appear.
  • What is the most likely cause?

Observable outcome: What is the most likely cause?

End-to-end outcome

After working through the stages, you should be able to explain:

  • After walking through eleven steps across three clients, here are the non-obvious lessons that save the most debugging time: 1.
  • Write the docstring before the implementation.

Alternatives considered

Decision Guide: stdio for Local, SSE for Shared, Container for Team

The simplest production pattern for a small team: one Railway or Fly.io container running SSE on port 8080, bearer token authentication via environment variable, and a single shared config snippet that each developer pastes into their client config file.

This section covers Steps 5–11: switching transports, adding auth, packaging in Docker, and writing the exact config file entries for each client.

This section covers Steps 5–11: switching transports, adding auth, packaging in Docker, and writing the exact config file entries for each client.

Step 5 β€” Switch to HTTP+SSE Transport

Replace the stdio server entrypoint with SSE transport.

Step 6 β€” Add Bearer Token Authentication

Wrap the SSE route with a simple Starlette middleware that checks the Authorization header: Set MCP AUTH TOKEN in your environment before starting the server:

Step 7 β€” Package as a Docker Container

Use a multi-stage build to keep the image small.

Failure and degraded modes

Trade-offs and Failure Modes: What Breaks When You Register Across Clients

  • Detection: Every cross-client MCP deployment surfaces failure modes that do not appear in local testing.
  • User or operator impact: Every cross-client MCP deployment surfaces failure modes that do not appear in local testing.
First article in seriesNext Article β†’

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit