Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

The smallest useful Python MCP server is a typed function decorated with @mcp.tool(). Install the official SDK with its CLI extra, save a short server file, and run uv run mcp dev server.py to open MCP Inspector. The example below also adds a URI-template resource and shows automated in-memory testing, so you can understand what runs locally before choosing a production transport.

What you need

  • Python 3.10 or newer. The official Python SDK documentation currently identifies the v2 line as stable.
  • A virtual environment or project managed by uv or pip.
  • The MCP CLI extra, which supplies the mcp command used by the Inspector workflow.

Install the package in your project:

uv add "mcp[cli]"

With pip, use:

pip install "mcp[cli]"

These commands and the version guidance are documented at the official Python SDK documentation.

A complete minimal server

Create a file named server.py:

from mcp.server import MCPServer

mcp = MCPServer("Demo")


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


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

This file creates an MCP server named Demo. The add function is exposed as a tool. Its Python type hints let the SDK derive the input schema, so you do not write JSON Schema or protocol parsing for this starter example. The greeting function is exposed at URI template greeting://{name}; a client can read greeting://World and receive Hello, World!.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The decorators are registered when Python imports the module. Keep the function docstrings: clients commonly display them as descriptions, and they tell a model what an operation does.

Run it with MCP Inspector

  1. Open a terminal in the directory containing server.py.
  2. Start the development command:
    uv run mcp dev server.py

    If you installed with pip and are not using uv, run the equivalent mcp dev server.py command from the environment containing the package.

  3. The command starts the server and opens MCP Inspector, an interactive browser UI.
  4. In Inspector, find the add tool, enter 1 for a and 2 for b, then invoke it. The result is 3.
  5. Use the resource view to read greeting://World. The returned text is Hello, World!.

The SDK getting-started guide treats its documentation examples as complete working files and uses the same Inspector workflow for local exploration. Inspector is useful for checking names, schemas and returned values without first building a host application.

Tools, resources and prompts are different

Choose the primitive according to who initiates the interaction:

Primitive Purpose Caller Example
Tool An action that can change state or perform computation The model chooses and calls it add(a, b)
Resource Read-only data addressed by a URI The application chooses to read it greeting://World
Prompt A reusable message template A person invokes it, often from a menu or slash command A named writing or analysis template

Do not describe a prompt as a tool or resource: the SDK documents separate invocation roles for all three in its server reference. A first server can contain only tools; the resource above demonstrates URI-based read-only data without adding a database or network dependency.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Automated testing without a subprocess

Inspector is interactive. For a repeatable test, the SDK documents an in-memory client that connects directly to the server object. It does not open a port, start a subprocess or select a network transport.

Add a test file such as test_server.py:

import pytest
from mcp import Client
from server import mcp


@pytest.mark.asyncio
async def test_add():
    async with Client(mcp) as client:
        result = await client.call_tool("add", {"a": 1, "b": 2})
        assert result.structured_content == {"result": 3}

Run it with your usual pytest command after installing pytest and an asyncio pytest plugin in the project. The important pattern is async with Client(mcp) as client, followed by client.call_tool. Keep this test separate from transport tests: it validates registration, argument handling and the returned structured value, while Inspector validates the local interactive experience.

Extending the example safely

Add validation at the function boundary

Type hints describe the expected shape, but business rules still belong in your function. Check ranges, required identifiers and permissions before performing side effects. Return predictable values and raise clear, intentional errors rather than exposing tracebacks or secrets.

Keep imports and startup cheap

Inspector imports the module to discover its primitives. Avoid doing expensive work, opening database connections or making API calls at import time. Initialize external clients inside functions or in an explicit startup path.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Expose only the capability a client needs

A tool can represent an action such as creating a ticket; a resource is better for reading a document or status snapshot. If a user needs a guided, reusable instruction, define a prompt instead of disguising it as a tool.

Move to a documented transport for deployment

The example is deliberately local. Production deployment requires choosing a transport, configuring authorization and mounting or hosting the server in your application. The official SDK documentation links to transport, authorization, deployment, FastAPI and Starlette integration guidance; do not treat the Inspector command as a production service.

Common errors and fixes

mcp: command not found

The CLI extra is missing or the active shell is outside the environment where it was installed. Install mcp[cli] again, activate the virtual environment, or run the command through uv: uv run mcp dev server.py.

Python version error during installation

Use Python 3.10 or newer, which is the requirement listed by the current SDK documentation. Check python --version and recreate the environment with a supported interpreter.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Inspector cannot start the file

Run the command from the directory containing server.py, verify the filename and fix syntax or import errors shown in the terminal. Keep application startup code out of module scope while diagnosing imports.

A tool is missing from Inspector

Confirm the function has the @mcp.tool() decorator, that the module imports successfully and that you restarted the development command after editing. The decorator must be applied to the function registered on the same MCPServer instance.

Arguments are rejected

Send the exact parameter names and JSON-compatible values defined by the function signature. For the example, the call must contain integer fields a and b; do not send a single positional string.

The in-memory test cannot import server

Place test_server.py beside server.py, run pytest from the project root and ensure the environment includes the project dependencies. Importing the module should register the server without starting a separate process.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Performance, reliability and security notes

  • The arithmetic tool is local and deterministic; real tools inherit the latency and failure modes of the APIs, files or databases they call.
  • Set timeouts on outbound requests, validate untrusted arguments and avoid returning credentials or sensitive raw responses.
  • Use structured, bounded results. Large payloads make clients slower and can exceed a host’s context limits.
  • Log failures without logging tokens, cookies or personal data. Add authentication and authorization before exposing a server outside a trusted development machine.
  • Use Inspector and in-memory tests together: one catches interactive registration mistakes, the other gives a repeatable check for changes.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your actual goal is to capture a web page for documentation, testing or an AI workflow rather than build a browser automation stack, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server includes take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

Using the API is one GET request:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the complete parameter list and MCP setup in the ScreenshotNeo documentation. Every plan includes its features; the Free plan includes 1,000 shots per month without a card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Where to go next

Once this server works locally, read the SDK sections on connecting to a real host, transports, authorization, testing and deployment. Add one capability at a time, keep an automated test for each tool, and decide explicitly whether new data belongs in a resource or whether an operation should be a tool.

Frequently Asked Questions

Can I use this example with Python 3.9?

No. The current official Python SDK documentation lists Python 3.10 or newer as its requirement.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does the Inspector command deploy my server?

No. uv run mcp dev server.py is a local development and inspection workflow. Production hosting requires a chosen transport plus deployment and authorization configuration.

Do I need to write JSON Schema for the add tool?

Not for this example. The SDK derives the input schema from the function’s Python type hints.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.