← Back to all posts
AI3 min read

Developing and Publishing an MCP Service

Set up a Python environment, develop and test an MCP service locally, and build and publish the package to PyPI.

#ChatGPT model
Read in Chinese→

Developing and Publishing an MCP Service

Environment

The MCP service development environment requires Python 3.10 or later.

I recommend using uv to manage the Python environment: https://docs.astral.sh/uv/

It makes creating a standard package structure quite convenient.

MCP debugging tool: https://github.com/modelcontextprotocol/inspector

MCP service

Official documentation: https://modelcontextprotocol.io/quickstart/server

MCP Service

The main MCP communication methods are:

  1. Stdio transport
    • Uses standard input/output for communication
    • Ideal for local processes
  2. Streamable HTTP transport
    • Uses HTTP with optional Server-Sent Events for streaming
    • HTTP POST for client-to-server messages

stdio: Transfers data through standard input and output streams. It is simple to implement, highly compatible, and suitable for local development and testing.

sse: Allows the server to push real-time updates to the client. It provides one-way communication and is suitable for scenarios requiring continuous data updates, such as real-time notifications.

Note: After reading the documentation later, I learned that SSE mode has been deprecated, but many people still use it.

Creating the Project

mcp_requests % uv init . –package -p 3.13 Initialized project mcp-requests at /Users/xxxxx/Desktop/mcp_requests

mcp_requests % uv add “mcp[cli]”

image.png

Here is some simple code that provides methods for sending HTTP GET and POST requests.

import json
from typing import Dict, Any, Optional
import httpx
from mcp.server.fastmcp import FastMCP

# Create an MCP server
mcp = FastMCP("HTTP Requests")

@mcp.tool()
def http_get(url: str, headers: Optional[Dict[str, str]] = None) -> str:
    """Make an HTTP GET request"""
    try:
        with httpx.Client() as client:
            response = client.get(url, headers=headers or {})
            return json.dumps({
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "content": response.text
            }, indent=2)
    except Exception as e:
        return f"Error: {str(e)}"

@mcp.tool()
def http_post(url: str, data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None) -> str:
    """Make an HTTP POST request"""
    try:
        with httpx.Client() as client:
            response = client.post(url, json=data, headers=headers or {})
            return json.dumps({
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "content": response.text
            }, indent=2)
    except Exception as e:
        return f"Error: {str(e)}"

@mcp.tool()
def http_put(url: str, data: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None) -> str:
    """Make an HTTP PUT request"""
    try:
        with httpx.Client() as client:
            response = client.put(url, json=data, headers=headers or {})
            return json.dumps({
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "content": response.text
            }, indent=2)
    except Exception as e:
        return f"Error: {str(e)}"

@mcp.tool()
def http_delete(url: str, headers: Optional[Dict[str, str]] = None) -> str:
    """Make an HTTP DELETE request"""
    try:
        with httpx.Client() as client:
            response = client.delete(url, headers=headers or {})
            return json.dumps({
                "status_code": response.status_code,
                "headers": dict(response.headers),
                "content": response.text
            }, indent=2)
    except Exception as e:
        return f"Error: {str(e)}"

def main() -> None:
    import asyncio
    asyncio.run(mcp.run(transport='stdio'))

if __name__ == "__main__":
    main()

Try invoking it locally for testing, then add the service in Cursor (other tools will also work; I just happened to have it installed, so it was more convenient).

{
  "mcpServers": {
    "request-server-aaddb": {
      "name": "request-mcp-server-aaddb",
      "type": "stdio",
      "description": "AADDb MCP server",
      "isActive": true,
      "command": "uv",
      "args": ["--directory",
"/Users/yi.zhai/Desktop/mcp_requests", "run", "mcp-requests"]
    }
  }
}

Started successfully.

image.png

Try having the AI invoke it.

image.png

The service successfully received the request. The test passed, and the functionality worked end to end.

image.png

Summary of the Steps for Uploading to PyPI

Install the Build Tools

uv add build twine

Build

uv run python -m build

Upload

uv run python -m twine upload dist/* -u **token** -p pypi-xxxxxx

Success indicator:

image.png

Finally, I tried importing and using it directly, and it worked without any issues.

{
  "mcpServers": {
    "mcp-request": {
      "command": "mcp-request",
      "type": "stdio"
    }
  }
}

image.png

The documentation service was ultimately published at:

https://pypi.org/project/mcp-request/ Feel free to try it out.