Step-by-Step: How to Expose a Skill as an MCP Server
The Copy-Paste Problem: Why Skills Die at IDE Boundaries
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
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β¦
Deep Dive
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.
