MCPClient declares a connection to an external MCP server. Direct clients live under root mcp/; each public filename supplies the connection identity and exports a zero-argument client() factory returning MCPClient.
Scaffold a connection
Create an authenticated Streamable HTTP client without putting its token in source:mcp/catalog.py reads the token through ${CATALOG_MCP_TOKEN}, sends it as Authorization: Bearer <token>, and prefixes discovered tool names with catalog_.
Choose where and how the token is sent when a server uses a different header scheme:
The command accepts only the environment variable’s name. It rejects token values, credentials embedded in URLs, invalid header names, and advanced-mode projects where
mcp/ discovery is not active.
mcp/catalog.py
MCPClient.sse(...) only for a legacy SSE server. Compilation does not connect. Tool discovery is lazy; explicitly configured subscriptions start with the serving runtime.
Discover what a server provides
Developers do not need to know resource URIs or prompt names in advance. From the agent folder, query a configured connection:--project ./my-agent when outside the agent folder. Inspection uses the agent’s environment and credentials without starting models or subscriptions.
Inspection returns capabilities, tool schemas, resources, URI templates, and prompt argument definitions. Each catalogue is one page: pass a returned nextCursor to harnest mcp inspect catalog --catalog resources --cursor '…'. The other catalogues are tools, resource-templates, and prompts. Servers without an optional capability return an empty catalogue.
The local playground’s MCP view offers the same discovery and explicit retrieval. It accepts only loopback, same-origin requests and is absent from deployed artifacts without the development UI.
Give the agent access to context
The connection above is enough. Managed ADK and LangGraph agents receive namespaced helpers based on the server’s advertised capabilities and your allowlists:
Resource and prompt helpers are additive. Setting
resources=[] or prompts=[] hides that category’s helpers and denies retrieval. An empty catalogue does not disable an advertised capability: resources or prompts can appear later. Harnest does not eagerly inject their contents into instructions.
Trusted code can use the same governed operations:
tools/load_context.py
connect() uses the LangGraph lifecycle identity by default; pass framework="adk" when sharing an ADK-owned lifecycle. Resource/prompt requests are independently scoped; do not rely on per-session server state shared with tool calls.
Streamable HTTP tries protocol 2026-07-28 discovery, falling back to SDK initialization for unsupported or older servers. Authentication, header-validation, and transport failures do not trigger fallback. Inspect the selected protocolVersion with harnest mcp inspect CLIENT --json.
Tool calls are sent once; Harnest does not automatically replay them after a disconnect or an input-required response. Interactive multi-round-trip input handling is not supported on the newer HTTP path yet. Stdio and legacy SSE continue using the installed SDK’s supported protocol versions.
Restrict retrieval
resources or prompts to allow discovery and retrieval of that category; use [] to deny it. Allowlists match exact URIs or prompt names. Listing an allowed URI template does not authorize arbitrary expanded URIs: concrete reads must also be allowed. max_content_bytes bounds returned content (default 1 MiB, configurable from 1 KiB to 16 MiB), not the SDK’s transport allocation. Binary content stays in MCP’s encoded representation.
Client-wide permission protects resource/prompt helpers as well as remote tools. tool_permissions applies only to named remote tools. No permission tag means no access when a default-deny Agent Runtime Principal is active.
Subscribe to retained resource updates
Subscriptions belong on the MCP client, not on a public HTTP route. Import an async handler from the agent’slib/:
mcp/catalog.py
lib/catalog_events.py
event.reason is start, update, or reconnect. Handlers have no inherited user/session context: choose an application-authorized identity explicitly if your service invokes an agent or enqueues a task. The handler timeout defaults to 30 seconds (handler_timeout_seconds). No public subscription-management endpoint is created.
Subscriptions default to SDK 1.x resources/subscribe. For stateless Streamable HTTP endpoints, explicitly set MCPSubscription(method="subscriptions/listen", ...) to use protocol 2026-07-28.
Harnest subscribes before its initial read and rereads after reconnecting. Initial failure stops startup; established listeners retry with backoff up to 30 seconds. Shutdown cancels listeners before closing clients.
Framework adapters
Harnest keeps connection identity and approval policy consistent. Same-named tools on different servers remain distinct.
Limit tools by runtime principal
Usepermission= to require one grant for every tool from a client. Use tool_permissions= to add a requirement to selected remote tools:
Call MCP from trusted code
Use the invocation-scoped facade when a tool or service chooses the remote operation:tools/search_catalog.py
Authentication and gateways
Read URLs and static credentials from the deployment environment. For user-scoped access, resolve a token through Authentication and Credentials. Customize the HTTP client only for a gateway, mTLS, or transport policy. Put shared clients in lifecycle resources because adapters may call the factory more than once.Direct connections and plugins
Combine APIs with Fused
Useharnest_fused to provision one MCP server from selected operations across multiple OpenAPI specifications.
Connect to OpenAPI at runtime
UseMCPClient.from_openapi(...) to load a specification at connection startup with normal MCP policies and credential references.
API reference
Reference:harnest.agent, harnest.logging.