Use a complete, runnable file rather than an isolated handler. The smallest useful Model Context Protocol (MCP) server registers a tool with an explicit input schema, starts a transport, and can be inspected by an MCP client. This guide builds that server in Python, shows the equivalent TypeScript pattern, and then covers local testing, transport choices, and safe extensions.
What an MCP server exposes
MCP standardizes how an application provides context to a large language model. The server surface has three primitives: tools (callable actions), resources (readable context), and prompts (reusable interaction templates). The official Python SDK supports stdio, Streamable HTTP, and SSE transports; the TypeScript SDK supports stdio and Streamable HTTP, with HTTP+SSE retained for backward compatibility. See the official Python SDK documentation and official TypeScript SDK documentation.
Start with one deterministic tool. Once it works in an inspector and a client test, add resources, prompts, remote transport, authentication, and authorization deliberately.
Choose Python or TypeScript
| Decision | Python SDK | TypeScript SDK |
|---|---|---|
| Runtime | Python 3.10 or newer | Node.js (the SDK examples use a modern npm project) |
| Install | uv add "mcp[cli]" or pip install "mcp[cli]" |
npm install @modelcontextprotocol/sdk zod |
| Schema style | Python type annotations and SDK decorators | Zod input and output schemas |
| Best first transport | stdio for a locally spawned process | stdio for a locally spawned process |
| Development run | uv run mcp dev server.py |
Run the compiled or configured Node entry point |
| Testing path | In-memory Client(mcp) or MCP Inspector |
SDK client example or MCP Inspector |
Python is concise for a first sample. TypeScript is a good fit when your existing service, validation, and deployment stack is already Node-based.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11#1 Best Overall
Build a minimal Python MCP server
1. Create the project and install the SDK
- Install Python 3.10 or newer.
- Create and enter a project directory, then create a virtual environment if you normally isolate Python dependencies.
- Install the official package with
uv add "mcp[cli]", or usepip install "mcp[cli]".
2. Save a complete server file
Save this as server.py. It registers a deterministic add tool, plus one resource and one prompt so you can see all three MCP primitives in a single, copyable file.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Arithmetic Server")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two integers and return the sum."""
return a + b
@mcp.resource("config://welcome")
def welcome_resource() -> str:
"""Provide a small, read-only resource."""
return "Use the add tool for integer addition."
@mcp.prompt()
def explain_addition(a: int, b: int) -> str:
"""Create a prompt that asks for an explanation of a sum."""
return f"Explain step by step why {a} + {b} equals {a + b}."
if __name__ == "__main__":
mcp.run()
The function annotations make the tool contract explicit. A client can discover the tool name, description, and input shape instead of guessing how to call it. Keep the first tool deterministic: predictable output makes transport and schema problems easier to isolate.
3. Run it in development mode
From the project directory, run:
uv run mcp dev server.py
The Python getting-started guide documents this command and recommends opening the server in MCP Inspector. Its complete code blocks are intended to be copied and used directly; see the official Python getting-started guide.
4. Inspect the primitives
In Inspector, connect to the development server and verify:
Rank #2
- Tools: discover
add, enter integer values, and confirm the result. - Resources: open
config://welcomeand check the returned text. - Prompts: select
explain_addition, provide values, and inspect the generated prompt.
Inspector catches naming, schema, and startup errors before an LLM client adds another layer of variables.
Test the Python server without a subprocess or port
The SDK’s in-memory client connects directly to the server object. The documented approach uses no subprocess, port, or transport, which makes it a fast unit-level check.
import asyncio
from mcp import ClientSession
from mcp.client.inmemory import create_connected_server_and_client_session
from server import mcp
async def main() -> None:
async with create_connected_server_and_client_session(mcp) as session:
result = await session.call_tool("add", {"a": 1, "b": 2})
assert result.structured_content == {"result": 3}
print(result.structured_content)
if __name__ == "__main__":
asyncio.run(main())
Run the test from the same environment as the server. A successful assertion proves that registration, argument validation, execution, and structured output work independently of stdio or HTTP.
Equivalent TypeScript server
Install and define the server
In a Node project, install the official packages:
npm install @modelcontextprotocol/sdk zod
The TypeScript SDK’s tool pattern supplies a name, title or description, an input schema, an output schema, and a result containing text plus structured content.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "arithmetic-server",
version: "1.0.0",
});
server.registerTool(
"add",
{
title: "Add integers",
description: "Add two integers and return the sum.",
inputSchema: { a: z.number().int(), b: z.number().int() },
outputSchema: { result: z.number().int() },
},
async ({ a, b }) => {
const result = a + b;
return {
content: [{ type: "text", text: String(result) }],
structuredContent: { result },
};
},
);
const transport = new StdioServerTransport();
await server.connect(transport);
The documented minimal connection is an McpServer followed by new StdioServerTransport() and await server.connect(transport). Runnable examples are included with the TypeScript SDK. Keep protocol messages on the expected stdio channel; ordinary diagnostic logging should not corrupt that stream.
Choose a transport deliberately
| Transport | Use it when | Operational shape |
|---|---|---|
| stdio | A local client can spawn your server | The client starts a process and exchanges protocol messages through standard input and output. It is the simplest local integration. |
| Streamable HTTP | A remote client needs network access | Run an HTTP endpoint that clients can reach; plan for deployment, connectivity, and any session or state behavior your application requires. |
| HTTP+SSE | You must support an older integration | The TypeScript documentation lists it as backward-compatible support rather than the preferred new remote choice. |
The current TypeScript guidance recommends Streamable HTTP for remote servers and stdio for local integrations. Do not move to HTTP merely to make a sample look more advanced: first prove the protocol with stdio and Inspector.
Extend the sample safely
Make schemas stricter
Declare bounds, formats, and required fields in the schema rather than relying on prompt wording. In TypeScript, express those constraints with Zod; in Python, use precise annotations and add explicit checks for domain rules that annotations cannot express.
Return useful errors
Validate inputs before performing side effects. Return an actionable protocol error for invalid values, and log the underlying exception where your host environment keeps server logs. Avoid exposing secrets, stack traces, or internal paths in tool results.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Separate read and write capabilities
Resources should represent readable context; tools should perform actions. Give destructive operations distinct names and require the calling application to provide whatever confirmation policy its environment uses. The official material establishes the primitives and transports, but it does not provide a complete production security control set; treat authentication, authorization, secret storage, auditing, and rate limits as deployment-specific work that needs its own review.
Add remote access only after local tests pass
For Streamable HTTP, define how clients reach the endpoint, how sessions or state are handled, and how access is authenticated. Test malformed requests, dropped connections, duplicate calls, and server restarts before exposing the service outside the local machine.
Troubleshoot common failures
The client cannot start the server
- Confirm Python is 3.10 or newer, or that Node can execute the TypeScript build.
- Run the command from the directory containing
server.pyor the configured JavaScript entry point. - Check that the SDK was installed in the same environment used to launch the process.
The tool does not appear in Inspector
- Verify the decorator (Python) or
registerToolcall (TypeScript) executes during startup. - Check that the tool name is stable and that the input schema is valid.
- Restart Inspector after changing registration code.
Arguments fail validation
- Send integers to the Python
addfunction, not quoted strings. - In TypeScript, satisfy both Zod schemas: values must be integers and the output must contain an integer
result. - Inspect the discovered schema in Inspector rather than guessing field names.
The process exits immediately
- For Python, ensure the
if __name__ == "__main__"block callsmcp.run(). - For TypeScript, ensure the top-level connection is reached and that the process is not being terminated by an unhandled startup exception.
- Keep protocol output separate from diagnostic messages.
In-memory tests pass but a real client fails
The in-memory test excludes transport and process boundaries by design. Reproduce the call in Inspector, then test the exact stdio command or HTTP endpoint used by the target client. This narrows the problem to transport configuration instead of tool logic.
Performance, reliability, and cost considerations
- Start with deterministic, short-running tools so failures are obvious and retries are safe to reason about.
- Keep resource payloads focused; large context responses increase client-side processing even when the protocol call succeeds.
- For remote HTTP, measure the behavior that matters to your application—connection setup, server processing, and downstream calls—rather than assuming local stdio timings apply.
- Do not claim reliability or throughput from this minimal sample. The official SDK pages describe supported transports and examples, not a universal performance benchmark.
- Pin and review SDK versions in your project, and rerun Inspector and client tests after upgrades.
Or skip the browser setup
If your MCP tool needs website screenshots, an API can remove browser orchestration from the server. ScreenshotNeo is a website screenshot API and MCP server: one GET request returns a PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.
cURL
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 ScreenshotNeo documentation for request options. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The service also supports full-page lazy-image loading, CSS-selector element capture, dark mode, device and viewport settings, retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work.
Best Value
Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get an API key.
What to verify before calling the sample complete
- The server starts with the documented runtime and package command.
- Inspector discovers every primitive you intended to expose.
- Valid and invalid tool inputs produce the expected structured result or error.
- An in-memory test passes before transport debugging begins.
- Your chosen transport matches the deployment: stdio locally, Streamable HTTP remotely, and HTTP+SSE only when compatibility requires it.
- Any remote deployment has a documented plan for authentication, authorization, secrets, logging, and failure recovery.
Frequently Asked Questions
Can an MCP server expose only tools?
Yes. Tools are the only primitive required for a minimal action-oriented server; resources and prompts are optional additions.
Do I need a network port to test a Python server?
No. The documented in-memory client connects directly to the server object without a subprocess, port, or transport.
Recommended Free Tools
Which transport should a remote MCP server use?
The current TypeScript documentation recommends Streamable HTTP for remote servers. stdio is intended for local clients that spawn the process; HTTP+SSE remains for backward compatibility.
Where can I find complete SDK examples?
Use the official Python pages at https://py.sdk.modelcontextprotocol.io/get-started/ and https://py.sdk.modelcontextprotocol.io/, and the TypeScript SDK and server guide at https://ts.sdk.modelcontextprotocol.io/ and https://ts.sdk.modelcontextprotocol.io/server.
The Bottom Line
Write one complete, schema-driven tool first, run it over stdio, inspect it, and test it in memory. Only then add resources, prompts, remote HTTP, and production security controls.
Quick Recap
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.

