Model Context ProtocolMCP Python SDK 2.2 · LangChain 1.4 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson →

MCP Inspector with mcp dev

The MCP Inspector is a developer tool that connects to a server and shows its tools, resources and prompts, so you see what a server offers before any model does.

Last updated: 29 Sep, 2026 · MCP 2.2

You have built a server and called it from code. Before you connect a server to a host, you want to see what it exposes and try a tool by hand. MCP gives you two ways to do that: the Inspector web app launched by mcp dev, and the same listing calls from your own code.

The MCP Inspector on the weather server · from the MCP Agentic AI Crash Course With Python · 24:56 to 30:58

The MCP crash course runs the Inspector on its weather server with uv run mcp dev server/weather.py. The Inspector opens as a local web page and asks for a transport type. With STDIO it starts the server as a command, connects, lists the tools, and runs get_alerts with the state CA. The result is the list of active weather alerts for California, fetched from the US National Weather Service.

The Inspector in the video offers STDIO and SSE. SSE was the older HTTP transport; the current specification defines stdio and Streamable HTTP, and this course uses those two. The video's server also imports FastMCP from mcp.server.fastmcp, which is MCPServer from mcp.server in mcp 2.2.

Running the weather server from the videoOptional

The server calls api.weather.gov, a free US government API that needs no key, only a User-Agent header. It makes the request with httpx, so install that first:

pip install "httpx==0.28.1"

Save the video's server as nws_weather.py; the video calls it server/weather.py, and a different name keeps it apart from the other weather server later in the course. Only the import and the class name differ from the video. The echo resource at the end returns in the resource templates lesson.

python
from typing import Any
import httpx
from mcp.server import MCPServer

# Initialize the MCP server
mcp = MCPServer("weather")

# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"


async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None
        
def format_alert(feature: dict) -> str:
    """Format an alert feature into a readable string."""
    props = feature["properties"]
    return f"""
        Event: {props.get('event', 'Unknown')}
        Area: {props.get('areaDesc', 'Unknown')}
        Severity: {props.get('severity', 'Unknown')}
        Description: {props.get('description', 'No description available')}
        Instructions: {props.get('instruction', 'No specific instructions provided')}
        """

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)

    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."

    if not data["features"]:
        return "No active alerts for this state."

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\n---\n".join(alerts)


@mcp.resource("echo://{message}")
def echo_resource(message: str) -> str:
    """Echo a message as a resource"""
    return f"Resource echo: {message}"

The Inspector's run, get_alerts for CA, written as a client call:

ExampleFrom the video, run on MCP 2.2
import asyncio

from mcp import Client
from nws_weather import mcp


async def main():
    async with Client(mcp) as client:
        result = await client.call_tool("get_alerts", {"state": "CA"})
        print(result.content[0].text[:500])


asyncio.run(main())

The alerts are live data, so your run prints whatever is active in California when you run it, or No active alerts for this state. The first 500 characters are printed here; drop [:500] to see every alert. Above the alerts your terminal also shows a log line, INFO HTTP Request: GET https://api.weather.gov/.... MCPServer logs to standard error at INFO level, and later lessons print more of these lines, such as a rejected argument or the traceback of a failed tool; the outputs on these pages show only what your script prints.

The rest of this lesson inspects the shop server, first with mcp dev, then with the same listing calls in code.

The mcp dev command

mcp dev loads your server file and opens the Inspector, a local web app, in your browser. There you pick a tool, fill its arguments in a form, and read the result, with no host and no model involved. It needs the [cli] extra from the setup lesson, Node.js for the web app, and uv, because the Inspector starts your server with uv run in a fresh environment. That environment gets mcp without the [cli] extra, so --with typer adds the one package the server command is missing; without it, Connect fails with Connection closed. The first run asks npx to install the Inspector package, and it opens in your browser, so it is shown here as a command rather than captured output.

bash
mcp dev shop.py --with typer

The page lists the server's tools, resources and prompts, and lets you call each one and watch the JSON-RPC messages go back and forth. It is the MCP counterpart of a run transcript: a window on what the server did.

Listing a server in code

The same four listing calls the Inspector makes are methods on the in-memory client. They run keyless, with no browser, so they belong in a small script you can keep.

python
tools = await client.list_tools()
resources = await client.list_resources()
templates = await client.list_resource_templates()
prompts = await client.list_prompts()
Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports this file. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep it in the same folder.
View the code here
shop.py
from mcp.server import MCPServer

mcp = MCPServer("Shop support")

ORDERS = {
    "A17": {"item": "blue mug", "status": "shipped", "total": 12.50},
    "B42": {"item": "desk lamp", "status": "processing", "total": 48.00},
}


@mcp.tool()
def lookup_order(order_id: str) -> str:
    """Look up an order by its id and say where it is."""
    order = ORDERS[order_id]
    return f"Order {order_id}: {order['item']}, {order['status']}."

Inspecting the shop server in code

Run the four listings against the server as it stands after the last lesson: one tool, with no resources or prompts yet.

Example
import asyncio

from mcp import Client
from shop import mcp


async def main():
    async with Client(mcp) as client:
        tools = await client.list_tools()
        print("tools:", [t.name for t in tools.tools])
        resources = await client.list_resources()
        print("resources:", [str(r.uri) for r in resources.resources])
        templates = await client.list_resource_templates()
        print("templates:", [t.uri_template for t in templates.resource_templates])
        prompts = await client.list_prompts()
        print("prompts:", [p.name for p in prompts.prompts])


asyncio.run(main())

What the listing shows now

  • tools holds lookup_order, the one tool registered so far.
  • resources and templates are empty until Resources and Resource templates add them.
  • prompts is empty until Prompts adds one. Run this script again after those lessons and the same four lines fill in.

Reading one tool's schema

Listing gives more than names. Each tool carries its description and input schema, the same fields a host reads to decide when and how to call it.

Example
import asyncio

from mcp import Client
from shop import mcp


async def main():
    async with Client(mcp) as client:
        tools = await client.list_tools()
        for tool in tools.tools:
            print(tool.name, "-", tool.description)
            print("  args:", list(tool.input_schema["properties"]))


asyncio.run(main())

The Inspector vs listing in code

mcp dev InspectorListing in code
Runs whereA web app in your browserIn a Python script
NeedsThe [cli] extra, Node.js and uvThe mcp package only
Best forPoking a server by handA repeatable check you save
Calls a toolThrough a form you fillWith call_tool

When to inspect a server

  • Right after adding a tool, to confirm its name, description and arguments are what you meant.
  • Before connecting a server to Claude Code or another host, to see what that host will be offered.
  • When a host cannot see a tool, to check whether the server lists it at all.
Watch out. mcp dev loads your server file, so an error while importing it means the Inspector shows nothing until you fix the import. The in-memory listing fails the same way, which is why running it as a script catches the problem early.
Try it yourself
  • Add a count_orders() -> int tool to shop.py and run the listing again.
  • Run mcp dev shop.py --with typer if you have the [cli] extra, Node and uv, and call lookup_order from the form.
  • Break an import in shop.py: mistype MCPServer, run the listing, and read the error.
PreviousMCP client

Every expert started right here.