AI & MCP October 2026

How to Deploy a Python MCP Server in 5 Minutes (No Docker, No VPS)

Model Context Protocol (MCP) lets Claude, Cursor, and other AI agents call your Python functions as tools. Here's the fastest way to deploy a Python MCP server to the cloud without managing any infrastructure.

L
LiteLambda Team
8 min read

Model Context Protocol (MCP) is how AI agents — Claude, Cursor, Windsurf — call external tools. If you write Python, you can expose your code as a tool that an AI agent calls on demand: query a database, run a calculation, check an API, process data.

The problem: running an MCP server requires a publicly accessible HTTPS endpoint, which means a server, a domain, SSL certificates, and uptime monitoring. For most developers, this overhead is the blocker.

This guide shows you how to deploy a Python MCP server in 5 minutes with zero infrastructure to manage.


What MCP Actually Does

An AI agent like Claude has a core limitation: it can only work with text. It can't run code, query your database, or call your private APIs. MCP bridges this gap.

With MCP configured, when you say to Claude:

"Check the current inventory for product SKU-9920"

Claude doesn't guess. It:
1. Identifies you have a tool named check-inventory
2. Calls your Python function with {"sku": "SKU-9920"}
3. Gets back the real result from your database
4. Uses that real data to answer you

Your Python function runs in the cloud, securely, with access to your environment variables (database URLs, API keys). Claude gets structured data back. No hallucination, no estimation.


Option 1: Self-Hosting an MCP Server (The Hard Way)

If you want full control, you can run your own MCP server using the mcp Python library:

pip install mcp
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
import mcp.types as types

