Running a Python MCP server inside .NET Aspire

.NET Aspire isn’t limited to .NET microservices — you can orchestrate Python components just as easily.
In this post, we’ll look at how to run a Python MCP (Model Context Protocol) server inside an Aspire application, integrate it with the MCP Inspector, and expose it via a Dev Tunnel for testing.

Folder Layout

05_PythonMCP/
├─ AppHost.cs
├─ PythonMCP.csproj
├─ appsettings.json
├─ appsettings.Development.json
├─ NuGet.config
├─ README.md
└─ pymcp/
   ├─ main.py
   ├─ requirements.txt
   └─ (optional) .venv/

AppHost.cs

Here’s the full orchestration file:

var builder = DistributedApplication.CreateBuilder(args);

var inspector = builder.AddMcpInspector("mcp-inspector");

var tunnel = builder.AddDevTunnel("dev-tunnel");

var pyMcpServer = builder.AddPythonApp(
    name: "pymcp",
    appDirectory: "./pymcp",
    scriptPath: "main.py"
).WithHttpEndpoint(env: "PORT");

tunnel.WithReference(pyMcpServer, allowAnonymous: true);

inspector.WithMcpServer(pyMcpServer);

builder. Build().Run();

What this does

  • AddMcpInspector("mcp-inspector")
    Adds the MCP Inspector — a visual client to inspect and test MCP servers.
  • AddDevTunnel("dev-tunnel")
    Creates a local tunnel to expose the Python MCP endpoint externally.
  • AddPythonApp("pymcp", "./pymcp", "main.py")
    Registers your Python app as an Aspire component.
    It will execute python main.py in the pymcp folder.
  • .WithHttpEndpoint(env: "PORT")
    Instructs Aspire to provide a dynamic HTTP port to the app via the PORT environment variable.
  • tunnel.WithReference(pyMcpServer, allowAnonymous: true)
    Allows access to the MCP server through the Dev Tunnel.
  • inspector.WithMcpServer(pyMcpServer)
    Connects the MCP Inspector to your running Python MCP app.

Python MCP Server

pymcp/main.py

import os
from mcp.server.fastmcp import FastMCP

PORT = int(os.environ.get("PORT", "8000"))

mcp = FastMCP(
    name="PyMCP",
    host="0.0.0.0",
    port=PORT,
    stateless_http=True,          # optional
    # streamable_http_path="/"    # optional: expose at "/" instead of "/mcp"
)

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@mcp.resource("status://ping")
def ping() -> str:
    return "ok"

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Key concepts

  • Tools → functions that can be invoked by clients or AI agents.
    Example: add(a, b) above.
  • Resources → data endpoints or read-only assets.
    Example: "status://ping" returns "ok".
  • Prompts → (optional) reusable instructions or templates for AI agents.

With FastMCP, everything is automatically exposed over HTTP and ready to integrate with MCP clients like the Inspector or external LLM agents.

Setting up the Python environment

From inside the pymcp directory:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
deactivate

This ensures the FastMCP runtime is available locally.
When Aspire launches the app, it will use the default Python interpreter in your system (or one defined in the environment).

Run the whole setup

From the root of the project (05_PythonMCP):

dotnet run

Aspire will:

  1. Start the MCP Inspector.
  2. Start the Dev Tunnel.
  3. Launch your Python MCP app.

Open the Aspire dashboard — you’ll see all three components:

  • pymcp (the FastMCP server)
  • mcp-inspector
  • dev-tunnel

Click on the Inspector to explore your tools and resources interactively.

How It Works Together

  1. Aspire orchestrates everything — all services share the same lifecycle.
  2. The Python app runs as a first-class citizen under Aspire.
  3. The Inspector automatically connects to your Python MCP endpoint via the tunnel.
  4. You can test, monitor, and extend everything without switching environments.

Extending the Example

Want to go further?

  • Add new @mcp.tool() functions for your own logic (e.g. database access, file readers, or AI calls).
  • Define a @mcp.prompt() to register reusable AI instructions.
  • Connect the MCP Inspector to other MCP servers or clients.
  • Replace the Dev Tunnel with a YARP reverse proxy or a public HTTPS endpoint.

This small sample shows that Aspire can orchestrate Python MCP servers just like any .NET component.
You get unified logs, health checks, dashboards, and service wiring — with Python running seamlessly next to your C# code.

[Source code]

That’s all folks!

Cheers!
Gašper Rupnik

{End.}

Leave a comment

Website Powered by WordPress.com.

Up ↑