MCP Core Concepts Basics
9 examples to get you started with MCP Core Concepts - 6 basic and 3 intermediate.
Prerequisites
- Python 3.10 or later.
- Install the official Python MCP SDK:
pip install mcp. - A terminal to run the server as a local process; no network setup required for stdio.
Basic Examples
1. Install and Import the SDK
Confirm the SDK is installed and pull in the pieces you need for a minimal server.
# server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("basics-demo")FastMCPis the high-level server class in the official Python SDK, built for quick tool/resource/prompt definitions.- The string passed to
FastMCPnames the server; clients display this name when listing connections. - This object is the single entry point you attach tools, resources, and prompts to.
2. Define a Single Tool
Register one callable tool using a decorator, the simplest way to expose a function to a client.
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b- The
@mcp.tool()decorator turns an ordinary Python function into a discoverable MCP tool. - Type hints (
a: int, b: int) become the tool's input schema automatically. - The docstring becomes the tool's description, which the model reads to decide when to call it.
Related: Defining Callable Tools in an MCP Server - full tool schema and handler patterns
3. Run the Server Over stdio
Start the server so a local client can spawn it as a subprocess and talk to it over standard input and output.
if __name__ == "__main__":
mcp.run(transport="stdio")transport="stdio"is the simplest transport: the server reads requests from stdin and writes responses to stdout.- stdio is the right default for local tools that run alongside the client on the same machine.
- No ports, TLS, or authentication setup is needed for a stdio server.
Related: Choosing Between stdio and HTTP/SSE Transports for MCP - when to reach for a remote transport instead
4. Connect a Client to the Server
Use the Python SDK's client helpers to spawn the server process and open a session.
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
params = StdioServerParameters(command="python", args=["server.py"])
async def main():
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
print("Connected to:", session)
asyncio.run(main())StdioServerParameterstells the client how to launch the server process (command plus arguments).stdio_clientopens the subprocess and yields read/write streams for the protocol.session.initialize()performs the MCP handshake before any tool, resource, or prompt calls are made.
5. List the Server's Tools
Ask the connected server what tools it exposes before calling any of them.
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
for tool in tools.tools:
print(tool.name, "-", tool.description)list_tools()performs discovery: the client learns what is available without hardcoding tool names.- Each returned tool carries its name, description, and input schema.
- This is the same discovery step a real client like Claude Code performs when it connects to your server.
6. Call the Tool
Invoke the add tool by name with matching arguments and read back the result.
result = await session.call_tool("add", arguments={"a": 2, "b": 3})
print(result.content)call_toolsends the tool name and a dictionary of arguments matching the tool's schema.- The response's
contentholds the tool's return value, wrapped in MCP's content format. - If the arguments do not match the schema, the server returns an error instead of a result.
Intermediate Examples
7. Add a Second Tool and Reconnect
Extend the same server with a second tool to see how multiple tools coexist in one session.
@mcp.tool()
def greet(name: str) -> str:
"""Return a friendly greeting for the given name."""
return f"Hello, {name}!"
# server now exposes both add() and greet()- Multiple
@mcp.tool()functions can live on the sameFastMCPinstance; each becomes its own discoverable entry. - A client that reconnects (or calls
list_tools()again) sees both tools without any server-side registry code. - Keeping each tool focused on one job makes the model's tool-selection decisions easier and more reliable.
8. Handle a Tool Error Gracefully
Raise a clear error from inside a tool handler so the client and model get useful feedback instead of a crash.
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide a by b."""
if b == 0:
raise ValueError("b must not be zero")
return a / b- Raising a standard Python exception inside a tool handler is caught by the SDK and returned to the client as a tool error.
- A specific, descriptive message helps the model decide whether to retry with different arguments or give up.
- Letting exceptions propagate uncaught (instead of crashing the process) keeps the server available for the next call.
9. Wire the Server into Claude Desktop
Point a real MCP client at your stdio server using its configuration file, instead of the hand-rolled client from example 4.
{
"mcpServers": {
"basics-demo": {
"command": "python",
"args": ["/absolute/path/to/server.py"]
}
}
}- Claude Desktop (and similar clients) launch stdio servers by reading a command and argument list from a config file, exactly like
StdioServerParameters. - Using an absolute path to
server.pyavoids working-directory issues when the client spawns the process. - Once configured, the same server code you tested with a hand-rolled client is now callable from Claude's conversation UI.
Related: MCP Tools vs Resources vs Prompts Comparison - decide what to expose next as your server grows
Related
- Understanding MCP: Tools, Resources, and Prompts - the conceptual foundation behind what you just built.
- Defining Callable Tools in an MCP Server - schemas and handlers in depth, beyond a single toy tool.
- Exposing Readable Data as MCP Resources - the next primitive to add to this server.
- MCP Core Concepts Best Practices - a checklist once you move past a single-tool prototype.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the current Model Context Protocol specification. Model names, SDK versions, and the MCP spec move quickly - verify current specifics at platform.claude.com/docs and modelcontextprotocol.io before relying on them.