app = Server("my-tools")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="check-inventory",
            description="Check current inventory for a product SKU",
            inputSchema={
                "type": "object",
                "properties": {
                    "sku": {"type": "string", "description": "Product SKU to check"}
                },
                "required": ["sku"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "check-inventory":
        sku = arguments["sku"]
        # Your actual logic here
        result = query_database(sku)
        return [types.TextContent(type="text", text=str(result))]

async def main():
    async with stdio_server() as (read, write):
        await app.run(read, write, app.create_initialization_options())

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

To make this publicly accessible, you need:
- A server with a public IP (Hetzner, DigitalOcean, etc.) — ~$5–10/mo
- Nginx reverse proxy with SSL — ~30 min setup
- Process manager (systemd, supervisor) to keep it running
- Domain and DNS configuration
- Monitoring to know if it goes down

For a single tool, this is disproportionate overhead.


Option 2: Deploy Python as MCP on LiteLambda (The 5-Minute Way)

LiteLambda lets you deploy Python functions and expose them directly as MCP tools. No server, no SSL certificates, no process manager.

Step 1: Install the CLI

pip install litelambda-cli
litelambda login

Step 2: Write your Python handler

# inventory_checker.py
import os
import requests

def handler(event, context):
    """
    Check current inventory for a product SKU.

    Args:
        event: dict with 'sku' (str) — the product SKU to check
        context: execution context (provides env vars, kv store)

    Returns:
        dict with inventory status, quantity, and pricing
    """
    sku = event.get("sku")

    if not sku:
        raise ValueError("'sku' parameter is required")

    # Your actual data source — database, internal API, etc.
    api_url = os.environ.get("INVENTORY_API_URL")
    api_key = os.environ.get("INVENTORY_API_KEY")

    response = requests.get(
        f"{api_url}/products/{sku}",
        headers={"Authorization": f"Bearer {api_key}"},
        timeout=10
    )
    response.raise_for_status()

    data = response.json()
    return {
        "sku": sku,
        "status": "available" if data["quantity"] > 0 else "out_of_stock",
        "quantity": data["quantity"],
        "unit_price": data["price"],
        "warehouse": data["location"]
    }

Step 3: Deploy and expose as MCP tool

litelambda deploy check-inventory inventory_checker.py \
  --description "Check current inventory quantity and pricing for a product SKU" \
  --expose-as-mcp \
  --env INVENTORY_API_URL=https://your-api.internal \
  --env INVENTORY_API_KEY=sk_prod_xxxx

That's it. Your tool is now deployed and accessible at:

https://mcp.litelambda.in/mcp

Step 4: Connect to Claude, Cursor, or any MCP client

Cursor (~/.cursor/mcp.json):

{
  "mcpServers": {
    "my-tools": {
      "url": "https://mcp.litelambda.in/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_LITELAMBDA_API_KEY"
      }
    }
  }
}

Claude Code (terminal):

claude mcp add my-tools https://mcp.litelambda.in/mcp \
  --header "Authorization: Bearer YOUR_LITELAMBDA_API_KEY"

Restart Cursor or Claude. Your tool is now available. Ask Claude: "Check inventory for SKU-9920" — it will call your Python function and return real data.


A More Complete Example: Multi-Tool MCP Server

Real-world use case: a developer tools MCP server with three tools for an internal codebase.

# github_tools.py
import os
import requests

GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
REPO = os.environ.get("GITHUB_REPO")  # "org/repo-name"

def _github_headers():
    return {
        "Authorization": f"Bearer {GITHUB_TOKEN}",
        "Accept": "application/vnd.github+json"
    }

def handler(event, context):
    """
    Tool: GitHub repository utility

    Supported actions (passed via event['action']):
    - 'list_open_prs': List all open pull requests
    - 'get_pr': Get details of a specific PR (requires 'pr_number')
    - 'list_issues': List open issues (optional 'label' filter)
    """
    action = event.get("action")

    if action == "list_open_prs":
        resp = requests.get(
            f"https://api.github.com/repos/{REPO}/pulls?state=open",
            headers=_github_headers(),
            timeout=15
        )
        resp.raise_for_status()
        prs = resp.json()
        return {
            "count": len(prs),
            "prs": [
                {"number": pr["number"], "title": pr["title"], "author": pr["user"]["login"]}
                for pr in prs
            ]
        }

    elif action == "get_pr":
        pr_number = event.get("pr_number")
        resp = requests.get(
            f"https://api.github.com/repos/{REPO}/pulls/{pr_number}",
            headers=_github_headers(),
            timeout=15
        )
        resp.raise_for_status()
        pr = resp.json()
        return {
            "number": pr["number"],
            "title": pr["title"],
            "body": pr["body"],
            "state": pr["state"],
            "author": pr["user"]["login"],
            "mergeable": pr["mergeable"],
            "changed_files": pr["changed_files"]
        }

    elif action == "list_issues":
        label = event.get("label", "")
        url = f"https://api.github.com/repos/{REPO}/issues?state=open"
        if label:
            url += f"&labels={label}"
        resp = requests.get(url, headers=_github_headers(), timeout=15)
        resp.raise_for_status()
        issues = [i for i in resp.json() if "pull_request" not in i]
        return {
            "count": len(issues),
            "issues": [
                {"number": i["number"], "title": i["title"], "author": i["user"]["login"]}
                for i in issues
            ]
        }

    else:
        raise ValueError(f"Unknown action: {action}. Use 'list_open_prs', 'get_pr', or 'list_issues'")

Deploy:

litelambda deploy github-tools github_tools.py \
  --description "Query GitHub repository: list PRs, get PR details, list issues" \
  --expose-as-mcp \
  --env GITHUB_TOKEN=ghp_xxxx \
  --env GITHUB_REPO=myorg/myrepo

Now in Claude: "How many open PRs do we have? Are any of them mergeable?"

Claude calls github-tools with {"action": "list_open_prs"}, gets the list back, then calls {"action": "get_pr", "pr_number": 142} for the interesting ones — all in the same conversation.


Security: Why Not Just Run This Locally?

When you run an MCP server locally:

  • Your environment variables (database URLs, API tokens) are on your local machine
  • AI agents have potential access to your local filesystem
  • Team members need to set up identical environments to use the same tools

With LiteLambda:

  • Environment variables are encrypted at rest and never exposed to AI clients
  • Each function execution is sandboxed — agents cannot touch your filesystem
  • All team members use the same deployed tools via a shared API key
  • Execution logs show exactly what the agent called and what your code returned

When to Use MCP vs a Cron Job vs a Webhook

Trigger Use Case LiteLambda Feature
AI agent asks a question Real-time data lookup during a Claude conversation MCP Tool (--expose-as-mcp)
Time-based schedule "Run this every day at 8am" Cron Job (--cron "0 8 * * *")
External event "Run this when Stripe sends a webhook" Webhook Function
Manual or API trigger "Run this via HTTP from my CI pipeline" Sync/Async Function

All four are available in LiteLambda. A single Python handler file can be deployed as any of them.


Summary

Deploying a Python MCP server without infrastructure:

  1. Write a handler(event, context) function — no special MCP library needed
  2. Deploy with litelambda deploy --expose-as-mcp
  3. Add your LiteLambda MCP URL to Cursor or Claude config
  4. Your Python function is now callable by AI agents in real-time

The entire setup takes 5 minutes. Your tools run in an isolated cloud sandbox, with encrypted environment variables and full execution logs.

Start deploying Python MCP tools →


Related: Turn Any Python Function into a Claude/Cursor Tool · Python Webhook Trigger — No Server Required · LiteLambda MCP Documentation

Skip the infrastructure setup.

Run this exact code in our secure, isolated Docker sandbox. It takes 10 seconds to deploy.

Deploy this script in 60s →

No DevOps required.