| Name | chuk-mcp JSON |
| Version |
0.8.1
JSON |
| download |
| home_page | None |
| Summary | Model Context Protocol client and server library with optional Pydantic support |
| upload_time | 2025-11-02 14:33:02 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.11 |
| license | MIT |
| keywords |
llm
openai
claude
mcp
client
protocol
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# chuk-mcp
[](https://pypi.org/project/chuk-mcp)
[](https://pypi.org/project/chuk-mcp)
[](https://pypi.org/project/chuk-mcp)
[](https://github.com/chrishayuk/chuk-mcp/actions)
[](https://codecov.io/gh/chrishayuk/chuk-mcp)
[](https://github.com/astral-sh/ruff)
[](LICENSE)
**A lean, production-minded Python implementation of the Model Context Protocol (MCP).**
**Brings first-class MCP protocol support to Python — lightweight, async, and spec-accurate from day one.**
**Requires Python 3.11+**
`chuk-mcp` gives you a clean, typed, transport-agnostic implementation for both **MCP clients and servers**. It focuses on the protocol surface (messages, types, versioning, transports) and leaves orchestration, UIs, and agent frameworks to other layers.
> ✳️ **What this is**: a **protocol compliance library** with ergonomic helpers for clients and servers.
>
> ⛔ **What this isn't**: a chatbot runtime, workflow engine, or an opinionated application framework.
## Architecture: Where chuk-mcp Fits
### Stack Overview
```
┌──────────────────────────────────────┐
│ Your AI Application │
│ (Claude, GPT, custom agents) │
└────────────┬─────────────────────────┘
│ MCP Protocol
▼
┌──────────────────────────────────────┐
│ chuk-mcp Client │ ← You are here
│ • Protocol compliance │
│ • Transport (stdio/Streamable HTTP)│
│ • Type-safe messages │
│ • Capability negotiation │
└────────────┬─────────────────────────┘
│ MCP Protocol
▼
┌──────────────────────────────────────┐
│ chuk-mcp Server (optional) │
│ • Protocol handlers │
│ • Tool/Resource registration │
│ • Session management │
└────────────┬─────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Your Tools & Resources │
│ (databases, APIs, files, etc) │
└──────────────────────────────────────┘
```
**chuk-mcp provides the protocol layer** — connect AI applications to tools and data sources using the standard MCP protocol.
### Internal Architecture
The library itself is organized in layers that you can use at different levels of abstraction:
```
┌─────────────────────────────────────────┐
│ CLI & Demo Layer │ __main__.py, demos/
├─────────────────────────────────────────┤
│ Client/Server API │ High-level abstractions
├─────────────────────────────────────────┤
│ Protocol Layer │ Messages, types, features
├─────────────────────────────────────────┤
│ Transport Layer │ stdio, Streamable HTTP
├─────────────────────────────────────────┤
│ Base Layer │ Pydantic fallback, config
└─────────────────────────────────────────┘
```
**Layer Details:**
| Layer | Purpose | Usage |
|-------|---------|-------|
| **CLI & Demo** | Built-in utilities and demonstrations | Optional — use protocol layer directly |
| **Client/Server API** | High-level abstractions for client-server interactions | Optional — can use protocol layer directly |
| **Protocol Layer** | Message definitions, type-safe request/response handling, capability negotiation | Core — implements MCP spec |
| **Transport Layer** | Pluggable transport implementations (stdio, Streamable HTTP) | Choose based on deployment |
| **Base Layer** | Pydantic fallback, shared config, type adapters | Foundation — automatic |
Most users work with the **Protocol Layer** (`send_*` functions) and **Transport Layer** (stdio/HTTP clients), optionally using the **Client/Server API** for higher-level abstractions.
---
## Table of Contents
* [Why chuk‑mcp?](#why-chuk-mcp)
* [At a Glance](#at-a-glance)
* [Install](#install)
* [Quick Start](#quick-start)
* [Core Concepts](#core-concepts)
* [Tools](#tools)
* [Resources](#resources)
* [Prompts](#prompts)
* [Roots (optional)](#roots-optional)
* [Sampling & Completion (optional)](#sampling--completion-optional)
* [Transports](#transports)
* [Configuration Examples](#configuration-examples)
* [Examples & Feature Demonstrations](#examples--feature-demonstrations)
* [Versioning & Compatibility](#versioning--compatibility)
* [Comparison with Official MCP SDK](#comparison-with-official-mcp-sdk)
* [Design Goals & Non‑Goals](#design-goals--non-goals)
* [FAQ](#faq)
* [Contributing](#contributing)
* [Feature Showcase](#feature-showcase)
* [Ecosystem](#ecosystem)
* [License](#license)
---
## Why chuk-mcp?
* **Protocol-first**: Focuses on MCP messages, types, and capability negotiation — [spec.modelcontextprotocol.io](https://spec.modelcontextprotocol.io)
* **Client + Server**: Full support for building both MCP clients and servers
* **Typed**: Full type hints; optional Pydantic models when available
* **Transport-agnostic**: stdio by default, Streamable HTTP (NDJSON) for remote servers, easily extensible
* **Async-first**: Built on AnyIO; integrate with `anyio.run(...)` or your existing loop
* **Small & focused**: No heavy orchestration or agent assumptions
* **Clean protocol layer**: Errors fail fast without retries — bring your own error handling strategy
* **Production-minded**: Clear errors, structured logging hooks, composable with retry/caching layers
---
## At a Glance
**Try it now:**
```bash
# Install an example MCP server
uv tool install mcp-server-sqlite
# Run the quick-start example
uv run python examples/quickstart_sqlite.py
```
### Hello World
A minimal working MCP server in ~10 lines:
```python
# hello_mcp.py
import anyio
from chuk_mcp.server import MCPServer, run_stdio_server
from chuk_mcp.protocol.types import ServerCapabilities, ToolCapabilities
async def main():
server = MCPServer("hello", "1.0", ServerCapabilities(tools=ToolCapabilities()))
async def handle_tools_list(message, session_id):
return server.protocol_handler.create_response(
message.id,
{"tools": [{"name": "hello", "description": "Say hi", "inputSchema": {"type": "object"}}]}
), None
server.protocol_handler.register_method("tools/list", handle_tools_list)
await run_stdio_server(server)
anyio.run(main)
```
**Run it:** `uv run python hello_mcp.py` — or connect any MCP client via stdio!
---
**Stdio (local processes):**
```python
# Connect to an MCP server via stdio and list tools
import anyio
from chuk_mcp import StdioServerParameters, stdio_client
from chuk_mcp.protocol.messages import send_initialize
from chuk_mcp.protocol.messages.tools import send_tools_list
async def main():
params = StdioServerParameters(command="uvx", args=["mcp-server-sqlite", "--db-path", "example.db"])
async with stdio_client(params) as (read, write):
init = await send_initialize(read, write)
tools = await send_tools_list(read, write)
print("Server:", init.serverInfo.name)
print("Tools:", [t.name for t in tools.tools])
anyio.run(main)
```
**Streamable HTTP (remote servers):**
```python
# Local dev (plain HTTP)
import anyio
from chuk_mcp.transports.http import http_client, HttpClientParameters
from chuk_mcp.protocol.messages import send_initialize
async def main():
params = HttpClientParameters(
url="http://localhost:8989/mcp",
timeout_s=30,
headers={"Authorization": "Bearer <token>"}
)
async with http_client(params) as (read, write):
init = await send_initialize(read, write)
print("Connected:", init.serverInfo.name)
anyio.run(main)
# Production (TLS)
async def main_secure():
params = HttpClientParameters(
url="https://mcp.example.com/mcp",
timeout_s=30,
headers={"Authorization": "Bearer <token>"}
)
async with http_client(params) as (read, write):
init = await send_initialize(read, write)
print("Connected:", init.serverInfo.name)
anyio.run(main_secure)
```
---
## Install
### With `uv` (recommended)
```bash
uv add chuk-mcp # core (Python 3.11+ required)
uv add "chuk-mcp[pydantic]" # add typed Pydantic models (Pydantic v2 only)
uv add "chuk-mcp[http]" # add Streamable HTTP transport extras
uv add "chuk-mcp[pydantic,http]" # full install with all features
```
### With `pip`
```bash
pip install "chuk-mcp"
pip install "chuk-mcp[pydantic]" # Pydantic v2 only
pip install "chuk-mcp[http]" # httpx>=0.28 for Streamable HTTP
pip install "chuk-mcp[pydantic,http]" # full install
```
> *(Requires `pydantic>=2.11.1,<3` and `httpx>=0.28.1,<1` for `[pydantic]` and `[http]` extras.)*
**Python versions:** Requires Python 3.11+; see badge for tested versions.
Verify:
```bash
python -c "import chuk_mcp; print('✅ chuk-mcp ready')"
```
---
## Quick Start
### Minimal initialize (inline demo server)
```python
import anyio
from chuk_mcp import StdioServerParameters, stdio_client
from chuk_mcp.protocol.messages import send_initialize
async def main():
params = StdioServerParameters(
command="python",
args=["-c", "import json,sys; req=json.loads(sys.stdin.readline()); print(json.dumps({\"id\":req['id'],\"result\":{\"serverInfo\":{\"name\":\"Demo\",\"version\":\"1.0\"},\"protocolVersion\":\"<negotiated-by-client>\",\"capabilities\":{}}}))"]
)
async with stdio_client(params) as (read, write):
res = await send_initialize(read, write)
print("Connected:", res.serverInfo.name)
anyio.run(main)
```
> **Note:** Protocol version is negotiated during `initialize`; avoid hard-coding.
> **Windows users:** Windows cmd/PowerShell may buffer stdio differently. Use `uv run` or WSL for local development if you encounter deadlocks.
**Run it:**
```bash
uv run python examples/quickstart_minimal.py
```
### Real server (SQLite example with capability check)
```python
import anyio
from chuk_mcp import StdioServerParameters, stdio_client
from chuk_mcp.protocol.messages import send_initialize
from chuk_mcp.protocol.messages.tools import send_tools_call, send_tools_list
async def main():
params = StdioServerParameters(command="uvx", args=["mcp-server-sqlite", "--db-path", "example.db"])
async with stdio_client(params) as (read, write):
# Initialize and check capabilities
init = await send_initialize(read, write)
# Capability-gated behavior
if hasattr(init.capabilities, 'tools'):
tools = await send_tools_list(read, write)
print("Tools:", [t.name for t in tools.tools])
result = await send_tools_call(read, write, name="read_query", arguments={"query": "SELECT 1 as x"})
print("Result:", result.content)
else:
print("Server does not support tools")
anyio.run(main)
```
**Run it:**
```bash
# Install SQLite server
uv tool install mcp-server-sqlite
# Run example
uv run python examples/quickstart_sqlite.py
```
### Minimal server (protocol layer)
Build your own MCP server using the same protocol layer. See [`examples/e2e_*_server.py`](examples/) for complete working servers:
```python
# Conceptual example — for a runnable server, see examples/e2e_*_server.py
import anyio
from chuk_mcp.server import MCPServer, run_stdio_server
from chuk_mcp.protocol.types import ServerCapabilities, ToolCapabilities
async def main():
server = MCPServer(
name="demo-server",
version="0.1.0",
capabilities=ServerCapabilities(tools=ToolCapabilities())
)
# Register handlers using the protocol layer
async def handle_tools_list(message, session_id):
# Return (response, notifications). Second value is reserved for
# optional out-of-band notifications; use None if not sending any.
return server.protocol_handler.create_response(
message.id,
{"tools": [{
"name": "greet",
"description": "Say hello",
"inputSchema": {
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"]
}
}]}
), None
server.protocol_handler.register_method("tools/list", handle_tools_list)
await run_stdio_server(server)
anyio.run(main)
```
**Pair it with a client:**
```bash
# See examples/ for complete client-server pairs
uv run python examples/e2e_tools_client.py
```
> The examples above use stdio. Swap the transport to talk to remote servers (see **Transports**).
---
## Core Concepts
### Tools
Discover and call server-exposed functions.
```python
from chuk_mcp.protocol.messages.tools import send_tools_list, send_tools_call
# list
tools = await send_tools_list(read, write)
for t in tools.tools:
print(t.name, "-", t.description)
# call
call = await send_tools_call(read, write, name="greet", arguments={"name": "World"})
print(call.content)
```
**See full example:** [`examples/e2e_tools_client.py`](examples/e2e_tools_client.py)
### Resources
List/read (and optionally subscribe to) data sources.
```python
from chuk_mcp.protocol.messages.resources import send_resources_list, send_resources_read
resources = await send_resources_list(read, write)
if resources.resources:
uri = resources.resources[0].uri
data = await send_resources_read(read, write, uri)
print(data.contents)
```
**See full examples:**
* [`examples/e2e_resources_client.py`](examples/e2e_resources_client.py)
* [`examples/e2e_subscriptions_client.py`](examples/e2e_subscriptions_client.py)
### Prompts
Parameterized, reusable prompt templates.
```python
from chuk_mcp.protocol.messages.prompts import send_prompts_list, send_prompts_get
prompts = await send_prompts_list(read, write)
if prompts.prompts:
got = await send_prompts_get(read, write, name=prompts.prompts[0].name, arguments={})
for m in got.messages:
print(m.role, m.content)
```
**See full example:** [`examples/e2e_prompts_client.py`](examples/e2e_prompts_client.py)
### Roots (optional)
Advertise directories the client authorizes the server to access.
```python
from chuk_mcp.protocol.messages.roots import send_roots_list
roots = await send_roots_list(read, write) # if supported
```
**See full example:** [`examples/e2e_roots_client.py`](examples/e2e_roots_client.py)
### Sampling & Completion (optional)
Some servers can ask the client to sample text or provide completion for arguments. These are opt-in and capability-gated.
**See full examples:**
* [`examples/e2e_sampling_client.py`](examples/e2e_sampling_client.py)
* [`examples/e2e_completion_client.py`](examples/e2e_completion_client.py)
---
## Transports
`chuk-mcp` cleanly separates **protocol** from **transport**, so you can use the same protocol handlers with any transport layer:
* **Stdio** — ideal for local child-process servers
* **Streamable HTTP** — speak to remote servers over HTTP (chunked/NDJSON)
* **Extensible** — implement your own transport by adapting the simple `(read, write)` async interface
> **Note:** chuk-mcp is fully async (AnyIO). Use `anyio.run(...)` or integrate into your event loop.
> **Note:** Protocol **capabilities** are negotiated during `initialize`, independent of transport. You choose the transport (stdio or Streamable HTTP) based on deployment/runtime needs.
> **Thread-safety:** Client instances are not thread-safe across event loops. See [FAQ](#is-it-thread-safe) for details.
> **Streamable HTTP** uses chunked NDJSON. Configure `HttpClientParameters(timeout_s=30, headers={"Authorization": "Bearer ..."})`. Clients stream NDJSON with backpressure. For large payloads, prefer NDJSON chunks over base64 blobs to avoid memory spikes.
> **Framing:** Streamable HTTP uses NDJSON (one JSON object per line). Servers should flush after each object; proxies must not buffer indefinitely.
> **Compression:** Enable gzip at the proxy to reduce large content streams. MCP payloads compress well.
> **Protocol Layer Design:** The protocol layer is intentionally **clean and minimal** — errors are raised immediately without retries. This design keeps the protocol layer focused on message transport and compliance with the MCP specification. For production use cases requiring retry logic, error handling, rate limiting, or caching, use [chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor) which provides composable wrappers for retries with exponential backoff, rate limiting, and caching. This separation of concerns allows you to choose the right retry strategy for your specific application needs.
> **Security:** When exposing Streamable HTTP, terminate TLS at a proxy and require auth (e.g., bearer tokens). For private CAs, configure your client's trust store (e.g., `SSL_CERT_FILE=/path/ca.pem`, `REQUESTS_CA_BUNDLE`, or `SSL_CERT_DIR`). The protocol layer is transport-agnostic and does not impose auth.
---
## Configuration Examples
### JSON config (client decides how to spawn/connect)
```json
{
"mcpServers": {
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "database.db"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
}
}
}
```
### Loading config in code
```python
from chuk_mcp import StdioServerParameters, stdio_client
from chuk_mcp.protocol.messages import send_initialize
params = StdioServerParameters(command="uvx", args=["mcp-server-sqlite", "--db-path", "database.db"])
async with stdio_client(params) as (read, write):
init = await send_initialize(read, write)
print("Connected to", init.serverInfo.name)
```
---
## Examples & Feature Demonstrations
The [`examples/`](examples/) directory contains comprehensive, working demonstrations of all MCP features:
### Quick Start Examples
* [`quickstart_minimal.py`](examples/quickstart_minimal.py) — Minimal MCP client setup
* [`quickstart_sqlite.py`](examples/quickstart_sqlite.py) — Working with SQLite MCP server
* [`quickstart_resources.py`](examples/quickstart_resources.py) — Accessing server resources
* [`quickstart_complete.py`](examples/quickstart_complete.py) — Multi-feature demo
### End-to-End (E2E) Examples
Complete **client-server pairs** built with pure chuk-mcp, demonstrating both client and server implementation for each MCP feature:
**Core Features:**
* [`e2e_tools_client.py`](examples/e2e_tools_client.py) — Tool registration, discovery, and invocation
* [`e2e_resources_client.py`](examples/e2e_resources_client.py) — Resource listing and reading
* [`e2e_prompts_client.py`](examples/e2e_prompts_client.py) — Reusable prompt templates
**Advanced Features:**
* [`e2e_roots_client.py`](examples/e2e_roots_client.py) — File system root management
* [`e2e_sampling_client.py`](examples/e2e_sampling_client.py) — Server-initiated LLM requests
* [`e2e_completion_client.py`](examples/e2e_completion_client.py) — Autocomplete functionality
* [`e2e_subscriptions_client.py`](examples/e2e_subscriptions_client.py) — Resource change notifications
* [`e2e_cancellation_client.py`](examples/e2e_cancellation_client.py) — Operation cancellation
* [`e2e_progress_client.py`](examples/e2e_progress_client.py) — Progress tracking
* [`e2e_logging_client.py`](examples/e2e_logging_client.py) — Log message handling
* [`e2e_elicitation_client.py`](examples/e2e_elicitation_client.py) — User input requests
* [`e2e_annotations_client.py`](examples/e2e_annotations_client.py) — Content metadata
**Error Handling:**
* [`initialize_error_handling.py`](examples/initialize_error_handling.py) — Comprehensive error handling patterns (OAuth 401, version mismatch, timeout, etc.)
**Running Examples:**
Many E2E examples are self-contained with their own **protocol-level server** built using pure chuk-mcp. Where relevant, the client starts the corresponding demo server:
```bash
# Run any example directly - the client will start its server
uv run python examples/e2e_tools_client.py
# Test all E2E examples
for example in examples/e2e_*_client.py; do
echo "Testing $example"
uv run python "$example" || exit 1
done
```
> **Note:** Where relevant, examples include a corresponding `e2e_*_server.py` showing a minimal server built with the same protocol layer.
See [`examples/README.md`](examples/README.md) for detailed documentation of all examples.
---
## Versioning & Compatibility
* `chuk-mcp` follows the MCP spec revisions and negotiates capabilities at **initialize**.
* Newer features are **capability-gated** and degrade gracefully with older servers.
* Optional typing/validation uses **Pydantic if available**, otherwise a lightweight fallback.
### 📋 Supported Protocol Versions (as of v0.1.x)
| Version | Status | Support Policy |
|---------|--------|----------------|
| `2025-06-18` | Latest | Primary support, all features |
| `2025-03-26` | Stable | Full compatibility, maintained |
| `2024-11-05` | Legacy | Backward compatibility, deprecation TBD |
**Tested Platforms:** Linux, macOS, Windows (Python 3.11+)
> **Support Policy:** Latest and Stable versions receive full support. Legacy version support will be maintained until 2026-Q2, after which it may be deprecated. See [changelog](CHANGELOG.md) for migration guidance.
### 📊 Client Feature Support Matrix
| Feature Category | 2024-11-05 | 2025-03-26 | 2025-06-18 | Implementation Status |
|-----------------|------------|------------|------------|---------------------|
| **Core Operations** | | | | |
| Tools (list/call) | ✅ | ✅ | ✅ | ✅ Complete |
| Resources (list/read/subscribe) | ✅ | ✅ | ✅ | ✅ Complete |
| Prompts (list/get) | ✅ | ✅ | ✅ | ✅ Complete |
| **Transport** | | | | |
| Stdio | ✅ | ✅ | ✅ | ✅ Complete |
| Streamable HTTP | – | ✅ | ✅ | ✅ Complete |
| **Advanced Features** | | | | |
| Sampling | ✅ | ✅ | ✅ | ✅ Complete |
| Completion | ✅ | ✅ | ✅ | ✅ Complete |
| Roots | ✅ | ✅ | ✅ | ✅ Complete |
| Elicitation | ❌ | ❌ | ✅ | ✅ Complete |
| **Quality Features** | | | | |
| Progress Tracking | ✅ | ✅ | ✅ | ✅ Complete |
| Cancellation | ✅ | ✅ | ✅ | ✅ Complete |
| Notifications | ✅ | ✅ | ✅ | ✅ Complete |
| Logging | ✅ | ✅ | ✅ | ✅ Complete |
| Annotations | ✅ | ✅ | ✅ | ✅ Complete |
Features degrade gracefully when interacting with older servers.
> See the [changelog](CHANGELOG.md) for exact spec versions supported and any deprecations.
### Versioning Policy
This project follows [Semantic Versioning](https://semver.org/) for public APIs under `chuk_mcp.*`:
- **Major** (X.0.0): Breaking changes to public APIs
- **Minor** (0.X.0): New features, backward compatible
- **Patch** (0.0.X): Bug fixes, backward compatible
### Breaking Changes & Migration
#### v0.7.2: Exception Handling Changes
**What Changed**: `send_initialize()` and `send_initialize_with_client_tracking()` now **always raise exceptions** instead of returning `None` on errors.
**Why**: This enables proper error handling, automatic OAuth re-authentication in downstream tools (like mcp-cli), and follows Python best practices.
**Migration Guide**:
**Before (v0.7.1 and earlier)**:
```python
result = await send_initialize(read, write)
if result is None:
logging.error("Initialization failed")
return
# Use result
print(f"Connected to {result.serverInfo.name}")
```
**After (v0.7.2+)**:
```python
try:
result = await send_initialize(read, write)
# Success - result is guaranteed to be InitializeResult (not None)
print(f"Connected to {result.serverInfo.name}")
except RetryableError as e:
# Handle retryable errors (e.g., 401 authentication)
logging.error(f"Retryable error: {e}")
except VersionMismatchError as e:
# Handle version incompatibility
logging.error(f"Version mismatch: {e}")
except TimeoutError as e:
# Handle timeout
logging.error(f"Timeout: {e}")
except Exception as e:
# Handle other errors
logging.error(f"Error: {e}")
```
**Return Type Changes**:
- `send_initialize()`: `Optional[InitializeResult]` → `InitializeResult`
- `send_initialize_with_client_tracking()`: `Optional[InitializeResult]` → `InitializeResult`
**Benefits**:
- ✅ Automatic OAuth re-authentication in mcp-cli
- ✅ Proper error propagation and debugging
- ✅ Type safety (no `Optional` checks needed)
- ✅ Full exception context with stack traces
**See Also**:
- [`EXCEPTION_HANDLING_FIX.md`](EXCEPTION_HANDLING_FIX.md) - Detailed technical documentation
- [`examples/initialize_error_handling.py`](examples/initialize_error_handling.py) - Complete error handling examples
- [`tests/mcp/messages/test_initialize_exceptions.py`](tests/mcp/messages/test_initialize_exceptions.py) - Test suite
---
## Comparison with Official MCP SDK
| Feature | chuk-mcp | Official MCP Python SDK |
|---------|----------|------------------------|
| **Philosophy** | Protocol compliance library | Full framework |
| **Scope** | Client + Server, protocol-focused | Client + Server framework |
| **Typing** | Optional Pydantic (fallback available) | Pydantic required |
| **Transports** | stdio, SSE, Streamable HTTP (pluggable) | stdio, SSE, Streamable HTTP |
| **Browser/WASM** | Pyodide-compatible | Varies / not a primary target |
| **Dependencies** | Minimal (anyio core) | Heavier stack |
| **Server Framework** | Lightweight helpers | Opinionated server structure |
| **API Style** | Explicit send_* functions | Higher-level abstractions |
| **Target Use Case** | Protocol integration, custom clients/servers | Full MCP applications |
| **Orchestration** | External (you choose) | Built-in patterns |
| **Learning Curve** | Low (protocol-level) | Medium (framework concepts) |
**When to choose chuk-mcp:**
- Building custom MCP clients or servers
- Need transport flexibility (Streamable HTTP)
- Want minimal dependencies
- Prefer protocol-level control
- Running in constrained environments (WASM, edge functions)
- Need to integrate MCP into existing applications
> **Real-world example:** [chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server) uses chuk-mcp as its protocol compliance layer
**When to choose official SDK:**
- Building full MCP servers quickly with opinionated patterns
- Want framework abstractions out of the box
- Primarily using stdio transport
- Prefer higher-level APIs
---
## Design Goals & Non-Goals
**Goals**
* Be the **simplest way** to implement MCP in Python (client or server)
* Keep the API **small, explicit, and typed**
* Make **transports pluggable** and protocol logic reusable
* Support both **client and server** use cases with lightweight abstractions
**Non‑Goals**
* Competing with full agent frameworks / IDEs
* Baking in opinionated application structure or workflow engines
* Shipping heavyweight dependencies by default
* Providing high-level orchestration (that's your application layer)
---
## FAQ
**Q: Does this include a server framework?**
A: **Yes**, at the protocol layer. `chuk-mcp` provides typed messages and helpers usable on both clients and servers, but it's **not** an opinionated server framework—you bring your own app structure/orchestration.
**Q: Is Pydantic required?**
A: No. If installed (Pydantic v2 only), you'll get richer types and validation. If not, the library uses a lightweight fallback with dict-based models.
**Q: Which transport should I use?**
A: Use **stdio** for local dev and child processes. Use **Streamable HTTP** for remote servers behind TLS with auth.
**Q: Where can I find more examples?**
A: See the [`examples/`](examples/) directory for comprehensive demonstrations of all MCP features, including both quick-start examples and full end-to-end client-server pairs. For a real-world server implementation, see [chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server) which uses chuk-mcp as its protocol library.
**Q: How do I test my implementation?**
A: Run `make test` or `uv run pytest` to run the test suite. Use `make examples` (if present) to test all E2E examples. See the [Contributing](#contributing) section for details.
**Q: Is this production-ready?**
A: Yes. chuk-mcp is used in production environments. It includes error handling, type safety, and follows MCP protocol specifications. See the test coverage reports for confidence metrics.
**Q: Is it thread-safe?**
A: Client instances are not thread-safe across event loops. Share a client within a single async loop; use separate instances per loop/thread.
**Q: What's not included?**
A: Auth, TLS termination, persistence, and orchestration are app concerns—bring your own. chuk-mcp provides protocol compliance only. For browser/WASM frontends with CORS and TLS, terminate TLS at the proxy and set `Access-Control-Allow-Origin` to your frontend origin; avoid `*` with credentials.
**Q: How do I add retry logic and rate limiting?**
A: Use [chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor) which provides composable wrappers for retries (with exponential backoff), rate limiting, and caching. chuk-mcp focuses on protocol compliance; chuk-tool-processor handles execution concerns.
**Q: What are common errors and how do I handle them?**
A: Common exceptions and recommended actions:
| Error Type | JSON-RPC Code | Action |
|------------|---------------|--------|
| **Parse error** | -32700 | Fix JSON syntax in request |
| **Invalid request** | -32600 | Check required fields (jsonrpc, method, id) |
| **Method not found** | -32601 | Verify method name and server capabilities |
| **Invalid params** | -32602 | Validate parameter types and required fields |
| **Internal error** | -32603 | Check server logs, retry operation |
| **Authentication error (401)** | -32603 | Re-authenticate (automatic in mcp-cli) |
| **Request cancelled** | -32800 | Handle cancellation gracefully |
| **Content too large** | -32801 | Reduce payload size or use streaming |
| **Connection/Transport** | varies | Check network, verify server is running |
**Note:** When using HTTP-based transports (SSE or Streamable HTTP), transport-layer errors (network failures, TLS issues, authentication problems) will appear as HTTP status codes before reaching the MCP protocol layer. However, once the transport is established, all MCP protocol errors follow the JSON-RPC error code system shown above.
All protocol errors inherit from base exception classes and are **always raised** (never return `None`). See examples for error handling patterns.
**Exception Handling Best Practices:**
```python
from chuk_mcp.protocol.types.errors import (
RetryableError,
NonRetryableError,
VersionMismatchError
)
from chuk_mcp.protocol.messages import send_initialize
try:
# Initialize connection
result = await send_initialize(read, write)
# Success - result is guaranteed to be InitializeResult (not None)
print(f"Connected to {result.serverInfo.name}")
except VersionMismatchError as e:
# Protocol version incompatibility - cannot recover
logging.error(f"Version mismatch: {e}")
# Disconnect and inform user
except RetryableError as e:
# Retryable errors (e.g., 401 authentication failures)
if "401" in str(e).lower() or "unauthorized" in str(e).lower():
# Trigger OAuth re-authentication
# In mcp-cli, this happens automatically
logging.info("Re-authenticating...")
else:
# Other retryable errors - implement retry logic
logging.warning(f"Retryable error: {e}")
except TimeoutError as e:
# Server didn't respond in time
logging.error(f"Timeout: {e}")
# Retry with longer timeout or check server status
except NonRetryableError as e:
# Non-retryable errors - log and fail
logging.error(f"Fatal error: {e}")
except Exception as e:
# Other unexpected errors
logging.error(f"Unexpected error: {e}")
```
See [`examples/initialize_error_handling.py`](examples/initialize_error_handling.py) for comprehensive error handling demonstrations.
---
## Contributing
PRs welcome! Please:
1. Open a small, focused issue first (optional but helpful).
2. Add tests and type hints for new functionality.
3. Keep public APIs minimal and consistent.
4. Run the linters and test suite before submitting.
*PRs must maintain ≥85% coverage; enforced in CI along with `mypy` type checks and `ruff` linting.*
```bash
# Clone and setup
git clone https://github.com/chrishayuk/chuk-mcp
cd chuk-mcp
uv sync
# Install pre-commit hooks (optional)
pre-commit install
# Run examples
uv run python examples/quickstart_minimal.py
# Run tests
uv run pytest
# Type checking
uv run mypy src/chuk_mcp
# Or use the Makefile (if present)
make test
make typecheck
make lint
make examples
```
> **Bug reports / feature requests:** Issue templates available in `.github/`
> **Code of Conduct:** Contributors expected to follow the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)
### Security
If you believe you've found a security issue, please report it by opening a security advisory in the [GitHub repository](https://github.com/chrishayuk/chuk-mcp/security/advisories) rather than opening a public issue.
---
## Feature Showcase
This section provides detailed code snippets demonstrating MCP features. All examples are production-ready with full type safety.
### 🔧 Tools — Calling Functions
Tools are functions that AI can invoke:
```python
from chuk_mcp.protocol.messages.tools import send_tools_list, send_tools_call
from chuk_mcp.protocol.types.content import parse_content, TextContent
# List all available tools — returns typed ListToolsResult
tools_result = await send_tools_list(read, write)
print(f"📋 Available tools: {len(tools_result.tools)}")
for tool in tools_result.tools:
print(f" • {tool.name}: {tool.description}")
# Call a tool — returns typed ToolResult
result = await send_tools_call(
read, write,
name="greet",
arguments={"name": "World"}
)
# Parse content with type safety
content = parse_content(result.content[0])
assert isinstance(content, TextContent)
print(f"✅ Result: {content.text}")
```
**Full example:** `uv run python examples/e2e_tools_client.py`
### 📄 Resources — Reading Data
Resources provide access to data sources (files, databases, APIs):
```python
from chuk_mcp.protocol.messages.resources import send_resources_list, send_resources_read
# List available resources — returns typed ListResourcesResult
resources_result = await send_resources_list(read, write)
print(f"📚 Found {len(resources_result.resources)} resources")
for resource in resources_result.resources:
print(f" • {resource.name}")
print(f" URI: {resource.uri}")
# Read a resource — returns typed ReadResourceResult
if resources_result.resources:
uri = resources_result.resources[0].uri
read_result = await send_resources_read(read, write, uri)
for content in read_result.contents:
if hasattr(content, 'text'):
print(f"📖 Content: {content.text[:200]}...")
```
**Full example:** `uv run python examples/e2e_resources_client.py`
### 📡 Resource Subscriptions — Live Updates
Subscribe to resources for real-time change notifications:
```python
from chuk_mcp.protocol.messages.resources import (
send_resources_subscribe,
send_resources_unsubscribe
)
# Subscribe to a resource
uri = "file:///logs/app.log"
success = await send_resources_subscribe(read, write, uri)
if success:
print(f"✅ Subscribed to {uri}")
print("📡 Listening for changes...")
# In a real app, handle notifications in a loop
# Notifications arrive as messages from the server
# Unsubscribe when done
await send_resources_unsubscribe(read, write, uri)
print("🔕 Unsubscribed")
```
**Full example:** `uv run python examples/e2e_subscriptions_client.py`
### 💬 Prompts — Template Management
Prompts are reusable templates with parameters:
```python
from chuk_mcp.protocol.messages.prompts import send_prompts_list, send_prompts_get
# List available prompts — returns typed ListPromptsResult
prompts_result = await send_prompts_list(read, write)
print(f"💬 Available prompts: {len(prompts_result.prompts)}")
for prompt in prompts_result.prompts:
print(f" • {prompt.name}: {prompt.description}")
if hasattr(prompt, 'arguments') and prompt.arguments:
args = [a.name for a in prompt.arguments]
print(f" Arguments: {', '.join(args)}")
# Get a prompt with arguments — returns typed GetPromptResult
prompt_result = await send_prompts_get(
read, write,
name="code_review",
arguments={"file": "main.py", "language": "python"}
)
# Use the formatted messages
for message in prompt_result.messages:
print(f"🤖 {message.role}: {message.content}")
```
**Full example:** `uv run python examples/e2e_prompts_client.py`
### 🎯 Sampling — AI Content Generation
Let servers request AI to generate content on their behalf (requires user approval):
```python
from chuk_mcp.protocol.messages.sampling import sample_text
# Check if server supports sampling
if hasattr(init_result.capabilities, 'sampling'):
print("✅ Server supports sampling")
# Server requests AI to generate content using helper
result = await sample_text(
read, write,
prompt="Explain quantum computing in simple terms",
max_tokens=1000,
model_hint="claude",
temperature=0.7
)
# Access typed response
if hasattr(result.content, 'text'):
print(f"🤖 AI Generated: {result.content.text}")
print(f"📊 Model: {result.model}")
print(f"🔢 Stop Reason: {result.stopReason or 'N/A'}")
```
**Use Case:** Servers can use sampling to generate code, documentation, or analysis based on data they have access to.
**Full example:** `uv run python examples/e2e_sampling_client.py`
### 📁 Roots — Directory Access Control
Roots define which directories the client allows servers to access.
```python
from chuk_mcp.protocol.messages.roots import (
send_roots_list,
send_roots_list_changed_notification
)
# Check if server supports roots
if hasattr(init_result.capabilities, 'roots'):
print("✅ Server supports roots capability")
# List current roots — returns typed ListRootsResult
roots_result = await send_roots_list(read, write)
print(f"📁 Available roots: {len(roots_result.roots)}")
for root in roots_result.roots:
print(f" • {root.name}: {root.uri}")
# Notify server when roots change
await send_roots_list_changed_notification(write)
print("📢 Notified server of roots change")
```
**Use Case:** Control which directories AI can access, enabling secure sandboxed operations.
**Full example:** `uv run python examples/e2e_roots_client.py`
### 🎭 Elicitation — User Input Requests
Elicitation allows servers to request structured input from users:
```python
from chuk_mcp.protocol.messages.elicitation import send_elicitation_request
# Server requests user input
response = await send_elicitation_request(
read, write,
prompt="Enter API credentials",
fields=[
{"name": "api_key", "type": "text", "required": True},
{"name": "region", "type": "select", "options": ["us", "eu", "asia"]}
]
)
# Access user's input
print(f"User provided: {response.values}")
```
**Use Case:** Interactive workflows, OAuth flows, confirmation dialogs.
**Full example:** `uv run python examples/e2e_elicitation_client.py`
### 💡 Completion — Smart Autocomplete
Get intelligent suggestions for tool arguments:
```python
from chuk_mcp.protocol.messages.completions import (
send_completion_complete,
create_argument_info
)
# Get completions for a file path argument — returns typed CompletionResult
response = await send_completion_complete(
read, write,
ref={"type": "ref/resource", "uri": "file:///data/"},
argument=create_argument_info(
name="filename",
value="sales_202" # Partial input
)
)
# Show suggestions
print("💡 Suggestions for 'sales_202':")
for value in response.completion.values:
print(f" • {value}")
```
**Full example:** `uv run python examples/e2e_completion_client.py`
### 📊 Progress Tracking
Monitor long-running operations with progress updates:
```python
from chuk_mcp.protocol.messages.tools import send_tools_call
# Call a long-running tool
# Progress notifications will be sent automatically
print("🔄 Starting long operation...")
result = await send_tools_call(
read, write,
name="process_large_dataset",
arguments={"dataset": "sales_data.csv"}
)
print("✅ Operation complete")
# Progress notifications are handled automatically by the client
```
**Full example:** `uv run python examples/e2e_progress_client.py`
### 🚫 Cancellation
Cancel long-running operations with timeout:
```python
import anyio
from chuk_mcp.protocol.messages.cancellation import send_cancelled_notification
from chuk_mcp.protocol.messages.tools import send_tools_call
async def cancel_after_timeout():
request_id = "long-op-123"
async with anyio.create_task_group() as tg:
# Start long-running operation
tg.start_soon(send_tools_call, read, write, "process_large_dataset",
{"dataset": "big.csv"}, request_id)
# Cancel after 2 seconds
with anyio.move_on_after(2):
await anyio.sleep(999)
# Send cancellation
await send_cancelled_notification(write, request_id=request_id, reason="timeout")
print("🚫 Cancellation sent")
anyio.run(cancel_after_timeout)
```
**Full example:** `uv run python examples/e2e_cancellation_client.py`
### 🌐 Multiple Transports
Use different transport protocols for different scenarios:
```python
import anyio
from chuk_mcp.protocol.messages import send_initialize
from chuk_mcp import stdio_client, StdioServerParameters
from chuk_mcp.transports.http import http_client, HttpClientParameters
async def main():
# Stdio transport (local processes)
p1 = StdioServerParameters(
command="uvx",
args=["mcp-server-sqlite", "--db-path", "local.db"]
)
async with stdio_client(p1) as (r, w):
init = await send_initialize(r, w)
print("📡 Stdio:", init.serverInfo.name)
# Streamable HTTP transport (remote servers)
p2 = HttpClientParameters(url="http://localhost:8989/mcp")
async with http_client(p2) as (r, w):
init = await send_initialize(r, w)
print("🌐 Streamable HTTP:", init.serverInfo.name)
anyio.run(main)
```
### 🔄 Multi-Server Orchestration
Connect to multiple servers simultaneously:
```python
from chuk_mcp import stdio_client, StdioServerParameters
from chuk_mcp.protocol.messages import send_initialize
from chuk_mcp.protocol.messages.tools import send_tools_list
servers = [
StdioServerParameters(
command="uvx",
args=["mcp-server-sqlite", "--db-path", "db1.db"]
),
StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "."]
)
]
print("🔗 Connecting to multiple servers...")
for i, server_params in enumerate(servers, 1):
try:
async with stdio_client(server_params) as (read, write):
init_result = await send_initialize(read, write)
tools_result = await send_tools_list(read, write)
print(f"\n📡 Server {i}: {init_result.serverInfo.name}")
print(f" Tools: {len(tools_result.tools)}")
# Show first 3 tools
for tool in tools_result.tools[:3]:
print(f" • {tool.name}")
except Exception as e:
print(f"⚠️ Server {i} failed: {e}")
```
### Type Safety & Validation
All protocol messages return fully typed results using Pydantic (or fallback validation):
```python
from chuk_mcp.protocol.types.content import parse_content, TextContent
from chuk_mcp.protocol.messages.tools import send_tools_call
# Call a tool and get a typed result
tool_result = await send_tools_call(read, write, name="greet", arguments={"name": "World"})
# Type-safe content parsing
content = parse_content(tool_result.content[0])
assert isinstance(content, TextContent)
print(content.text)
```
**Benefits:**
* **Typed returns**: All `send_*` functions return typed Pydantic models
* **Content parsing**: Use `parse_content()` for type-safe content handling
* **Runtime validation**: Automatic validation with clear error messages
* **IDE support**: Full autocomplete and type checking
### Monitoring & Logging
Built-in features for production environments:
```python
from chuk_mcp.protocol.messages.logging import send_logging_set_level
# Set server logging level
await send_logging_set_level(write, level="debug")
```
**Features:**
* Structured logging with configurable levels
* Performance monitoring (latency, error rates, throughput)
* Progress tracking and cancellation support
* Clean error propagation (no automatic retries at protocol layer)
**Full example:** `uv run python examples/e2e_logging_client.py`
---
## Ecosystem
`chuk-mcp` is part of a modular suite of Python MCP tools:
* **[chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor)** — Reliable tool call execution with retries, caching, and exponential backoff
* **[chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server)** — Real-world MCP server implementation built on chuk-mcp
* **[chuk-mcp-cli](https://github.com/chrishayuk/chuk-mcp-cli)** — Interactive CLI and playground for testing MCP servers
Each component focuses on doing one thing well and can be used independently or together.
---
## License
MIT — see [LICENSE](LICENSE).
Raw data
{
"_id": null,
"home_page": null,
"name": "chuk-mcp",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "llm, openai, claude, mcp, client, protocol",
"author": null,
"author_email": "Chris Hay <chrishayuk@somejunkmailbox.com>",
"download_url": "https://files.pythonhosted.org/packages/36/31/48e2c2c0a9f8e0f83594ed0ce72b6b9ac2b803c41b158a0c9926019e4ba7/chuk_mcp-0.8.1.tar.gz",
"platform": null,
"description": "# chuk-mcp\n\n[](https://pypi.org/project/chuk-mcp)\n[](https://pypi.org/project/chuk-mcp)\n[](https://pypi.org/project/chuk-mcp)\n[](https://github.com/chrishayuk/chuk-mcp/actions)\n[](https://codecov.io/gh/chrishayuk/chuk-mcp)\n[](https://github.com/astral-sh/ruff)\n[](LICENSE)\n\n**A lean, production-minded Python implementation of the Model Context Protocol (MCP).**\n\n**Brings first-class MCP protocol support to Python \u2014 lightweight, async, and spec-accurate from day one.**\n\n**Requires Python 3.11+**\n\n`chuk-mcp` gives you a clean, typed, transport-agnostic implementation for both **MCP clients and servers**. It focuses on the protocol surface (messages, types, versioning, transports) and leaves orchestration, UIs, and agent frameworks to other layers.\n\n> \u2733\ufe0f **What this is**: a **protocol compliance library** with ergonomic helpers for clients and servers.\n>\n> \u26d4 **What this isn't**: a chatbot runtime, workflow engine, or an opinionated application framework.\n\n## Architecture: Where chuk-mcp Fits\n\n### Stack Overview\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Your AI Application \u2502\n\u2502 (Claude, GPT, custom agents) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502 MCP Protocol\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 chuk-mcp Client \u2502 \u2190 You are here\n\u2502 \u2022 Protocol compliance \u2502\n\u2502 \u2022 Transport (stdio/Streamable HTTP)\u2502\n\u2502 \u2022 Type-safe messages \u2502\n\u2502 \u2022 Capability negotiation \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502 MCP Protocol\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 chuk-mcp Server (optional) \u2502\n\u2502 \u2022 Protocol handlers \u2502\n\u2502 \u2022 Tool/Resource registration \u2502\n\u2502 \u2022 Session management \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Your Tools & Resources \u2502\n\u2502 (databases, APIs, files, etc) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**chuk-mcp provides the protocol layer** \u2014 connect AI applications to tools and data sources using the standard MCP protocol.\n\n### Internal Architecture\n\nThe library itself is organized in layers that you can use at different levels of abstraction:\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 CLI & Demo Layer \u2502 __main__.py, demos/\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Client/Server API \u2502 High-level abstractions\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Protocol Layer \u2502 Messages, types, features\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Transport Layer \u2502 stdio, Streamable HTTP\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Base Layer \u2502 Pydantic fallback, config\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**Layer Details:**\n\n| Layer | Purpose | Usage |\n|-------|---------|-------|\n| **CLI & Demo** | Built-in utilities and demonstrations | Optional \u2014 use protocol layer directly |\n| **Client/Server API** | High-level abstractions for client-server interactions | Optional \u2014 can use protocol layer directly |\n| **Protocol Layer** | Message definitions, type-safe request/response handling, capability negotiation | Core \u2014 implements MCP spec |\n| **Transport Layer** | Pluggable transport implementations (stdio, Streamable HTTP) | Choose based on deployment |\n| **Base Layer** | Pydantic fallback, shared config, type adapters | Foundation \u2014 automatic |\n\nMost users work with the **Protocol Layer** (`send_*` functions) and **Transport Layer** (stdio/HTTP clients), optionally using the **Client/Server API** for higher-level abstractions.\n\n---\n\n## Table of Contents\n\n* [Why chuk\u2011mcp?](#why-chuk-mcp)\n* [At a Glance](#at-a-glance)\n* [Install](#install)\n* [Quick Start](#quick-start)\n* [Core Concepts](#core-concepts)\n * [Tools](#tools)\n * [Resources](#resources)\n * [Prompts](#prompts)\n * [Roots (optional)](#roots-optional)\n * [Sampling & Completion (optional)](#sampling--completion-optional)\n* [Transports](#transports)\n* [Configuration Examples](#configuration-examples)\n* [Examples & Feature Demonstrations](#examples--feature-demonstrations)\n* [Versioning & Compatibility](#versioning--compatibility)\n* [Comparison with Official MCP SDK](#comparison-with-official-mcp-sdk)\n* [Design Goals & Non\u2011Goals](#design-goals--non-goals)\n* [FAQ](#faq)\n* [Contributing](#contributing)\n* [Feature Showcase](#feature-showcase)\n* [Ecosystem](#ecosystem)\n* [License](#license)\n\n---\n\n## Why chuk-mcp?\n\n* **Protocol-first**: Focuses on MCP messages, types, and capability negotiation \u2014 [spec.modelcontextprotocol.io](https://spec.modelcontextprotocol.io)\n* **Client + Server**: Full support for building both MCP clients and servers\n* **Typed**: Full type hints; optional Pydantic models when available\n* **Transport-agnostic**: stdio by default, Streamable HTTP (NDJSON) for remote servers, easily extensible\n* **Async-first**: Built on AnyIO; integrate with `anyio.run(...)` or your existing loop\n* **Small & focused**: No heavy orchestration or agent assumptions\n* **Clean protocol layer**: Errors fail fast without retries \u2014 bring your own error handling strategy\n* **Production-minded**: Clear errors, structured logging hooks, composable with retry/caching layers\n\n---\n\n## At a Glance\n\n**Try it now:**\n\n```bash\n# Install an example MCP server\nuv tool install mcp-server-sqlite\n\n# Run the quick-start example\nuv run python examples/quickstart_sqlite.py\n```\n\n### Hello World\n\nA minimal working MCP server in ~10 lines:\n\n```python\n# hello_mcp.py\nimport anyio\nfrom chuk_mcp.server import MCPServer, run_stdio_server\nfrom chuk_mcp.protocol.types import ServerCapabilities, ToolCapabilities\n\nasync def main():\n server = MCPServer(\"hello\", \"1.0\", ServerCapabilities(tools=ToolCapabilities()))\n\n async def handle_tools_list(message, session_id):\n return server.protocol_handler.create_response(\n message.id,\n {\"tools\": [{\"name\": \"hello\", \"description\": \"Say hi\", \"inputSchema\": {\"type\": \"object\"}}]}\n ), None\n\n server.protocol_handler.register_method(\"tools/list\", handle_tools_list)\n await run_stdio_server(server)\n\nanyio.run(main)\n```\n\n**Run it:** `uv run python hello_mcp.py` \u2014 or connect any MCP client via stdio!\n\n---\n\n**Stdio (local processes):**\n\n```python\n# Connect to an MCP server via stdio and list tools\nimport anyio\nfrom chuk_mcp import StdioServerParameters, stdio_client\nfrom chuk_mcp.protocol.messages import send_initialize\nfrom chuk_mcp.protocol.messages.tools import send_tools_list\n\nasync def main():\n params = StdioServerParameters(command=\"uvx\", args=[\"mcp-server-sqlite\", \"--db-path\", \"example.db\"])\n async with stdio_client(params) as (read, write):\n init = await send_initialize(read, write)\n tools = await send_tools_list(read, write)\n print(\"Server:\", init.serverInfo.name)\n print(\"Tools:\", [t.name for t in tools.tools])\n\nanyio.run(main)\n```\n\n**Streamable HTTP (remote servers):**\n\n```python\n# Local dev (plain HTTP)\nimport anyio\nfrom chuk_mcp.transports.http import http_client, HttpClientParameters\nfrom chuk_mcp.protocol.messages import send_initialize\n\nasync def main():\n params = HttpClientParameters(\n url=\"http://localhost:8989/mcp\",\n timeout_s=30,\n headers={\"Authorization\": \"Bearer <token>\"}\n )\n async with http_client(params) as (read, write):\n init = await send_initialize(read, write)\n print(\"Connected:\", init.serverInfo.name)\n\nanyio.run(main)\n\n# Production (TLS)\nasync def main_secure():\n params = HttpClientParameters(\n url=\"https://mcp.example.com/mcp\",\n timeout_s=30,\n headers={\"Authorization\": \"Bearer <token>\"}\n )\n async with http_client(params) as (read, write):\n init = await send_initialize(read, write)\n print(\"Connected:\", init.serverInfo.name)\n\nanyio.run(main_secure)\n```\n\n---\n\n## Install\n\n### With `uv` (recommended)\n\n```bash\nuv add chuk-mcp # core (Python 3.11+ required)\nuv add \"chuk-mcp[pydantic]\" # add typed Pydantic models (Pydantic v2 only)\nuv add \"chuk-mcp[http]\" # add Streamable HTTP transport extras\nuv add \"chuk-mcp[pydantic,http]\" # full install with all features\n```\n\n### With `pip`\n\n```bash\npip install \"chuk-mcp\"\npip install \"chuk-mcp[pydantic]\" # Pydantic v2 only\npip install \"chuk-mcp[http]\" # httpx>=0.28 for Streamable HTTP\npip install \"chuk-mcp[pydantic,http]\" # full install\n```\n\n> *(Requires `pydantic>=2.11.1,<3` and `httpx>=0.28.1,<1` for `[pydantic]` and `[http]` extras.)*\n\n**Python versions:** Requires Python 3.11+; see badge for tested versions.\n\nVerify:\n\n```bash\npython -c \"import chuk_mcp; print('\u2705 chuk-mcp ready')\"\n```\n\n---\n\n## Quick Start\n\n### Minimal initialize (inline demo server)\n\n```python\nimport anyio\nfrom chuk_mcp import StdioServerParameters, stdio_client\nfrom chuk_mcp.protocol.messages import send_initialize\n\nasync def main():\n params = StdioServerParameters(\n command=\"python\",\n args=[\"-c\", \"import json,sys; req=json.loads(sys.stdin.readline()); print(json.dumps({\\\"id\\\":req['id'],\\\"result\\\":{\\\"serverInfo\\\":{\\\"name\\\":\\\"Demo\\\",\\\"version\\\":\\\"1.0\\\"},\\\"protocolVersion\\\":\\\"<negotiated-by-client>\\\",\\\"capabilities\\\":{}}}))\"]\n )\n async with stdio_client(params) as (read, write):\n res = await send_initialize(read, write)\n print(\"Connected:\", res.serverInfo.name)\n\nanyio.run(main)\n```\n\n> **Note:** Protocol version is negotiated during `initialize`; avoid hard-coding.\n\n> **Windows users:** Windows cmd/PowerShell may buffer stdio differently. Use `uv run` or WSL for local development if you encounter deadlocks.\n\n**Run it:**\n\n```bash\nuv run python examples/quickstart_minimal.py\n```\n\n### Real server (SQLite example with capability check)\n\n```python\nimport anyio\nfrom chuk_mcp import StdioServerParameters, stdio_client\nfrom chuk_mcp.protocol.messages import send_initialize\nfrom chuk_mcp.protocol.messages.tools import send_tools_call, send_tools_list\n\nasync def main():\n params = StdioServerParameters(command=\"uvx\", args=[\"mcp-server-sqlite\", \"--db-path\", \"example.db\"])\n async with stdio_client(params) as (read, write):\n # Initialize and check capabilities\n init = await send_initialize(read, write)\n\n # Capability-gated behavior\n if hasattr(init.capabilities, 'tools'):\n tools = await send_tools_list(read, write)\n print(\"Tools:\", [t.name for t in tools.tools])\n result = await send_tools_call(read, write, name=\"read_query\", arguments={\"query\": \"SELECT 1 as x\"})\n print(\"Result:\", result.content)\n else:\n print(\"Server does not support tools\")\n\nanyio.run(main)\n```\n\n**Run it:**\n\n```bash\n# Install SQLite server\nuv tool install mcp-server-sqlite\n\n# Run example\nuv run python examples/quickstart_sqlite.py\n```\n\n### Minimal server (protocol layer)\n\nBuild your own MCP server using the same protocol layer. See [`examples/e2e_*_server.py`](examples/) for complete working servers:\n\n```python\n# Conceptual example \u2014 for a runnable server, see examples/e2e_*_server.py\nimport anyio\nfrom chuk_mcp.server import MCPServer, run_stdio_server\nfrom chuk_mcp.protocol.types import ServerCapabilities, ToolCapabilities\n\nasync def main():\n server = MCPServer(\n name=\"demo-server\",\n version=\"0.1.0\",\n capabilities=ServerCapabilities(tools=ToolCapabilities())\n )\n\n # Register handlers using the protocol layer\n async def handle_tools_list(message, session_id):\n # Return (response, notifications). Second value is reserved for\n # optional out-of-band notifications; use None if not sending any.\n return server.protocol_handler.create_response(\n message.id,\n {\"tools\": [{\n \"name\": \"greet\",\n \"description\": \"Say hello\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\"name\": {\"type\": \"string\"}},\n \"required\": [\"name\"]\n }\n }]}\n ), None\n\n server.protocol_handler.register_method(\"tools/list\", handle_tools_list)\n await run_stdio_server(server)\n\nanyio.run(main)\n```\n\n**Pair it with a client:**\n\n```bash\n# See examples/ for complete client-server pairs\nuv run python examples/e2e_tools_client.py\n```\n\n> The examples above use stdio. Swap the transport to talk to remote servers (see **Transports**).\n\n---\n\n## Core Concepts\n\n### Tools\n\nDiscover and call server-exposed functions.\n\n```python\nfrom chuk_mcp.protocol.messages.tools import send_tools_list, send_tools_call\n\n# list\ntools = await send_tools_list(read, write)\nfor t in tools.tools:\n print(t.name, \"-\", t.description)\n\n# call\ncall = await send_tools_call(read, write, name=\"greet\", arguments={\"name\": \"World\"})\nprint(call.content)\n```\n\n**See full example:** [`examples/e2e_tools_client.py`](examples/e2e_tools_client.py)\n\n### Resources\n\nList/read (and optionally subscribe to) data sources.\n\n```python\nfrom chuk_mcp.protocol.messages.resources import send_resources_list, send_resources_read\n\nresources = await send_resources_list(read, write)\nif resources.resources:\n uri = resources.resources[0].uri\n data = await send_resources_read(read, write, uri)\n print(data.contents)\n```\n\n**See full examples:**\n\n* [`examples/e2e_resources_client.py`](examples/e2e_resources_client.py)\n* [`examples/e2e_subscriptions_client.py`](examples/e2e_subscriptions_client.py)\n\n### Prompts\n\nParameterized, reusable prompt templates.\n\n```python\nfrom chuk_mcp.protocol.messages.prompts import send_prompts_list, send_prompts_get\n\nprompts = await send_prompts_list(read, write)\nif prompts.prompts:\n got = await send_prompts_get(read, write, name=prompts.prompts[0].name, arguments={})\n for m in got.messages:\n print(m.role, m.content)\n```\n\n**See full example:** [`examples/e2e_prompts_client.py`](examples/e2e_prompts_client.py)\n\n### Roots (optional)\n\nAdvertise directories the client authorizes the server to access.\n\n```python\nfrom chuk_mcp.protocol.messages.roots import send_roots_list\nroots = await send_roots_list(read, write) # if supported\n```\n\n**See full example:** [`examples/e2e_roots_client.py`](examples/e2e_roots_client.py)\n\n### Sampling & Completion (optional)\n\nSome servers can ask the client to sample text or provide completion for arguments. These are opt-in and capability-gated.\n\n**See full examples:**\n\n* [`examples/e2e_sampling_client.py`](examples/e2e_sampling_client.py)\n* [`examples/e2e_completion_client.py`](examples/e2e_completion_client.py)\n\n---\n\n## Transports\n\n`chuk-mcp` cleanly separates **protocol** from **transport**, so you can use the same protocol handlers with any transport layer:\n\n* **Stdio** \u2014 ideal for local child-process servers\n* **Streamable HTTP** \u2014 speak to remote servers over HTTP (chunked/NDJSON)\n* **Extensible** \u2014 implement your own transport by adapting the simple `(read, write)` async interface\n\n> **Note:** chuk-mcp is fully async (AnyIO). Use `anyio.run(...)` or integrate into your event loop.\n\n> **Note:** Protocol **capabilities** are negotiated during `initialize`, independent of transport. You choose the transport (stdio or Streamable HTTP) based on deployment/runtime needs.\n\n> **Thread-safety:** Client instances are not thread-safe across event loops. See [FAQ](#is-it-thread-safe) for details.\n\n> **Streamable HTTP** uses chunked NDJSON. Configure `HttpClientParameters(timeout_s=30, headers={\"Authorization\": \"Bearer ...\"})`. Clients stream NDJSON with backpressure. For large payloads, prefer NDJSON chunks over base64 blobs to avoid memory spikes.\n\n> **Framing:** Streamable HTTP uses NDJSON (one JSON object per line). Servers should flush after each object; proxies must not buffer indefinitely.\n\n> **Compression:** Enable gzip at the proxy to reduce large content streams. MCP payloads compress well.\n\n> **Protocol Layer Design:** The protocol layer is intentionally **clean and minimal** \u2014 errors are raised immediately without retries. This design keeps the protocol layer focused on message transport and compliance with the MCP specification. For production use cases requiring retry logic, error handling, rate limiting, or caching, use [chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor) which provides composable wrappers for retries with exponential backoff, rate limiting, and caching. This separation of concerns allows you to choose the right retry strategy for your specific application needs.\n\n> **Security:** When exposing Streamable HTTP, terminate TLS at a proxy and require auth (e.g., bearer tokens). For private CAs, configure your client's trust store (e.g., `SSL_CERT_FILE=/path/ca.pem`, `REQUESTS_CA_BUNDLE`, or `SSL_CERT_DIR`). The protocol layer is transport-agnostic and does not impose auth.\n\n---\n\n## Configuration Examples\n\n### JSON config (client decides how to spawn/connect)\n\n```json\n{\n \"mcpServers\": {\n \"sqlite\": {\n \"command\": \"uvx\",\n \"args\": [\"mcp-server-sqlite\", \"--db-path\", \"database.db\"]\n },\n \"filesystem\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"]\n }\n }\n}\n```\n\n### Loading config in code\n\n```python\nfrom chuk_mcp import StdioServerParameters, stdio_client\nfrom chuk_mcp.protocol.messages import send_initialize\n\nparams = StdioServerParameters(command=\"uvx\", args=[\"mcp-server-sqlite\", \"--db-path\", \"database.db\"])\nasync with stdio_client(params) as (read, write):\n init = await send_initialize(read, write)\n print(\"Connected to\", init.serverInfo.name)\n```\n\n---\n\n## Examples & Feature Demonstrations\n\nThe [`examples/`](examples/) directory contains comprehensive, working demonstrations of all MCP features:\n\n### Quick Start Examples\n\n* [`quickstart_minimal.py`](examples/quickstart_minimal.py) \u2014 Minimal MCP client setup\n* [`quickstart_sqlite.py`](examples/quickstart_sqlite.py) \u2014 Working with SQLite MCP server\n* [`quickstart_resources.py`](examples/quickstart_resources.py) \u2014 Accessing server resources\n* [`quickstart_complete.py`](examples/quickstart_complete.py) \u2014 Multi-feature demo\n\n### End-to-End (E2E) Examples\n\nComplete **client-server pairs** built with pure chuk-mcp, demonstrating both client and server implementation for each MCP feature:\n\n**Core Features:**\n\n* [`e2e_tools_client.py`](examples/e2e_tools_client.py) \u2014 Tool registration, discovery, and invocation\n* [`e2e_resources_client.py`](examples/e2e_resources_client.py) \u2014 Resource listing and reading\n* [`e2e_prompts_client.py`](examples/e2e_prompts_client.py) \u2014 Reusable prompt templates\n\n**Advanced Features:**\n\n* [`e2e_roots_client.py`](examples/e2e_roots_client.py) \u2014 File system root management\n* [`e2e_sampling_client.py`](examples/e2e_sampling_client.py) \u2014 Server-initiated LLM requests\n* [`e2e_completion_client.py`](examples/e2e_completion_client.py) \u2014 Autocomplete functionality\n* [`e2e_subscriptions_client.py`](examples/e2e_subscriptions_client.py) \u2014 Resource change notifications\n* [`e2e_cancellation_client.py`](examples/e2e_cancellation_client.py) \u2014 Operation cancellation\n* [`e2e_progress_client.py`](examples/e2e_progress_client.py) \u2014 Progress tracking\n* [`e2e_logging_client.py`](examples/e2e_logging_client.py) \u2014 Log message handling\n* [`e2e_elicitation_client.py`](examples/e2e_elicitation_client.py) \u2014 User input requests\n* [`e2e_annotations_client.py`](examples/e2e_annotations_client.py) \u2014 Content metadata\n\n**Error Handling:**\n\n* [`initialize_error_handling.py`](examples/initialize_error_handling.py) \u2014 Comprehensive error handling patterns (OAuth 401, version mismatch, timeout, etc.)\n\n**Running Examples:**\n\nMany E2E examples are self-contained with their own **protocol-level server** built using pure chuk-mcp. Where relevant, the client starts the corresponding demo server:\n\n```bash\n# Run any example directly - the client will start its server\nuv run python examples/e2e_tools_client.py\n\n# Test all E2E examples\nfor example in examples/e2e_*_client.py; do\n echo \"Testing $example\"\n uv run python \"$example\" || exit 1\ndone\n```\n\n> **Note:** Where relevant, examples include a corresponding `e2e_*_server.py` showing a minimal server built with the same protocol layer.\n\nSee [`examples/README.md`](examples/README.md) for detailed documentation of all examples.\n\n---\n\n## Versioning & Compatibility\n\n* `chuk-mcp` follows the MCP spec revisions and negotiates capabilities at **initialize**.\n* Newer features are **capability-gated** and degrade gracefully with older servers.\n* Optional typing/validation uses **Pydantic if available**, otherwise a lightweight fallback.\n\n### \ud83d\udccb Supported Protocol Versions (as of v0.1.x)\n\n| Version | Status | Support Policy |\n|---------|--------|----------------|\n| `2025-06-18` | Latest | Primary support, all features |\n| `2025-03-26` | Stable | Full compatibility, maintained |\n| `2024-11-05` | Legacy | Backward compatibility, deprecation TBD |\n\n**Tested Platforms:** Linux, macOS, Windows (Python 3.11+)\n\n> **Support Policy:** Latest and Stable versions receive full support. Legacy version support will be maintained until 2026-Q2, after which it may be deprecated. See [changelog](CHANGELOG.md) for migration guidance.\n\n### \ud83d\udcca Client Feature Support Matrix\n\n| Feature Category | 2024-11-05 | 2025-03-26 | 2025-06-18 | Implementation Status |\n|-----------------|------------|------------|------------|---------------------|\n| **Core Operations** | | | | |\n| Tools (list/call) | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Resources (list/read/subscribe) | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Prompts (list/get) | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| **Transport** | | | | |\n| Stdio | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Streamable HTTP | \u2013 | \u2705 | \u2705 | \u2705 Complete |\n| **Advanced Features** | | | | |\n| Sampling | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Completion | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Roots | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Elicitation | \u274c | \u274c | \u2705 | \u2705 Complete |\n| **Quality Features** | | | | |\n| Progress Tracking | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Cancellation | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Notifications | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Logging | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n| Annotations | \u2705 | \u2705 | \u2705 | \u2705 Complete |\n\nFeatures degrade gracefully when interacting with older servers.\n\n> See the [changelog](CHANGELOG.md) for exact spec versions supported and any deprecations.\n\n### Versioning Policy\n\nThis project follows [Semantic Versioning](https://semver.org/) for public APIs under `chuk_mcp.*`:\n- **Major** (X.0.0): Breaking changes to public APIs\n- **Minor** (0.X.0): New features, backward compatible\n- **Patch** (0.0.X): Bug fixes, backward compatible\n\n### Breaking Changes & Migration\n\n#### v0.7.2: Exception Handling Changes\n\n**What Changed**: `send_initialize()` and `send_initialize_with_client_tracking()` now **always raise exceptions** instead of returning `None` on errors.\n\n**Why**: This enables proper error handling, automatic OAuth re-authentication in downstream tools (like mcp-cli), and follows Python best practices.\n\n**Migration Guide**:\n\n**Before (v0.7.1 and earlier)**:\n```python\nresult = await send_initialize(read, write)\nif result is None:\n logging.error(\"Initialization failed\")\n return\n# Use result\nprint(f\"Connected to {result.serverInfo.name}\")\n```\n\n**After (v0.7.2+)**:\n```python\ntry:\n result = await send_initialize(read, write)\n # Success - result is guaranteed to be InitializeResult (not None)\n print(f\"Connected to {result.serverInfo.name}\")\nexcept RetryableError as e:\n # Handle retryable errors (e.g., 401 authentication)\n logging.error(f\"Retryable error: {e}\")\nexcept VersionMismatchError as e:\n # Handle version incompatibility\n logging.error(f\"Version mismatch: {e}\")\nexcept TimeoutError as e:\n # Handle timeout\n logging.error(f\"Timeout: {e}\")\nexcept Exception as e:\n # Handle other errors\n logging.error(f\"Error: {e}\")\n```\n\n**Return Type Changes**:\n- `send_initialize()`: `Optional[InitializeResult]` \u2192 `InitializeResult`\n- `send_initialize_with_client_tracking()`: `Optional[InitializeResult]` \u2192 `InitializeResult`\n\n**Benefits**:\n- \u2705 Automatic OAuth re-authentication in mcp-cli\n- \u2705 Proper error propagation and debugging\n- \u2705 Type safety (no `Optional` checks needed)\n- \u2705 Full exception context with stack traces\n\n**See Also**:\n- [`EXCEPTION_HANDLING_FIX.md`](EXCEPTION_HANDLING_FIX.md) - Detailed technical documentation\n- [`examples/initialize_error_handling.py`](examples/initialize_error_handling.py) - Complete error handling examples\n- [`tests/mcp/messages/test_initialize_exceptions.py`](tests/mcp/messages/test_initialize_exceptions.py) - Test suite\n\n---\n\n## Comparison with Official MCP SDK\n\n| Feature | chuk-mcp | Official MCP Python SDK |\n|---------|----------|------------------------|\n| **Philosophy** | Protocol compliance library | Full framework |\n| **Scope** | Client + Server, protocol-focused | Client + Server framework |\n| **Typing** | Optional Pydantic (fallback available) | Pydantic required |\n| **Transports** | stdio, SSE, Streamable HTTP (pluggable) | stdio, SSE, Streamable HTTP |\n| **Browser/WASM** | Pyodide-compatible | Varies / not a primary target |\n| **Dependencies** | Minimal (anyio core) | Heavier stack |\n| **Server Framework** | Lightweight helpers | Opinionated server structure |\n| **API Style** | Explicit send_* functions | Higher-level abstractions |\n| **Target Use Case** | Protocol integration, custom clients/servers | Full MCP applications |\n| **Orchestration** | External (you choose) | Built-in patterns |\n| **Learning Curve** | Low (protocol-level) | Medium (framework concepts) |\n\n**When to choose chuk-mcp:**\n- Building custom MCP clients or servers\n- Need transport flexibility (Streamable HTTP)\n- Want minimal dependencies\n- Prefer protocol-level control\n- Running in constrained environments (WASM, edge functions)\n- Need to integrate MCP into existing applications\n\n> **Real-world example:** [chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server) uses chuk-mcp as its protocol compliance layer\n\n**When to choose official SDK:**\n- Building full MCP servers quickly with opinionated patterns\n- Want framework abstractions out of the box\n- Primarily using stdio transport\n- Prefer higher-level APIs\n\n---\n\n## Design Goals & Non-Goals\n\n**Goals**\n\n* Be the **simplest way** to implement MCP in Python (client or server)\n* Keep the API **small, explicit, and typed**\n* Make **transports pluggable** and protocol logic reusable\n* Support both **client and server** use cases with lightweight abstractions\n\n**Non\u2011Goals**\n\n* Competing with full agent frameworks / IDEs\n* Baking in opinionated application structure or workflow engines\n* Shipping heavyweight dependencies by default\n* Providing high-level orchestration (that's your application layer)\n\n---\n\n## FAQ\n\n**Q: Does this include a server framework?**\n\nA: **Yes**, at the protocol layer. `chuk-mcp` provides typed messages and helpers usable on both clients and servers, but it's **not** an opinionated server framework\u2014you bring your own app structure/orchestration.\n\n**Q: Is Pydantic required?**\n\nA: No. If installed (Pydantic v2 only), you'll get richer types and validation. If not, the library uses a lightweight fallback with dict-based models.\n\n**Q: Which transport should I use?**\n\nA: Use **stdio** for local dev and child processes. Use **Streamable HTTP** for remote servers behind TLS with auth.\n\n**Q: Where can I find more examples?**\n\nA: See the [`examples/`](examples/) directory for comprehensive demonstrations of all MCP features, including both quick-start examples and full end-to-end client-server pairs. For a real-world server implementation, see [chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server) which uses chuk-mcp as its protocol library.\n\n**Q: How do I test my implementation?**\n\nA: Run `make test` or `uv run pytest` to run the test suite. Use `make examples` (if present) to test all E2E examples. See the [Contributing](#contributing) section for details.\n\n**Q: Is this production-ready?**\n\nA: Yes. chuk-mcp is used in production environments. It includes error handling, type safety, and follows MCP protocol specifications. See the test coverage reports for confidence metrics.\n\n**Q: Is it thread-safe?**\n\nA: Client instances are not thread-safe across event loops. Share a client within a single async loop; use separate instances per loop/thread.\n\n**Q: What's not included?**\n\nA: Auth, TLS termination, persistence, and orchestration are app concerns\u2014bring your own. chuk-mcp provides protocol compliance only. For browser/WASM frontends with CORS and TLS, terminate TLS at the proxy and set `Access-Control-Allow-Origin` to your frontend origin; avoid `*` with credentials.\n\n**Q: How do I add retry logic and rate limiting?**\n\nA: Use [chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor) which provides composable wrappers for retries (with exponential backoff), rate limiting, and caching. chuk-mcp focuses on protocol compliance; chuk-tool-processor handles execution concerns.\n\n**Q: What are common errors and how do I handle them?**\n\nA: Common exceptions and recommended actions:\n\n| Error Type | JSON-RPC Code | Action |\n|------------|---------------|--------|\n| **Parse error** | -32700 | Fix JSON syntax in request |\n| **Invalid request** | -32600 | Check required fields (jsonrpc, method, id) |\n| **Method not found** | -32601 | Verify method name and server capabilities |\n| **Invalid params** | -32602 | Validate parameter types and required fields |\n| **Internal error** | -32603 | Check server logs, retry operation |\n| **Authentication error (401)** | -32603 | Re-authenticate (automatic in mcp-cli) |\n| **Request cancelled** | -32800 | Handle cancellation gracefully |\n| **Content too large** | -32801 | Reduce payload size or use streaming |\n| **Connection/Transport** | varies | Check network, verify server is running |\n\n**Note:** When using HTTP-based transports (SSE or Streamable HTTP), transport-layer errors (network failures, TLS issues, authentication problems) will appear as HTTP status codes before reaching the MCP protocol layer. However, once the transport is established, all MCP protocol errors follow the JSON-RPC error code system shown above.\n\nAll protocol errors inherit from base exception classes and are **always raised** (never return `None`). See examples for error handling patterns.\n\n**Exception Handling Best Practices:**\n\n```python\nfrom chuk_mcp.protocol.types.errors import (\n RetryableError,\n NonRetryableError,\n VersionMismatchError\n)\nfrom chuk_mcp.protocol.messages import send_initialize\n\ntry:\n # Initialize connection\n result = await send_initialize(read, write)\n # Success - result is guaranteed to be InitializeResult (not None)\n print(f\"Connected to {result.serverInfo.name}\")\n\nexcept VersionMismatchError as e:\n # Protocol version incompatibility - cannot recover\n logging.error(f\"Version mismatch: {e}\")\n # Disconnect and inform user\n\nexcept RetryableError as e:\n # Retryable errors (e.g., 401 authentication failures)\n if \"401\" in str(e).lower() or \"unauthorized\" in str(e).lower():\n # Trigger OAuth re-authentication\n # In mcp-cli, this happens automatically\n logging.info(\"Re-authenticating...\")\n else:\n # Other retryable errors - implement retry logic\n logging.warning(f\"Retryable error: {e}\")\n\nexcept TimeoutError as e:\n # Server didn't respond in time\n logging.error(f\"Timeout: {e}\")\n # Retry with longer timeout or check server status\n\nexcept NonRetryableError as e:\n # Non-retryable errors - log and fail\n logging.error(f\"Fatal error: {e}\")\n\nexcept Exception as e:\n # Other unexpected errors\n logging.error(f\"Unexpected error: {e}\")\n```\n\nSee [`examples/initialize_error_handling.py`](examples/initialize_error_handling.py) for comprehensive error handling demonstrations.\n\n---\n\n## Contributing\n\nPRs welcome! Please:\n\n1. Open a small, focused issue first (optional but helpful).\n2. Add tests and type hints for new functionality.\n3. Keep public APIs minimal and consistent.\n4. Run the linters and test suite before submitting.\n\n*PRs must maintain \u226585% coverage; enforced in CI along with `mypy` type checks and `ruff` linting.*\n\n```bash\n# Clone and setup\ngit clone https://github.com/chrishayuk/chuk-mcp\ncd chuk-mcp\nuv sync\n\n# Install pre-commit hooks (optional)\npre-commit install\n\n# Run examples\nuv run python examples/quickstart_minimal.py\n\n# Run tests\nuv run pytest\n\n# Type checking\nuv run mypy src/chuk_mcp\n\n# Or use the Makefile (if present)\nmake test\nmake typecheck\nmake lint\nmake examples\n```\n\n> **Bug reports / feature requests:** Issue templates available in `.github/`\n\n> **Code of Conduct:** Contributors expected to follow the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)\n\n### Security\n\nIf you believe you've found a security issue, please report it by opening a security advisory in the [GitHub repository](https://github.com/chrishayuk/chuk-mcp/security/advisories) rather than opening a public issue.\n\n---\n\n## Feature Showcase\n\nThis section provides detailed code snippets demonstrating MCP features. All examples are production-ready with full type safety.\n\n### \ud83d\udd27 Tools \u2014 Calling Functions\n\nTools are functions that AI can invoke:\n\n```python\nfrom chuk_mcp.protocol.messages.tools import send_tools_list, send_tools_call\nfrom chuk_mcp.protocol.types.content import parse_content, TextContent\n\n# List all available tools \u2014 returns typed ListToolsResult\ntools_result = await send_tools_list(read, write)\nprint(f\"\ud83d\udccb Available tools: {len(tools_result.tools)}\")\n\nfor tool in tools_result.tools:\n print(f\" \u2022 {tool.name}: {tool.description}\")\n\n# Call a tool \u2014 returns typed ToolResult\nresult = await send_tools_call(\n read, write,\n name=\"greet\",\n arguments={\"name\": \"World\"}\n)\n\n# Parse content with type safety\ncontent = parse_content(result.content[0])\nassert isinstance(content, TextContent)\nprint(f\"\u2705 Result: {content.text}\")\n```\n\n**Full example:** `uv run python examples/e2e_tools_client.py`\n\n### \ud83d\udcc4 Resources \u2014 Reading Data\n\nResources provide access to data sources (files, databases, APIs):\n\n```python\nfrom chuk_mcp.protocol.messages.resources import send_resources_list, send_resources_read\n\n# List available resources \u2014 returns typed ListResourcesResult\nresources_result = await send_resources_list(read, write)\nprint(f\"\ud83d\udcda Found {len(resources_result.resources)} resources\")\n\nfor resource in resources_result.resources:\n print(f\" \u2022 {resource.name}\")\n print(f\" URI: {resource.uri}\")\n\n# Read a resource \u2014 returns typed ReadResourceResult\nif resources_result.resources:\n uri = resources_result.resources[0].uri\n read_result = await send_resources_read(read, write, uri)\n\n for content in read_result.contents:\n if hasattr(content, 'text'):\n print(f\"\ud83d\udcd6 Content: {content.text[:200]}...\")\n```\n\n**Full example:** `uv run python examples/e2e_resources_client.py`\n\n### \ud83d\udce1 Resource Subscriptions \u2014 Live Updates\n\nSubscribe to resources for real-time change notifications:\n\n```python\nfrom chuk_mcp.protocol.messages.resources import (\n send_resources_subscribe,\n send_resources_unsubscribe\n)\n\n# Subscribe to a resource\nuri = \"file:///logs/app.log\"\nsuccess = await send_resources_subscribe(read, write, uri)\n\nif success:\n print(f\"\u2705 Subscribed to {uri}\")\n print(\"\ud83d\udce1 Listening for changes...\")\n\n # In a real app, handle notifications in a loop\n # Notifications arrive as messages from the server\n\n # Unsubscribe when done\n await send_resources_unsubscribe(read, write, uri)\n print(\"\ud83d\udd15 Unsubscribed\")\n```\n\n**Full example:** `uv run python examples/e2e_subscriptions_client.py`\n\n### \ud83d\udcac Prompts \u2014 Template Management\n\nPrompts are reusable templates with parameters:\n\n```python\nfrom chuk_mcp.protocol.messages.prompts import send_prompts_list, send_prompts_get\n\n# List available prompts \u2014 returns typed ListPromptsResult\nprompts_result = await send_prompts_list(read, write)\nprint(f\"\ud83d\udcac Available prompts: {len(prompts_result.prompts)}\")\n\nfor prompt in prompts_result.prompts:\n print(f\" \u2022 {prompt.name}: {prompt.description}\")\n if hasattr(prompt, 'arguments') and prompt.arguments:\n args = [a.name for a in prompt.arguments]\n print(f\" Arguments: {', '.join(args)}\")\n\n# Get a prompt with arguments \u2014 returns typed GetPromptResult\nprompt_result = await send_prompts_get(\n read, write,\n name=\"code_review\",\n arguments={\"file\": \"main.py\", \"language\": \"python\"}\n)\n\n# Use the formatted messages\nfor message in prompt_result.messages:\n print(f\"\ud83e\udd16 {message.role}: {message.content}\")\n```\n\n**Full example:** `uv run python examples/e2e_prompts_client.py`\n\n### \ud83c\udfaf Sampling \u2014 AI Content Generation\n\nLet servers request AI to generate content on their behalf (requires user approval):\n\n```python\nfrom chuk_mcp.protocol.messages.sampling import sample_text\n\n# Check if server supports sampling\nif hasattr(init_result.capabilities, 'sampling'):\n print(\"\u2705 Server supports sampling\")\n\n # Server requests AI to generate content using helper\n result = await sample_text(\n read, write,\n prompt=\"Explain quantum computing in simple terms\",\n max_tokens=1000,\n model_hint=\"claude\",\n temperature=0.7\n )\n\n # Access typed response\n if hasattr(result.content, 'text'):\n print(f\"\ud83e\udd16 AI Generated: {result.content.text}\")\n\n print(f\"\ud83d\udcca Model: {result.model}\")\n print(f\"\ud83d\udd22 Stop Reason: {result.stopReason or 'N/A'}\")\n```\n\n**Use Case:** Servers can use sampling to generate code, documentation, or analysis based on data they have access to.\n\n**Full example:** `uv run python examples/e2e_sampling_client.py`\n\n### \ud83d\udcc1 Roots \u2014 Directory Access Control\n\nRoots define which directories the client allows servers to access.\n\n```python\nfrom chuk_mcp.protocol.messages.roots import (\n send_roots_list,\n send_roots_list_changed_notification\n)\n\n# Check if server supports roots\nif hasattr(init_result.capabilities, 'roots'):\n print(\"\u2705 Server supports roots capability\")\n\n # List current roots \u2014 returns typed ListRootsResult\n roots_result = await send_roots_list(read, write)\n\n print(f\"\ud83d\udcc1 Available roots: {len(roots_result.roots)}\")\n for root in roots_result.roots:\n print(f\" \u2022 {root.name}: {root.uri}\")\n\n # Notify server when roots change\n await send_roots_list_changed_notification(write)\n print(\"\ud83d\udce2 Notified server of roots change\")\n```\n\n**Use Case:** Control which directories AI can access, enabling secure sandboxed operations.\n\n**Full example:** `uv run python examples/e2e_roots_client.py`\n\n### \ud83c\udfad Elicitation \u2014 User Input Requests\n\nElicitation allows servers to request structured input from users:\n\n```python\nfrom chuk_mcp.protocol.messages.elicitation import send_elicitation_request\n\n# Server requests user input\nresponse = await send_elicitation_request(\n read, write,\n prompt=\"Enter API credentials\",\n fields=[\n {\"name\": \"api_key\", \"type\": \"text\", \"required\": True},\n {\"name\": \"region\", \"type\": \"select\", \"options\": [\"us\", \"eu\", \"asia\"]}\n ]\n)\n\n# Access user's input\nprint(f\"User provided: {response.values}\")\n```\n\n**Use Case:** Interactive workflows, OAuth flows, confirmation dialogs.\n\n**Full example:** `uv run python examples/e2e_elicitation_client.py`\n\n### \ud83d\udca1 Completion \u2014 Smart Autocomplete\n\nGet intelligent suggestions for tool arguments:\n\n```python\nfrom chuk_mcp.protocol.messages.completions import (\n send_completion_complete,\n create_argument_info\n)\n\n# Get completions for a file path argument \u2014 returns typed CompletionResult\nresponse = await send_completion_complete(\n read, write,\n ref={\"type\": \"ref/resource\", \"uri\": \"file:///data/\"},\n argument=create_argument_info(\n name=\"filename\",\n value=\"sales_202\" # Partial input\n )\n)\n\n# Show suggestions\nprint(\"\ud83d\udca1 Suggestions for 'sales_202':\")\nfor value in response.completion.values:\n print(f\" \u2022 {value}\")\n```\n\n**Full example:** `uv run python examples/e2e_completion_client.py`\n\n### \ud83d\udcca Progress Tracking\n\nMonitor long-running operations with progress updates:\n\n```python\nfrom chuk_mcp.protocol.messages.tools import send_tools_call\n\n# Call a long-running tool\n# Progress notifications will be sent automatically\nprint(\"\ud83d\udd04 Starting long operation...\")\n\nresult = await send_tools_call(\n read, write,\n name=\"process_large_dataset\",\n arguments={\"dataset\": \"sales_data.csv\"}\n)\n\nprint(\"\u2705 Operation complete\")\n# Progress notifications are handled automatically by the client\n```\n\n**Full example:** `uv run python examples/e2e_progress_client.py`\n\n### \ud83d\udeab Cancellation\n\nCancel long-running operations with timeout:\n\n```python\nimport anyio\nfrom chuk_mcp.protocol.messages.cancellation import send_cancelled_notification\nfrom chuk_mcp.protocol.messages.tools import send_tools_call\n\nasync def cancel_after_timeout():\n request_id = \"long-op-123\"\n\n async with anyio.create_task_group() as tg:\n # Start long-running operation\n tg.start_soon(send_tools_call, read, write, \"process_large_dataset\",\n {\"dataset\": \"big.csv\"}, request_id)\n\n # Cancel after 2 seconds\n with anyio.move_on_after(2):\n await anyio.sleep(999)\n\n # Send cancellation\n await send_cancelled_notification(write, request_id=request_id, reason=\"timeout\")\n print(\"\ud83d\udeab Cancellation sent\")\n\nanyio.run(cancel_after_timeout)\n```\n\n**Full example:** `uv run python examples/e2e_cancellation_client.py`\n\n### \ud83c\udf10 Multiple Transports\n\nUse different transport protocols for different scenarios:\n\n```python\nimport anyio\nfrom chuk_mcp.protocol.messages import send_initialize\nfrom chuk_mcp import stdio_client, StdioServerParameters\nfrom chuk_mcp.transports.http import http_client, HttpClientParameters\n\nasync def main():\n # Stdio transport (local processes)\n p1 = StdioServerParameters(\n command=\"uvx\",\n args=[\"mcp-server-sqlite\", \"--db-path\", \"local.db\"]\n )\n async with stdio_client(p1) as (r, w):\n init = await send_initialize(r, w)\n print(\"\ud83d\udce1 Stdio:\", init.serverInfo.name)\n\n # Streamable HTTP transport (remote servers)\n p2 = HttpClientParameters(url=\"http://localhost:8989/mcp\")\n async with http_client(p2) as (r, w):\n init = await send_initialize(r, w)\n print(\"\ud83c\udf10 Streamable HTTP:\", init.serverInfo.name)\n\nanyio.run(main)\n```\n\n### \ud83d\udd04 Multi-Server Orchestration\n\nConnect to multiple servers simultaneously:\n\n```python\nfrom chuk_mcp import stdio_client, StdioServerParameters\nfrom chuk_mcp.protocol.messages import send_initialize\nfrom chuk_mcp.protocol.messages.tools import send_tools_list\n\nservers = [\n StdioServerParameters(\n command=\"uvx\",\n args=[\"mcp-server-sqlite\", \"--db-path\", \"db1.db\"]\n ),\n StdioServerParameters(\n command=\"npx\",\n args=[\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"]\n )\n]\n\nprint(\"\ud83d\udd17 Connecting to multiple servers...\")\n\nfor i, server_params in enumerate(servers, 1):\n try:\n async with stdio_client(server_params) as (read, write):\n init_result = await send_initialize(read, write)\n tools_result = await send_tools_list(read, write)\n\n print(f\"\\n\ud83d\udce1 Server {i}: {init_result.serverInfo.name}\")\n print(f\" Tools: {len(tools_result.tools)}\")\n\n # Show first 3 tools\n for tool in tools_result.tools[:3]:\n print(f\" \u2022 {tool.name}\")\n except Exception as e:\n print(f\"\u26a0\ufe0f Server {i} failed: {e}\")\n```\n\n### Type Safety & Validation\n\nAll protocol messages return fully typed results using Pydantic (or fallback validation):\n\n```python\nfrom chuk_mcp.protocol.types.content import parse_content, TextContent\nfrom chuk_mcp.protocol.messages.tools import send_tools_call\n\n# Call a tool and get a typed result\ntool_result = await send_tools_call(read, write, name=\"greet\", arguments={\"name\": \"World\"})\n\n# Type-safe content parsing\ncontent = parse_content(tool_result.content[0])\nassert isinstance(content, TextContent)\nprint(content.text)\n```\n\n**Benefits:**\n\n* **Typed returns**: All `send_*` functions return typed Pydantic models\n* **Content parsing**: Use `parse_content()` for type-safe content handling\n* **Runtime validation**: Automatic validation with clear error messages\n* **IDE support**: Full autocomplete and type checking\n\n### Monitoring & Logging\n\nBuilt-in features for production environments:\n\n```python\nfrom chuk_mcp.protocol.messages.logging import send_logging_set_level\n\n# Set server logging level\nawait send_logging_set_level(write, level=\"debug\")\n```\n\n**Features:**\n\n* Structured logging with configurable levels\n* Performance monitoring (latency, error rates, throughput)\n* Progress tracking and cancellation support\n* Clean error propagation (no automatic retries at protocol layer)\n\n**Full example:** `uv run python examples/e2e_logging_client.py`\n\n---\n\n## Ecosystem\n\n`chuk-mcp` is part of a modular suite of Python MCP tools:\n\n* **[chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor)** \u2014 Reliable tool call execution with retries, caching, and exponential backoff\n* **[chuk-mcp-server](https://github.com/chrishayuk/chuk-mcp-server)** \u2014 Real-world MCP server implementation built on chuk-mcp\n* **[chuk-mcp-cli](https://github.com/chrishayuk/chuk-mcp-cli)** \u2014 Interactive CLI and playground for testing MCP servers\n\nEach component focuses on doing one thing well and can be used independently or together.\n\n---\n\n## License\n\nMIT \u2014 see [LICENSE](LICENSE).\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Model Context Protocol client and server library with optional Pydantic support",
"version": "0.8.1",
"project_urls": {
"Bug Tracker": "https://github.com/chrishayuk/chuk-mcp/issues",
"Homepage": "https://github.com/chrishayuk/chuk-mcp",
"Repository": "https://github.com/chrishayuk/chuk-mcp"
},
"split_keywords": [
"llm",
" openai",
" claude",
" mcp",
" client",
" protocol"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "8f6940988763461afb09ae4fe8934d295da2b98cc47a5ab636a081a536757cfc",
"md5": "6089b1d9ddfca887176a6f26c9d520e2",
"sha256": "372e4a06987210dc6d635f12ec6fb9d6a0335b8cc3895b8453df7808f21ec819"
},
"downloads": -1,
"filename": "chuk_mcp-0.8.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6089b1d9ddfca887176a6f26c9d520e2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 136228,
"upload_time": "2025-11-02T14:33:00",
"upload_time_iso_8601": "2025-11-02T14:33:00.881356Z",
"url": "https://files.pythonhosted.org/packages/8f/69/40988763461afb09ae4fe8934d295da2b98cc47a5ab636a081a536757cfc/chuk_mcp-0.8.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "363148e2c2c0a9f8e0f83594ed0ce72b6b9ac2b803c41b158a0c9926019e4ba7",
"md5": "e65d2a59440c2ec0da03e90484fbffe6",
"sha256": "6913651b1c5696dbe2868ab33e920ef1491f1f2a908d61c742c7b5a8f7f4adbd"
},
"downloads": -1,
"filename": "chuk_mcp-0.8.1.tar.gz",
"has_sig": false,
"md5_digest": "e65d2a59440c2ec0da03e90484fbffe6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 150091,
"upload_time": "2025-11-02T14:33:02",
"upload_time_iso_8601": "2025-11-02T14:33:02.528660Z",
"url": "https://files.pythonhosted.org/packages/36/31/48e2c2c0a9f8e0f83594ed0ce72b6b9ac2b803c41b158a0c9926019e4ba7/chuk_mcp-0.8.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-02 14:33:02",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "chrishayuk",
"github_project": "chuk-mcp",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "chuk-mcp"
}