Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mcp-client-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ Without an `ANTHROPIC_API_KEY`, the client still connects, prints the server's t

The two channels go to different readers: `content` is forwarded to the model, while `structured_content` is used as data — when a tool returns an array, the client counts its items rather than re-reading the prose. See [Structured Content](https://modelcontextprotocol.io/specification/2026-07-28/server/tools#structured-content).

`Client(transport, mode="auto")` probes `server/discover` and falls back to the `2025-11-25` handshake; `client.protocol_version` reports which era you got. See [Protocol versions](https://py.sdk.modelcontextprotocol.io/v2/protocol-versions/).
`Client(server_params, mode="auto")` launches the server from its `StdioServerParameters`, then probes `server/discover` and falls back to the `2025-11-25` handshake; `client.protocol_version` reports which era you got. See [Protocol versions](https://py.sdk.modelcontextprotocol.io/v2/protocol-versions/).
10 changes: 3 additions & 7 deletions mcp-client-python/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from anthropic import Anthropic
from dotenv import load_dotenv
from mcp import Client, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp_types import TextContent

load_dotenv() # load environment variables from .env
Expand Down Expand Up @@ -54,10 +53,9 @@ async def connect_to_server(self, server_script_path: str):
else:
server_params = StdioServerParameters(command="node", args=[server_script_path], env=None)

# Client launches the command itself when given StdioServerParameters.
# "auto" probes server/discover, falling back to the 2025-11-25 handshake.
self.client = await self.exit_stack.enter_async_context(
Client(stdio_client(server_params), mode="auto")
)
self.client = await self.exit_stack.enter_async_context(Client(server_params, mode="auto"))

# List available tools
response = await self.client.list_tools()
Expand Down Expand Up @@ -106,9 +104,7 @@ async def process_query(self, query: str) -> str:
{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": "\n".join(
block.text for block in result.content if isinstance(block, TextContent)
),
"content": "\n".join(block.text for block in result.content if isinstance(block, TextContent)),
"is_error": bool(result.is_error),
}
)
Expand Down
8 changes: 4 additions & 4 deletions mcp-client-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ description = "A simple MCP client"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"anthropic>=0.87.0",
"mcp>=2.0.0",
"anthropic>=1.4.0",
"mcp>=2.2.0",
# client.py imports TextContent from mcp_types, which `mcp` pins exactly.
"mcp-types>=2.0.0",
"python-dotenv>=1.2.2",
"mcp-types>=2.2.0",
"python-dotenv>=1.2.3",
]

[dependency-groups]
Expand Down
701 changes: 321 additions & 380 deletions mcp-client-python/uv.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion weather-server-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ requires-python = ">=3.10"
# install a second HTTP stack alongside it.
dependencies = [
"httpx2>=2.12.0",
"mcp[cli]>=2.0.0",
"mcp[cli]>=2.2.0",
# weather.py imports BaseModel, Field, and RootModel directly. RootModel is v2-only.
"pydantic>=2.0.0",
]
Expand Down
649 changes: 324 additions & 325 deletions weather-server-python/uv.lock

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions weather-server-python/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import httpx2
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
from pydantic import BaseModel, Field, RootModel

# Initialize the MCP server
Expand Down Expand Up @@ -74,7 +75,7 @@ async def get_alerts(state: str) -> Alerts:
data = await make_nws_request(url)

if not data or "features" not in data:
raise ValueError(f"Unable to fetch alerts for {state.upper()}.")
raise ToolError(f"Unable to fetch alerts for {state.upper()}.")

# An empty result is an empty array, not an error.
#
Expand Down Expand Up @@ -107,18 +108,18 @@ async def get_forecast(latitude: float, longitude: float) -> Forecast:
points_data = await make_nws_request(points_url)

if not points_data:
raise ValueError("Unable to fetch forecast data for this location.")
raise ToolError("Unable to fetch forecast data for this location.")

# Get the forecast URL from the points response
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)

if not forecast_data:
raise ValueError("Unable to fetch detailed forecast.")
raise ToolError("Unable to fetch detailed forecast.")

periods = forecast_data["properties"]["periods"][:5] # Only show next 5 periods
if not periods:
raise ValueError("No forecast periods available.")
raise ToolError("No forecast periods available.")

return Forecast(
latitude=latitude,
Expand Down
Loading