Skip to main content
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:
The generated 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
Use 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:
Use identifiers returned by discovery and add --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
See the MCP discovery operation reference. Trusted operations enforce permissions and invocation lifetime even when hidden from the model. Unsupported catalogues are empty; unsupported reads fail. Prompt arguments are strings, and returned messages are reference data. Outside an invocation, use a developer-owned connection:
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

Omit 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’s lib/:
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.
Latest-value recovery is not replay of every missed event. Identical content is deduplicated in process only, after successful handling. Restarts and multiple replicas can deliver again. Make handlers idempotent; use a durable queue or provider event history when every event must be processed.

Framework adapters

Harnest keeps connection identity and approval policy consistent. Same-named tools on different servers remain distinct.

Limit tools by runtime principal

Use permission= to require one grant for every tool from a client. Use tool_permissions= to add a requirement to selected remote tools:
An active Agent Runtime Principal must hold every permission required by the client and operation. Untagged operations are unavailable. Harnest checks exposure and execution; the remote server must still authorize requests.

Call MCP from trusted code

Use the invocation-scoped facade when a tool or service chooses the remote operation:
tools/search_catalog.py
The facade exposes neither the raw transport nor credentials. Calls use the same approval, Tool Lifecycle, and MCP Client Lifecycle paths as model-selected MCP tools.

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

Use harnest_fused to provision one MCP server from selected operations across multiple OpenAPI specifications.

Connect to OpenAPI at runtime

Use MCPClient.from_openapi(...) to load a specification at connection startup with normal MCP policies and credential references.

API reference

Reference: harnest.agent, harnest.logging.