mcp_aj
README.md
# Python MCP 2.x Server: Hands-on Example
This project is a small, complete Model Context Protocol (MCP) server and client written for Python 3.13 and the installed `mcp==2.1.1` SDK. It deliberately uses the MCP 2.x `MCPServer` API, not the old `FastMCP` API.
## What MCP is
MCP is an open protocol for connecting an AI application to external capabilities in a consistent way. Instead of an application inventing a separate integration format for every database, API, or local utility, MCP gives it standard operations to discover capabilities and invoke them.
## MCP architecture
```text
MCP client -- JSON-RPC over stdio --> MCP server --> registered Python tool/resource/prompt
^ | |
+----------- structured result --------+-------------------------+
```
This example uses the **stdio transport**. The client starts `server.py` as a subprocess, writes MCP JSON-RPC messages to its standard input, and receives MCP responses from standard output. Standard output is therefore reserved for the protocol; diagnostics belong on standard error.
## Client versus server
An MCP **server** exposes capabilities and implements their behavior. Here, `server.py` declares five tools, a resource, and a prompt.
An MCP **client** connects to a server, discovers those capabilities, and requests work. Here, `client.py` starts the server through the SDK's stdio transport, initializes an `ClientSession`, lists tools, and calls three of them.
## Tools, resources, and prompts
| MCP feature | Purpose | This project |
| --- | --- | --- |
| Tool | An action with input that returns a result. | `add_numbers`, `read_text_file`, and the other functions. |
| Resource | Readable server-provided context addressed by a URI. | `info://server` returns server information. |
| Prompt | A reusable template that returns chat messages. | `summarize_file(filename)` produces a summarization request containing a safe project file. |
## Project structure
```text
mcp_python_server/
├── server.py # MCP 2.x server, five tools, one resource, and one prompt
├── client.py # stdio MCP client that exercises the math tools
├── requirements.txt # pinned, verified MCP SDK dependency
├── README.md # setup, architecture, and troubleshooting guide
└── test.txt # a safe example file for the file-reading tools and prompt
```
## Installation on macOS
From the repository root:
```bash
cd mcp_python_server
python3.13 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
```
The requirement is pinned to `mcp==2.1.1`, the MCP 2.x SDK version verified for this project. Its transitive dependencies are installed automatically by pip.
## Run the server
Activate the environment, then run:
```bash
python server.py
```
The server waits for MCP JSON-RPC messages on standard input. It does not print a friendly interactive menu, because standard output is the MCP protocol channel. Usually another MCP client, such as `client.py`, launches it for you.
## Run the client
With the environment activated:
```bash
python client.py
```
The client asks for two integers and a comma-separated list of numbers, then passes your values to the MCP tools. For example, enter `10`, `20`, and `10, 20, 30, 40, 50`; the output includes:
```text
Add result: 30
Multiply result: 200
Average result: 30.0
```
The first line also lists the discovered server tools.
## What happens in an MCP request
For `add_numbers(10, 20)`, the request flow is:
1. `client.py` creates `StdioServerParameters` for the same Python interpreter and `server.py`, then opens `stdio_client(...)`.
2. `ClientSession.initialize()` completes the MCP handshake; `list_tools()` asks the server which tools it exposes.
3. `session.call_tool("add_numbers", {"a": 10, "b": 20})` sends an MCP `tools/call` JSON-RPC request over stdio.
4. The MCP 2.x `MCPServer` routes that request to the function registered with `@server.tool()`.
5. `add_numbers` receives two validated Python integers and returns `10 + 20`, which is `30`.
6. The SDK serializes that result into an MCP `CallToolResult`; the client receives it and prints `Add result: 30`.
The multiply and average calls follow the same path. The client really invokes a separate server subprocess through MCP; it does not import and call the tool functions directly.
## File tool safety
`read_text_file` and `get_file_info` resolve a requested path and require it to remain inside the directory containing `server.py`. They reject blank names, null bytes, missing files, directories, traversal such as `../secret.txt`, and symlinks that resolve outside the project. Text reads are UTF-8 only, with clear errors for invalid text. `test.txt` is a safe file to use:
```python
# Via an MCP client session:
await session.call_tool("read_text_file", {"filename": "test.txt"})
await session.call_tool("get_file_info", {"filename": "test.txt"})
```
## Common errors
| Problem | Likely cause and fix |
| --- | --- |
| `ModuleNotFoundError: No module named 'mcp'` | Activate the project virtual environment and run `python -m pip install -r requirements.txt`. |
| `mcp` is version 1.x | Use the virtual environment above and reinstall from `requirements.txt`; do not downgrade or use old `FastMCP` examples. |
| Server appears to do nothing | This is normal when started directly: it is waiting for MCP messages on standard input. Run `python client.py`. |
| `Access denied: files must be inside the project directory` | Supply a relative filename such as `test.txt`, not a traversal or a file outside this folder. |
| `File is not valid UTF-8 text` | Use `read_text_file` only with a UTF-8 text file. `get_file_info` can still inspect the file metadata. |
| Client cannot start the server | Confirm `server.py` exists and run `client.py` from this project directory using the activated environment. |
## SDK APIs used
This project was verified against the installed MCP 2.1.1 package. The key APIs are:
```python
from mcp.server import MCPServer
from mcp import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
```
`server.py` uses `@server.tool()`, `@server.resource()`, `@server.prompt()`, and `server.run(transport="stdio")`. It does **not** import or use `mcp.server.fastmcp.FastMCP`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues