Custom agents
capsol is a plain MCP server over Streamable HTTP at /mcp/<capsule-id>. OAuth discovery + Dynamic Client Registration is the default hosted-agent path. Autonomous agents that cannot begin with OAuth should use agent enrollment and wait for approval.
Framework matrix
Section titled “Framework matrix”| Framework | Package | Transport constructor | Lifecycle |
|---|---|---|---|
| LangChain (Python) | langchain-mcp-adapters | MultiServerMCPClient | Long-lived session |
| LangChain (JS) | @langchain/mcp-adapters | MultiServerMCPClient | Long-lived session |
| OpenAI Agents SDK | openai-agents | MCPServerStreamableHttp | async with ... as server: |
| Vercel AI SDK | ai | experimental_createMCPClient | Close explicitly (.close()) |
| Mastra | @mastra/mcp | MCPClient | Long-lived; tools cached at boot |
| Pydantic AI | pydantic-ai | MCPServerStreamableHTTP | async with agent.run_mcp_servers() |
All use the same capsule URL: https://capsol.example.com/mcp/<capsule-id>. Give every autonomous agent its own OAuth grant or approved enrollment so activity, unread signals, and revocation stay independent.
Custom clients must follow MCP Streamable HTTP request headers: POST JSON-RPC to the capsule URL with Content-Type: application/json and Accept: application/json, text/event-stream. Browser-based clients should send requests from the hosted registry origin or loopback during local debugging; unexpected Origin headers are rejected.
LangChain (Python)
Section titled “LangChain (Python)”pip install langchain-mcp-adapters langchain-openai langgraphimport asynciofrom langchain_mcp_adapters.client import MultiServerMCPClientfrom langchain_openai import ChatOpenAIfrom langgraph.prebuilt import create_react_agent
async def main(): client = MultiServerMCPClient({ "capsol": { "transport": "streamable_http", "url": "https://capsol.example.com/mcp/<capsule-id>", }, }) tools = await client.get_tools() agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools) result = await agent.ainvoke({"messages": [("user", "list every capsol item")]}) print(result["messages"][-1].content)
asyncio.run(main())Gotchas: transport: "streamable_http" (no SSE). MultiServerMCPClient holds a single long-lived session — don’t create one per request. Docs: https://github.com/langchain-ai/langchain-mcp-adapters ↗
LangChain (JS)
Section titled “LangChain (JS)”npm i @langchain/mcp-adapters @langchain/openai @langchain/langgraphimport { MultiServerMCPClient } from "@langchain/mcp-adapters";import { ChatOpenAI } from "@langchain/openai";import { createReactAgent } from "@langchain/langgraph/prebuilt";
const client = new MultiServerMCPClient({ mcpServers: { capsol: { transport: "streamable_http", url: "https://capsol.example.com/mcp/<capsule-id>", }, },});const tools = await client.getTools();const agent = createReactAgent({ llm: new ChatOpenAI({ model: "gpt-4o" }), tools });const result = await agent.invoke({ messages: [{ role: "user", content: "list every capsol item" }] });console.log(result.messages.at(-1)?.content);Gotchas: Node 20+. transport: "streamable_http". Docs: https://www.npmjs.com/package/@langchain/mcp-adapters ↗
OpenAI Agents SDK
Section titled “OpenAI Agents SDK”pip install openai-agentsimport asynciofrom agents import Agent, Runnerfrom agents.mcp import MCPServerStreamableHttp
async def main(): async with MCPServerStreamableHttp( name="capsol", params={"url": "https://capsol.example.com/mcp/<capsule-id>"}, ) as capsol: agent = Agent( name="researcher", instructions="Use capsol to recall project state before answering.", mcp_servers=[capsol], ) result = await Runner.run(agent, "what do we know about the auth flow?") print(result.final_output)
asyncio.run(main())Gotchas: MCPServerStreamableHttp is an async context manager. capsol’s tool names pass through verbatim — don’t rename. Docs: https://openai.github.io/openai-agents-python/mcp/ ↗
Vercel AI SDK
Section titled “Vercel AI SDK”npm i ai @ai-sdk/openaiimport { experimental_createMCPClient, generateText } from "ai";import { openai } from "@ai-sdk/openai";
const capsol = await experimental_createMCPClient({ transport: { type: "sse", url: "https://capsol.example.com/mcp/<capsule-id>" },});const tools = await capsol.tools();const { text } = await generateText({ model: openai("gpt-4o"), tools, prompt: "summarise every capsol docs:// entry",});console.log(text);await capsol.close();Gotchas: import is still experimental_. Use maxSteps: 5+ when chaining tool calls. Always close() the client. Docs: https://sdk.vercel.ai/docs/ai-sdk-core/mcp-tools ↗
Mastra
Section titled “Mastra”npm i @mastra/core @mastra/mcpimport { Agent } from "@mastra/core/agent";import { MCPClient } from "@mastra/mcp";import { openai } from "@ai-sdk/openai";
const mcp = new MCPClient({ servers: { capsol: { url: new URL("https://capsol.example.com/mcp/<capsule-id>") }, },});
export const researcher = new Agent({ name: "researcher", instructions: "Consult capsol before answering project questions.", model: openai("gpt-4o"), tools: await mcp.getTools(),});Gotchas: url must be a URL instance, not a string. Tool schemas cached at boot — restart after changing the capsule’s skills. Docs: https://mastra.ai/docs/agents/mcp-integration ↗
Pydantic AI
Section titled “Pydantic AI”pip install pydantic-aiimport asynciofrom pydantic_ai import Agentfrom pydantic_ai.mcp import MCPServerStreamableHTTP
capsol = MCPServerStreamableHTTP(url="https://capsol.example.com/mcp/<capsule-id>")agent = Agent("openai:gpt-4o", mcp_servers=[capsol], system_prompt="Consult capsol before answering.")
async def main(): async with agent.run_mcp_servers(): result = await agent.run("list every capsol item") print(result.data)
asyncio.run(main())Gotchas: wrap invocations in async with agent.run_mcp_servers() for session lifecycle. Python 3.10+. Docs: https://ai.pydantic.dev/mcp/client/ ↗
Session lifecycle rule
Section titled “Session lifecycle rule”Every framework above holds one long-lived MCP session per capsol connection for the life of the agent. Do not open a session per tool call; every initialize round-trip counts. If you need higher throughput or separate identity, create multiple connections from the dashboard and load-balance across them.
Use the stable capsule URL (https://capsol.example.com/mcp/<capsule-id>). OAuth-capable agents should discover /.well-known/oauth-protected-resource/mcp/<capsule-id>, register at /oauth/register, and complete PKCE. Agents that cannot start with OAuth should use /v1/agent-enrollments, save the one-time enrollment token, and poll until approval. Pending polls do not return MCP connection details; after approval, the same token becomes the MCP credential. Tokens are never accepted in URLs. See Access.
Agents can also inspect /.well-known/capsol-agent.json for supported auth flows, grant endpoints, docs links, and CLI commands. Human operators can approve OAuth and enrollment grants from the dashboard; enrollment grants can also be approved with capsol grants approve <grant-id>.