MCP Client API Reference¶
Client libraries for connecting to MCP servers.
MCPClient¶
promptise.mcp.client.MCPClient
¶
Production-grade MCP client for a single server.
Supports HTTP (Streamable HTTP), SSE, and stdio transports with Bearer token or API key authentication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str | None
|
Server endpoint URL (for HTTP/SSE). Mutually exclusive
with |
None
|
transport
|
str
|
|
'http'
|
headers
|
dict[str, str] | None
|
Extra HTTP headers sent on every request. |
None
|
bearer_token
|
str | None
|
Pre-issued Bearer token. When provided, an
|
None
|
api_key
|
str | None
|
Pre-shared API key. When provided, an |
None
|
command
|
str | None
|
Executable for stdio transport (e.g. |
None
|
args
|
list[str] | None
|
Arguments for the stdio command. |
None
|
env
|
dict[str, str] | None
|
Environment variables for the stdio process. |
None
|
cwd
|
str | None
|
Working directory for the stdio subprocess. When |
None
|
timeout
|
float
|
HTTP request timeout in seconds. |
30.0
|
elicitation_callback
|
ElicitationFnT | None
|
Answers the server's MCP elicitation
requests ( |
None
|
auto_reconnect
|
bool
|
Re-open the MCP session when an HTTP or SSE server
loses it (default |
True
|
bearer_token_provider
|
BearerTokenProvider | None
|
For short-lived tokens, instead of
|
None
|
Example — unauthenticated::
async with MCPClient(url="http://localhost:8080/mcp") as client:
tools = await client.list_tools()
Example — with Bearer token::
async with MCPClient(
url="http://localhost:8080/mcp",
bearer_token="eyJhbGciOiJIUzI1NiIs...",
) as client:
result = await client.call_tool("search", {"query": "python"})
Example — with short-lived tokens from an identity::
async with MCPClient(
url="http://localhost:8080/mcp",
bearer_token_provider=lambda force: identity.get_credential(
"api://my-server", force_refresh=force
),
) as client:
result = await client.call_tool("search", {"query": "python"})
Example — with API key::
async with MCPClient(
url="http://localhost:8080/mcp",
api_key="my-secret-key",
) as client:
tools = await client.list_tools()
Example — fetch token from server endpoint::
token = await MCPClient.fetch_token(
"http://localhost:8080/auth/token",
client_id="my-agent",
client_secret="agent-secret",
)
async with MCPClient(
url="http://localhost:8080/mcp",
bearer_token=token,
) as client:
tools = await client.list_tools()
headers
property
¶
Current static HTTP headers (read-only copy).
A token from bearer_token_provider is set per request and is not
included.
in_flight_calls
property
¶
Tool calls sent on this connection that have not returned yet.
An elicitation handler uses this to relate a server request to the call that caused it. With exactly one call in flight, a request arriving meanwhile is attributed to that call; with none or several, it cannot be tied to one.
Read from inside an elicitation callback that is answering a request
on a per-caller session derived from this client (see
:meth:with_bearer_token), it returns that session's calls: the
connection the request actually arrived on.
session
property
¶
The underlying MCP ClientSession, or None if not connected.
session_generation
property
¶
How many MCP sessions this client has opened.
1 after connecting; each transparent re-initialisation after a
lost session adds one. Compare the value before and after a call to
tell whether the server's session — and so possibly its tool list —
changed underneath it.
supports_bearer_token
property
¶
Whether a per-caller bearer token can be sent to this server.
True for HTTP and SSE. False for stdio, which has no
request headers: a stdio server runs with the agent's own
privileges for every caller.
transport
property
¶
The transport this client connects with ("http", "sse", "stdio").
__aenter__()
async
¶
Connect to the server and initialise the session.
Raises:
| Type | Description |
|---|---|
MCPConnectionRejectedError
|
The HTTP server answered the handshake with a 4xx status (e.g. 401 Unauthorized, or 404 for a URL that is not an MCP endpoint). |
MCPClientError
|
Any other connection or handshake failure. |
__aexit__(*exc)
async
¶
Close the session and transport.
Cleanup is best-effort and never raises: the session is closed in the task that opened it, so it is safe to call from any task.
call_tool(name, arguments=None, *, progress_callback=None)
async
¶
Call a tool on the connected server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Tool name. |
required |
arguments
|
dict[str, Any] | None
|
Tool arguments dict. |
None
|
progress_callback
|
ProgressFnT | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
CallToolResult
|
MCP |
fetch_token(token_url, client_id, client_secret, *, timeout=10.0)
async
staticmethod
¶
Fetch a Bearer token with an OAuth 2.0 client_credentials grant.
Sends a standard RFC 6749 token request: a form-encoded
(application/x-www-form-urlencoded) POST with
grant_type=client_credentials, client_id and
client_secret. That works against the MCP server's built-in
token issuer (:meth:~promptise.mcp.server.MCPServer.enable_token_endpoint)
and against any token endpoint that accepts client_secret_post.
A built-in issuer from Promptise 1.3.2 or earlier only reads JSON; when
one answers that the body must be JSON, the request is retried once as
JSON.
For production, obtain tokens from your Identity Provider
(Auth0, Keycloak, Okta, etc.) and pass them directly via
bearer_token.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_url
|
str
|
Full URL of the token endpoint
(e.g. |
required |
client_id
|
str
|
Client identifier registered on the server. |
required |
client_secret
|
str
|
Client secret for authentication. |
required |
timeout
|
float
|
HTTP request timeout in seconds. |
10.0
|
Returns:
| Type | Description |
|---|---|
str
|
The access token string (ready to pass as |
Raises:
| Type | Description |
|---|---|
MCPClientError
|
On network errors or authentication failure. |
get_prompt(name, arguments=None)
async
¶
Get a prompt, rendered with arguments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Prompt name. |
required |
arguments
|
dict[str, Any] | None
|
Prompt arguments. MCP carries them as strings:
strings are sent as is, other values as JSON ( |
None
|
Returns:
| Type | Description |
|---|---|
GetPromptResult
|
MCP |
Raises:
| Type | Description |
|---|---|
MCPClientError
|
The server refused the request (unknown prompt, missing argument, denied, ...) or the connection failed. |
list_prompts()
async
¶
List the server's prompts (every page).
Returns:
| Type | Description |
|---|---|
list[Prompt]
|
MCP |
list[Prompt]
|
|
list_resource_templates()
async
¶
List the server's resource templates (every page).
Returns:
| Type | Description |
|---|---|
list[ResourceTemplate]
|
MCP |
list[ResourceTemplate]
|
|
list_resources()
async
¶
List the server's static resources (every page).
Returns:
| Type | Description |
|---|---|
list[Resource]
|
MCP |
list[Resource]
|
and |
list_tools()
async
¶
List all tools from the connected server.
Returns:
| Type | Description |
|---|---|
list[Tool]
|
List of MCP |
read_resource(uri)
async
¶
Read a resource by URI (a static resource or a template expansion).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
The resource URI, e.g. |
required |
Returns:
| Type | Description |
|---|---|
ReadResourceResult
|
MCP |
ReadResourceResult
|
|
ReadResourceResult
|
base64 |
Raises:
| Type | Description |
|---|---|
MCPClientError
|
The server refused the read (unknown URI, denied by auth or a guard, ...) or the connection failed. |
with_bearer_token(bearer_token)
¶
Return a new, unconnected client that authenticates as bearer_token.
The copy keeps this client's URL, transport, timeout, headers
(including x-api-key) and elicitation callback, and replaces any
Authorization header, whatever its casing, with
Bearer <bearer_token>. Used to open a session per caller, so one
caller's token is never sent on another caller's requests, while the
server can still ask the caller's human to approve a gated call.
A bearer_token_provider (for instance the agent's own identity)
is not copied: the caller's token takes its place.
Raises:
| Type | Description |
|---|---|
MCPClientError
|
For a stdio client, which cannot carry headers. |
MCPClientError¶
promptise.mcp.client.MCPClientError
¶
Bases: RuntimeError
Raised when an MCP client operation fails.
MCPConnectionRejectedError¶
promptise.mcp.client.MCPConnectionRejectedError
¶
Bases: MCPClientError
Raised when an HTTP MCP server rejects the session with a 4xx status.
Typically 401/403 (missing or wrong bearer_token/api_key)
or 404 (wrong endpoint URL). Retrying with the same configuration
will fail the same way.
Also raised when a server rejects the credential of an established
session (mid_session=True), typically because a static
bearer_token expired. The call that hit it fails at once.
Attributes:
| Name | Type | Description |
|---|---|---|
status_code |
The HTTP status the server answered with. |
|
reason |
The HTTP reason phrase (e.g. |
|
url |
The endpoint that was contacted. |
|
server_name |
The server's name when connected through
:class: |
|
mid_session |
|
for_server(server_name)
¶
Return a copy of this error that names server_name.
MCPMultiClient¶
promptise.mcp.client.MCPMultiClient
¶
Connect to multiple MCP servers and aggregate their tools.
Each server gets its own MCPClient with independent auth / headers.
Tools are tracked per-server so call_tool routes to the correct one.
Tool name collisions: If two servers expose a tool with the same
name, the server that comes last in clients wins: list_tools
returns only its copy, call_tool routes to it, and a warning names
both servers. The other copies are not reachable through this client.
Consider using server-specific prefixes on your MCP servers to avoid
collisions.
Per-caller tokens: call_tool(..., bearer_token=...) sends that
token instead of the client's own credentials. Each distinct token
gets its own session to an HTTP/SSE server (opened on first use,
reused by later calls with the same token), so concurrent calls for
different users never share headers. stdio servers have no request
headers: the token is ignored there and a warning is logged once.
Server restarts: when a server loses the session (restart,
redeploy), its MCPClient opens a new one and retries the call once;
this client then re-lists that server's tools so routing follows the
tools the new deployment serves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clients
|
dict[str, MCPClient]
|
Mapping of server name → |
required |
elicitation_callback
|
ElicitationFnT | None
|
Default handler for MCP elicitation requests,
installed on every client that was not given its own
|
None
|
max_caller_sessions
|
int
|
Most idle per-caller sessions kept open. Least recently used sessions are closed first. |
256
|
caller_session_idle_timeout
|
float
|
Seconds after which an unused per-caller session is closed. |
300.0
|
multi = MCPMultiClient({
"hr": MCPClient(url="http://localhost:8080/mcp", bearer_token="..."),
"docs": MCPClient(url="http://localhost:9090/mcp", api_key="secret"),
})
async with multi:
tools = await multi.list_tools()
result = await multi.call_tool("search_employees", {"query": "python"})
prompt_to_server
property
¶
Read-only view of prompt name → server name mapping.
resource_to_server
property
¶
Read-only view of static resource URI → server name mapping.
servers
property
¶
Read-only view of server name → client mapping.
template_to_server
property
¶
Read-only view of resource URI template → server name mapping.
tool_to_server
property
¶
Read-only view of tool name → server name mapping.
__aenter__()
async
¶
Connect to all servers.
__aexit__(*exc)
async
¶
Disconnect from all servers.
Every client is closed even when closing another one fails or the caller is cancelled meanwhile; the cancellation is re-raised once they all are.
call_tool(name, arguments=None, *, bearer_token=None, progress_callback=None)
async
¶
Call a tool, automatically routing to the correct server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Tool name (as discovered via |
required |
arguments
|
dict[str, Any] | None
|
Tool arguments dict. |
None
|
bearer_token
|
str | None
|
Send this token as |
None
|
progress_callback
|
ProgressFnT | None
|
Receives the call's progress notifications;
see :meth: |
None
|
Returns:
| Type | Description |
|---|---|
CallToolResult
|
MCP |
Raises:
| Type | Description |
|---|---|
MCPClientError
|
If the tool name is unknown or the call fails. |
MCPConnectionRejectedError
|
The server refused bearer_token. |
get_prompt(name, arguments=None, *, server=None, bearer_token=None)
async
¶
Get a prompt, routing to the server that serves name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Prompt name. |
required |
arguments
|
dict[str, Any] | None
|
Prompt arguments (see :meth: |
None
|
server
|
str | None
|
Get the prompt from this server instead of routing by name. |
None
|
bearer_token
|
str | None
|
Get it as this caller, over the caller's own
session (see :meth: |
None
|
Raises:
| Type | Description |
|---|---|
MCPClientError
|
No server serves name, or the request failed. |
MCPConnectionRejectedError
|
The server refused bearer_token. |
list_prompts()
async
¶
Discover prompts from every connected server.
A server that does not serve prompts is skipped. If two servers
serve a prompt with the same name, the last one wins: only its copy
is listed (a warning is logged) — pass server= to
:meth:get_prompt to pick another.
list_resource_templates()
async
¶
Discover resource templates from every connected server.
A server that does not serve resources is skipped. Updates the
template routing used by :meth:read_resource.
list_resources()
async
¶
Discover static resources from every connected server.
A server that does not serve resources is skipped. Updates the
URI → server routing used by :meth:read_resource.
list_tools()
async
¶
Discover tools from all connected servers.
Returns:
| Type | Description |
|---|---|
list[Tool]
|
Combined list of tools from all servers, one per name. When |
list[Tool]
|
two servers expose a tool with the same name, only the copy |
list[Tool]
|
from the server that comes last in |
list[Tool]
|
and |
list[Tool]
|
The |
list[Tool]
|
routes correctly. |
read_resource(uri, *, server=None, bearer_token=None)
async
¶
Read a resource, routing to the server that serves uri.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
Resource URI (static, or matching a server's template). |
required |
server
|
str | None
|
Read from this server instead of routing by URI. |
None
|
bearer_token
|
str | None
|
Read as this caller: the token is sent as
|
None
|
Raises:
| Type | Description |
|---|---|
MCPClientError
|
No server serves uri, or the read failed. |
MCPConnectionRejectedError
|
The server refused bearer_token. |
server_for_prompt(name)
async
¶
Name of the server that serves prompt name (discovering prompts if needed).
Raises:
| Type | Description |
|---|---|
MCPClientError
|
No connected server serves name. |
server_for_resource(uri)
async
¶
Name of the server that serves uri (discovering resources if needed).
Raises:
| Type | Description |
|---|---|
MCPClientError
|
No connected server serves uri. |
InFlightToolCall¶
promptise.mcp.client.InFlightToolCall
dataclass
¶
A call_tool request awaiting its result.
Exposed through :attr:MCPClient.in_flight_calls so an elicitation
handler can relate a server's elicitation/create request to the
tool call that triggered it. MCP carries no such link on the wire,
so a request is attributed to a call only when exactly one is in flight.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The tool being called. |
arguments |
dict[str, Any]
|
A copy of the arguments the call was sent with. |
context |
Context
|
A snapshot of the caller's :mod: |
MCPToolAdapter¶
promptise.mcp.client.MCPToolAdapter
¶
Discover MCP tools and convert them to LangChain BaseTool instances.
Backed by the Promptise MCP Client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
multi
|
MCPMultiClient
|
Connected |
required |
on_before
|
OnBefore | None
|
Callback fired before each tool invocation. |
None
|
on_after
|
OnAfter | None
|
Callback fired after each tool invocation. |
None
|
on_error
|
OnError | None
|
Callback fired on tool errors. |
None
|
optimize
|
Any | None
|
Tool optimization config (schema minification etc.). |
None
|
forward_caller_token
|
bool | Collection[str]
|
Which servers' tools send the invoking
|
False
|
on_progress
|
OnProgress | None
|
Callback fired for each progress notification a
server sends during a tool call, as |
None
|
multi = MCPMultiClient({"hr": MCPClient(...)})
async with multi:
adapter = MCPToolAdapter(multi)
tools = await adapter.as_langchain_tools()
# Pass `tools` to build_agent(extra_tools=tools)
as_langchain_tools()
async
¶
Discover tools and return them as LangChain BaseTool instances.
Each tool's args_schema is a recursively-built Pydantic model
that preserves nested object structure, descriptions, defaults,
and constraints from the MCP server's JSON Schema.
When optimize is set, static optimizations (schema
minification, description truncation) are applied to reduce
token cost.
Returns:
| Type | Description |
|---|---|
list[BaseTool]
|
List of |
list_tool_info()
async
¶
Return human-readable tool metadata for introspection.