Your own apps, scripts and agents need a client to use MCP servers.
The same mcp package includes a client. In SDK v2 it is one class, Client, used with async with so the connection is opened and closed cleanly.
What you pass decides the transport:
- a URL string such as
"http://localhost:8000/mcp": Streamable HTTP, StdioServerParameters(command=..., args=[...]): launch a local server as a subprocess over stdio,- the server object itself (
Client(mcp)): connect in memory, no process or port. Ideal for tests.
Entering the async with block connects and agrees on a protocol version automatically. Afterwards you can read client.protocol_version, client.server_capabilities, client.server_info and client.instructions (optional usage notes from the server).
The main calls mirror the protocol methods:
await client.list_tools()thenresult.tools(each hasname,description,input_schema),await client.call_tool(name, {...})returnscontent,structured_contentandis_error,list_resources(),list_resource_templates(),read_resource(uri),list_prompts(),get_prompt(name, {...}).
Client code is asynchronous; run it with anyio.run(main) or asyncio.run(main()).
When talking to servers you don't control, remember SDK v2's client validates responses strictly: a server that sends invalid data raises a validation error instead of being silently accepted. If a v1 tutorial shows ClientSession plus await session.initialize(), that lower layer still exists, but Client replaces it for normal use.
Example
import sys
import anyio
from mcp import Client, StdioServerParameters
async def main() -> None:
server = StdioServerParameters(command=sys.executable, args=["notes_server.py"])
async with Client(server) as client: # or Client("http://localhost:8000/mcp")
print(client.protocol_version) # 2026-07-28
tools = await client.list_tools()
print([t.name for t in tools.tools]) # ['list_notes']
result = await client.call_tool("list_notes", {})
print(result.is_error, result.structured_content)
note = await client.read_resource("notes://groceries")
print(note.contents[0].text) # milk, eggs
anyio.run(main)Remember
- Client(url) = HTTP, Client(StdioServerParameters) = subprocess, Client(server) = in memory
- async with handles connection and version negotiation
- list_tools / call_tool / read_resource / get_prompt mirror the protocol
- Client replaces v1's ClientSession + initialize() for normal use