MCP Tool Clients
Praval 0.8 can consume tools from Model Context Protocol servers without
creating a second tool registry. Discovered tools become normal Praval
ToolSpec entries, so provider-neutral model execution, HITL approval,
tracing, timeouts, and error handling continue to apply.
Install the optional SDK support on Python 3.10 or newer:
pip install 'praval[mcp]'
Praval core continues to support Python 3.9. The official MCP Python SDK v1 requires Python 3.10 or newer, so MCP connections are not available on Python 3.9.
Local stdio server
The stdio transport starts the configured program directly. Praval never passes the command through a shell.
import asyncio
from praval import Agent
from praval.mcp import MCPClient, MCPServerConfig
async def main() -> None:
agent = Agent(
"researcher",
provider="openai",
model="gpt-5.4-mini",
hitl_enabled=True,
)
config = MCPServerConfig(
name="filesystem",
transport="stdio",
command="python",
args=["servers/filesystem_server.py"],
env={"DATA_ROOT": "/safe/research-data"},
cwd="/opt/praval-mcp",
)
async with MCPClient(config) as client:
await client.register_tools(agent)
response = await agent.agenerate("Summarize the available notes.")
print(response.content)
asyncio.run(main())
The default tool prefix is the server name followed by two underscores. A
server tool named read_file is exposed to the model as
filesystem__read_file. Set tool_name_prefix when a different namespace is
needed.
Remote Streamable HTTP server
Use Streamable HTTP for remote MCP servers. Static headers are caller supplied and are redacted from Praval errors.
config = MCPServerConfig(
name="catalog",
transport="streamable_http",
url="https://mcp.example.com/api",
headers={"Authorization": "Bearer ..."},
connection_timeout=15,
tool_timeout=30,
)
async with MCPClient(config) as client:
tools = await client.register_tools(agent)
print([tool.name for tool in tools])
response = await agent.agenerate("Find the current catalog entry.")
Remote endpoints must use HTTPS. Plain HTTP is accepted only for loopback
hosts such as 127.0.0.1 and localhost, which supports local development and
CI contract tests.
Approval and async execution
MCP tools require approval by default. When a model selects one, Praval raises
InterventionRequired before sending the call to the MCP server. Approve and
resume on the same event loop:
from praval import InterventionRequired
try:
response = await agent.agenerate("Publish the selected catalog entry.")
except InterventionRequired as pending:
agent.approve_intervention(
pending.intervention_id,
reviewer="release-manager",
)
text = await agent.aresume_run(pending.run_id)
Set require_approval=False only for a server whose tools are safe to run
without a human decision. MCP handlers are async-only in 0.8. Use
Agent.agenerate() or Agent.astream(). A sync agent call that selects an MCP
tool fails with an instruction to use the async API; Praval does not move a
live MCP session between event loops.
Result handling
Text result blocks are joined into one ToolResult.content value. Structured
content is serialized when needed and retained in
ToolResult.metadata["structured_content"]. The default normalized result
limit is 1 MiB and can be changed with max_result_size.
Images, embedded resources, audio, and other binary MCP result blocks are rejected with a structured tool error in 0.8. Remote tool failures become sanitized tool errors instead of leaking configured headers, environment values, commands, or credentials.
Lifecycle and security
Keep the
MCPClientcontext open while an agent may use its registered tools. Leaving the context closes the session and any spawned stdio process.Calls after closure fail. Automatic reconnect is intentionally disabled.
Stdio configuration cannot contain HTTP fields. HTTP configuration cannot contain a command, arguments, environment, or working directory.
Tool discovery is namespaced per server, and duplicate or invalid schemas are rejected.
Connection and tool calls have bounded timeouts. Cancellation closes partial connection state.
OAuth negotiation is not included. Callers may provide static headers and are responsible for acquiring and rotating them.
MCP clients and provider-hosted descriptors
These are separate capabilities:
praval.mcp.MCPClientopens and owns a direct MCP client connection, then registers discovered tools in the normal Praval tool runtime.Provider-hosted MCP descriptors are raw provider options passed to a model vendor that owns the connection. They remain experimental and provider-specific.
Praval never reinterprets a provider-hosted descriptor as a local MCP client.
Explicitly unsupported in 0.8
Legacy SSE transport
Resources and prompts
Sampling and elicitation
Experimental MCP tasks
MCP server hosting
Managed OAuth flows
Binary and image results
Automatic reconnect
Sync MCP event-loop bridging