Research date · May 25, 2026 · MCP transport note updated · August 6, 2026
Generated with AI

Internal vs Remote Tools — How AI Agent Frameworks Wire In Each

A reference map of where tools can live across ten AI agent frameworks — spanning Python, TypeScript, .NET, Java, Go, and Rust — and the official declaration and call mechanism for each: in-process code, MCP servers, OpenAPI specs, remote agents over A2A, and generic RPC. Every code snippet is taken from the framework's own documentation.

The five places a tool can live

Across the ten frameworks profiled here, tools are sourced from five categories. The framework determines which categories have first-class APIs and which require custom code. Each category is described below; per-framework support is summarised in the side-by-side table.

INTERNAL In-process code

A function, class, or annotated method that runs in the agent's own runtime. Schema is derived from the host language's type system or from explicit metadata.

MCP Model Context Protocol

An open JSON-RPC protocol for tool servers, defined at modelcontextprotocol.io. The current transports are stdio (subprocess) and Streamable HTTP; legacy SSE remains supported by some framework integrations for compatibility. Tools are discovered at runtime via tools/list.

OPENAPI REST / OpenAPI

The framework parses an OpenAPI 3.x specification and generates one tool per operation, with parameters typed from the spec.

A2A Remote agent (A2A)

Another agent running on a different host, described by an agent-card.json at a .well-known/ URL, invoked over HTTP per the A2A protocol.

RPC Generic RPC / gRPC

No framework on this list provides a first-class generator from a gRPC / Thrift / other RPC schema. The documented pattern is to call the RPC client from within an in-process tool body.

Ten frameworks, five wiring points

Each card lists the in-process baseline (recapped from the companion paper How AI Agent Frameworks Wire In Tools and Agents), then the framework's documented mechanism for MCP, OpenAPI, A2A, and generic RPC. Highlighted spans mark the line of code where each external tool source is attached to the agent.

Lc

LangChain Python

Decorator for internal; langchain-mcp-adapters for MCP; community RequestsToolkit for OpenAPI.
All external tool sources are exposed through adapter packages that produce a list of LangChain tools, which is passed to create_agent.
Internal

Recap: the @tool decorator turns a function into a tool; pass the list to create_agent.

from langchain.tools import tool
from langchain.agents import create_agent

@tool
def search_database(query: str) -> str:
    """Search the customer database."""
    return f"results for {query}"

agent = create_agent("openai:gpt-4o", tools=[search_database])
MCP

The langchain-mcp-adapters package ships MultiServerMCPClient, which connects to one or many MCP servers (stdio, SSE, or streamable HTTP) and returns a list of LangChain tools.

from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient({
    "math": {
        "transport": "stdio",
        "command": "python",
        "args": ["/path/to/math_server.py"],
    },
    "weather": {
        "transport": "streamable_http",
        "url": "http://localhost:8000/mcp",
    },
})

tools = await client.get_tools()
agent = create_agent("anthropic:claude-sonnet-4-6", tools=tools)
OpenAPI / REST

Use RequestsToolkit from langchain_community to expose generic HTTP verbs, then pair with prompt-based OpenAPI guidance. There is no auto-generator from spec to typed tools in core; the langchain-community openapi planner is the closest first-class option.

from langchain_community.agent_toolkits.openapi.toolkit import RequestsToolkit
from langchain_community.utilities.requests import TextRequestsWrapper

toolkit = RequestsToolkit(
    requests_wrapper=TextRequestsWrapper(headers={"Authorization": "Bearer …"}),
    allow_dangerous_requests=True,
)
agent = create_agent(model, tools=toolkit.get_tools())
Remote agent (A2A)

Not first-class in LangChain core. The recommended path is LangGraph's RemoteGraph (see next card) — wrap a deployed graph and embed it as a tool or subgraph.

Generic RPC

Open the gRPC / Thrift / custom-RPC client inside the body of an @tool-decorated function. LangChain doesn't add anything special; the tool is just a callable.

Mechanism

All external tool sources are converted to LangChain BaseTool instances by adapter packages.

Notes

Each remote source requires a separate adapter package. Connection lifecycle and authentication are configured per-adapter.

Lg

LangGraph Python

Inherits LangChain's adapters; adds RemoteGraph as the first-class A2A primitive.
A RemoteGraph is invoked or embedded as a subgraph using the same APIs as a local compiled graph.
Internal

Recap: tools are LangChain tools; the ToolNode fans them out in parallel.

from langgraph.prebuilt import create_react_agent
graph = create_react_agent(model, tools=[check_weather])
MCP

Inherits from LangChain — drop the MultiServerMCPClient output straight into create_react_agent or a ToolNode.

from langgraph.prebuilt import create_react_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

tools = await MultiServerMCPClient({
    "github": {"transport": "streamable_http", "url": "https://mcp.github.com"},
}).get_tools()

graph = create_react_agent("openai:gpt-4o", tools=tools)
OpenAPI / REST

Same as LangChain — use RequestsToolkit or community OpenAPI planners. There is no LangGraph-specific OpenAPI loader.

Remote agent (A2A) — RemoteGraph

RemoteGraph connects to a deployed LangGraph Platform graph at the given url and exposes the same interface as a local compiled graph. It can be invoked directly or embedded as a node in a parent graph.

from langgraph.pregel.remote import RemoteGraph
from langgraph.graph import StateGraph, MessagesState

# Connect to a graph deployed on LangGraph Platform
remote_graph = RemoteGraph("research_agent", url="https://<deployment>.langgraph.cloud")

# 1. Call it directly
result = await remote_graph.ainvoke(
    {"messages": [{"role": "user", "content": "analyse Q1 earnings"}]}
)

# 2. Or embed it as a subgraph node in a larger orchestrator
builder = StateGraph(MessagesState)
builder.add_node("researcher", remote_graph)   # remote graph as a local node
builder.add_edge("researcher", "writer")
super_graph = builder.compile()
Generic RPC

Wrap the RPC client in any LangChain tool, or inside a custom node function — graph nodes are async Python callables.

Mechanism

RemoteGraph uses the LangGraph SDK over HTTP. Checkpointer state is maintained on the remote deployment.

Constraints

The remote endpoint must implement the LangGraph Platform API; arbitrary HTTP agents are not directly supported.

Source · RemoteGraph docs · MCP and OpenAPI inherited from LangChain
Cr

CrewAI Python

Declarative mcps=[...] field on the Agent; first-class A2AClientConfig for remote agents.
Internal tools, MCP servers, and remote A2A endpoints are declared via dedicated fields on the Agent constructor: tools=, mcps=, and a2a=.
Internal

Recap: subclass BaseTool or use the @tool decorator; pass instances via tools=.

from crewai import Agent
researcher = Agent(role="Analyst", goal="…", backstory="…", tools=[MyTool()])
MCP — the mcps= field

The mcps= list on Agent accepts string references (remote HTTPS URLs or catalog slugs) and structured transport objects (MCPServerStdio, MCPServerHTTP, MCPServerSSE) in the same list. Stdio, streamable HTTP, and SSE transports are supported.

from crewai import Agent
from crewai.mcp import MCPServerStdio, MCPServerHTTP, MCPServerSSE

agent = Agent(
    role="Research Analyst",
    goal="Research with full control over MCP connections",
    backstory="Expert researcher",
    mcps=[
        # String reference — remote HTTPS MCP server
        "https://mcp.exa.ai/mcp?api_key=your_key",
        # Stdio transport for a local subprocess MCP server
        MCPServerStdio(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-filesystem"],
        ),
        # Streamable HTTP for a remote MCP server
        MCPServerHTTP(
            url="https://api.example.com/mcp",
            headers={"Authorization": "Bearer your_token"},
        ),
    ],
)
OpenAPI / REST

No first-class OpenAPI loader. REST endpoints are typically wrapped in a custom BaseTool subclass or accessed by fronting the API with an MCP server and declaring it via mcps=.

Remote agent (A2A)

Installed with the crewai[a2a] extra. The a2a= field on Agent takes an A2AClientConfig pointing at the remote agent's agent-card.json endpoint.

from crewai import Agent
from crewai.a2a import A2AClientConfig

agent = Agent(
    role="Coordinator",
    goal="Delegate work to a remote A2A agent",
    backstory="Expert delegator",
    a2a=A2AClientConfig(
        endpoint="https://remote.example.com/.well-known/agent-card.json",
        timeout=120,
        max_turns=10,
    ),
)
Generic RPC

Wrap the RPC client call inside a custom BaseTool._run or @tool-decorated function.

Mechanism

Tool sources are declared via dedicated fields on Agent: tools=, mcps=, a2a=.

Notes

OpenAPI ingestion is not provided as a first-class field; use a custom BaseTool or an MCP fronting server.

Ad

Google ADK Python

Built-in MCPToolset, OpenAPIToolset, and RemoteA2aAgent — every remote mechanism is a first-class object.
Each external tool source is represented by a dedicated class (MCPToolset, OpenAPIToolset, RemoteA2aAgent) attached to LlmAgent via tools= or sub_agents=.
Internal

Recap: a plain Python function is auto-wrapped as a FunctionTool when passed to LlmAgent(tools=[…]).

from google.adk.agents import LlmAgent
weather_agent = LlmAgent(model="gemini-2.0-flash", tools=[get_weather])
MCP — MCPToolset

An MCPToolset instance is added to an agent's tools= list. At runtime it connects to the MCP server (stdio or SSE) and exposes the server's tools to the agent.

from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset

mcp_toolset = MCPToolset(
    name="github_mcp",
    stdio_command="docker",
    stdio_args=["run", "-i", "--rm", "-e", "GITHUB_TOKEN",
                "ghcr.io/github/github-mcp-server"],
    env={"GITHUB_TOKEN": os.getenv("GITHUB_TOKEN")},
)

agent = LlmAgent(
    name="github_agent",
    model="gemini-2.0-flash",
    tools=[mcp_toolset],
)
OpenAPI — OpenAPIToolset

OpenAPIToolset parses an OpenAPI JSON or YAML specification and generates one tool per operation, with parameter schemas derived from the spec.

from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset \
    import OpenAPIToolset

toolset = OpenAPIToolset(
    spec_str=open("petstore.openapi.json").read(),
    spec_str_type="json",
)

agent = LlmAgent(
    name="petstore_agent",
    model="gemini-2.0-flash",
    tools=[toolset],
)
Remote agent — RemoteA2aAgent

RemoteA2aAgent takes an agent_card (URL or local file path) and is attached to a parent agent's sub_agents= list. The runtime handles the A2A protocol exchange.

from google.adk.agents import LlmAgent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent

prime_agent = RemoteA2aAgent(
    name="prime_agent",
    description="Agent that checks if numbers are prime.",
    agent_card="http://localhost:8001/a2a/check_prime/.well-known/agent.json",
    timeout=300.0,
)

root_agent = LlmAgent(
    model="gemini-2.0-flash",
    sub_agents=[prime_agent],
)
Generic RPC

No first-class gRPC binding. The documented pattern is to call the gRPC client inside a function tool.

Mechanism

Each remote tool source has a dedicated class (MCPToolset, OpenAPIToolset, RemoteA2aAgent).

Notes

Per-mechanism classes add API surface compared with a single-field declaration model.

Sk

Microsoft Semantic Kernel Python

Plugins are the universal abstraction: MCPStdioPlugin, add_plugin_from_openapi, all registered on the Kernel.
All tool sources are represented as plugins registered on the Kernel. With FunctionChoiceBehavior.Auto(), the Kernel resolves and invokes the matching plugin function.
Internal

Recap: a class of @kernel_function-decorated methods, registered on the Kernel.

from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function

class WeatherPlugin:
    @kernel_function(description="Gets the weather for a city")
    def get_weather(self, city: str) -> str: return f"sunny in {city}"

kernel = Kernel()
kernel.add_plugin(WeatherPlugin(), plugin_name="weather")
MCP — MCPStdioPlugin / MCPSsePlugin

An MCP server is wrapped as a plugin via an async context manager (MCPStdioPlugin for stdio transport, MCPSsePlugin for SSE) and registered on the Kernel via kernel.add_plugin.

import os
from semantic_kernel import Kernel
from semantic_kernel.connectors.mcp import MCPStdioPlugin

async with MCPStdioPlugin(
    name="Github",
    description="Github Plugin",
    command="docker",
    args=["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
          "ghcr.io/github/github-mcp-server"],
    env={"GITHUB_PERSONAL_ACCESS_TOKEN":
         os.getenv("GITHUB_PERSONAL_ACCESS_TOKEN")},
) as github_plugin:
    kernel = Kernel()
    kernel.add_plugin(github_plugin)
OpenAPI — add_plugin_from_openapi

kernel.add_plugin_from_openapi accepts an OpenAPI spec URL or file path and registers one kernel function per operation.

from semantic_kernel import Kernel
from semantic_kernel.connectors.openapi_plugin import \
    OpenAPIFunctionExecutionParameters

kernel = Kernel()
await kernel.add_plugin_from_openapi(
    plugin_name="lights",
    openapi_document_path="https://example.com/v1/swagger.json",
    execution_settings=OpenAPIFunctionExecutionParameters(
        enable_payload_namespacing=True,
    ),
)
Remote agent (A2A)

No first-class A2A primitive in the Python SDK. The documented pattern is to expose the producing agent's Kernel as an MCP server (via kernel.as_mcp_server) and consume it from the calling agent via MCPStdioPlugin or MCPSsePlugin.

Generic RPC

No first-class gRPC binding. The documented pattern is to wrap the gRPC client in a kernel function registered via kernel.add_function().

Mechanism

A single abstraction (plugin) covers internal code, MCP, and OpenAPI sources.

Notes

A2A is achieved by composing an MCP server on one side with an MCP plugin on the other; no dedicated A2A class exists.

Ma

Mastra TypeScript

MCPClient from @mastra/mcp, plus a first-class A2AAgent for remote agents.
MCP tools are attached via tools: await mcp.listTools(). Remote A2A agents are attached via the agents: map on the Agent constructor.
Internal

Recap: createTool({...}) with Zod schemas; attach via the agent's tools map.

export const weatherAgent = new Agent({
  id: 'weather-agent', model: 'openai/gpt-4o',
  tools: { weatherTool },
})
MCP — MCPClient

The MCPClient constructor accepts a servers map keyed by server name. Each server is configured with command/args for stdio transport or url for HTTP transport. listTools() returns a tools map suitable for an Agent's tools field.

import { MCPClient } from '@mastra/mcp'
import { Agent } from '@mastra/core/agent'

export const mcp = new MCPClient({
  id: 'mcp-client',
  servers: {
    wikipedia: { command: 'npx', args: ['-y', 'wikipedia-mcp'] },
    weather:   { url: new URL(`https://server.smithery.ai/.../mcp?api_key=${KEY}`) },
  },
})

export const researchAgent = new Agent({
  id: 'research-agent',
  model: 'openai/gpt-4o',
  instructions: 'Use the Wikipedia and weather MCP servers when needed.',
  tools: await mcp.listTools(),
})
OpenAPI / REST

No first-class OpenAPI loader. REST calls are typically made inside the execute function of a createTool definition.

Remote agent — A2AAgent

A2AAgent takes an A2A-compliant .well-known agent-card URL and optional headers. It is attached under a supervising agent's agents map, where Mastra auto-exposes it as a tool named agent-<key>.

import { Agent } from '@mastra/core/agent'
import { A2AAgent } from '@mastra/core/a2a'

const a2aWeatherAgent = new A2AAgent({
  url: 'https://py.acme.com/api/a2a/.well-known/a2a-weather-agent/agent-card.json',
  headers: { 'x-api-key': process.env.A2A_AGENT_API_KEY! },
})

export const weatherAgent = new Agent({
  id: 'weather-agent',
  model: 'anthropic/claude-opus-4-6',
  instructions: 'Delegate weather questions to a2aWeatherAgent.',
  agents: { a2aWeatherAgent },
})
Generic RPC

No first-class gRPC binding. Node gRPC libraries can be called inside the tool's execute function.

Mechanism

Internal tools, MCP tools, sub-agents, and A2A agents are all attached via constructor fields on Agent (tools, agents).

Notes

No OpenAPI loader; spec-to-client codegen is sourced from the broader TypeScript ecosystem.

Mf

Microsoft Agent Framework .NET

McpClientFactory for MCP; OpenAPI via the Semantic Kernel plugin pipeline.
All tool sources are exposed as AITool instances. Factories (AIFunctionFactory, McpClientFactory) produce them from method references or remote endpoints.
Internal

Recap: any C# method becomes a tool via AIFunctionFactory.Create.

AIAgent agent = client.AsAIAgent(
    model: "gpt-4o-mini",
    instructions: "You are a helpful assistant",
    tools: [ AIFunctionFactory.Create(GetWeather) ]);
MCP — McpClientFactory

An MCP client is created via McpClientFactory.CreateAsync with a transport (e.g. StdioClientTransport). ListToolsAsync returns the available tools, which are cast to AITool and passed to the agent.

await using var mcpClient = await McpClientFactory.CreateAsync(
    new StdioClientTransport(new() {
        Name = "MCPServer",
        Command = "npx",
        Arguments = ["-y", "@modelcontextprotocol/server-github"],
    }));

var mcpTools = await mcpClient.ListToolsAsync();

AIAgent agent = new AIProjectClient(endpoint, new DefaultAzureCredential())
    .AsAIAgent(
        model: deploymentName,
        tools: [.. mcpTools.Cast<AITool>()]);
OpenAPI

OpenAPI ingestion is provided via the Semantic Kernel pipeline. kernel.ImportPluginFromOpenApi registers a plugin whose functions are passed to the agent as tools.

var kernel = new Kernel();
kernel.ImportPluginFromOpenApi("GitHubPlugin",
    new Uri("https://api.github.com/openapi.json"));
// Pass kernel.Plugins["GitHubPlugin"] functions to the agent's tools list.
Remote agent (A2A)

No first-class A2A primitive in the current SDK. The documented patterns are to expose the remote agent as an MCP server and consume it via McpClientFactory, or to host remote agents through workflow primitives on Azure AI Foundry.

Generic RPC

No first-class gRPC binding. The documented pattern is to call a gRPC client (e.g. Grpc.Net.Client) inside a method exposed via AIFunctionFactory.Create.

Mechanism

OpenAPI support is inherited via the Semantic Kernel plugin pipeline. Hosted under ASP.NET Core with standard DI.

Notes

Cross-vendor agent collaboration is documented via MCP rather than a dedicated A2A primitive.

L4j

LangChain4j Java

Three-step MCP pattern: McpTransport → McpClient → McpToolProvider → AiServices.
Tools are sourced from a ToolProvider or supplied directly via @Tool-annotated objects. MCP integration is implemented as an McpToolProvider.
Internal

Recap: @Tool-annotated POJO methods, registered with AiServices.builder().tools(...).

Assistant assistant = AiServices.builder(Assistant.class)
    .chatModel(model)
    .tools(new Calculator())
    .build();
MCP — transport → client → provider

Construct an McpTransport (stdio, streamable HTTP, WebSocket, or Docker), wrap it in a DefaultMcpClient, register the client with an McpToolProvider, and attach the provider to the AI service via AiServices.builder(...).toolProvider(...).

// 1. Transport — start an MCP server as a subprocess
McpTransport transport = StdioMcpTransport.builder()
    .command(List.of("/usr/bin/npm", "exec",
        "@modelcontextprotocol/server-everything@0.6.2"))
    .build();

// 2. Client
McpClient mcpClient = DefaultMcpClient.builder()
    .key("MyMCPClient")
    .transport(transport)
    .build();

// 3. Tool provider — optionally filter which tools to expose
McpToolProvider toolProvider = McpToolProvider.builder()
    .mcpClients(mcpClient)
    .filterToolNames("get_issue", "list_issues")
    .build();

// 4. Wire into AI service
Bot bot = AiServices.builder(Bot.class)
    .chatModel(model)
    .toolProvider(toolProvider)
    .build();
OpenAPI

No first-class OpenAPI loader. REST calls are typically wrapped in @Tool-annotated methods, or accessed via an MCP fronting server.

Remote agent (A2A)

No first-class A2A primitive. Local agents can be exposed as tools; remote agents are accessed by wrapping them as an MCP server.

Generic RPC

No first-class gRPC binding. The documented pattern is to call a Java gRPC client inside a @Tool-annotated method.

Mechanism

MCP coverage includes stdio, streamable HTTP, WebSocket, and Docker transports.

Notes

Tool registration is a four-step pipeline: transport → client → provider → AI service.

Gk

Genkit Go

Dedicated plugins/mcp package; multi-server management via MCPManager.
MCP servers are registered in an MCPManager; the manager holds connections and exposes tools keyed by server name.
Internal

Recap: genkit.DefineTool with a typed input struct; pass via ai.WithTools.

getWeatherTool := genkit.DefineTool(g, "getWeather", "Gets the weather",
    func(ctx *ai.ToolContext, in WeatherInput) (string, error) { /* ... */ })

resp, _ := genkit.Generate(ctx, g, ai.WithPrompt("…"),
    ai.WithTools(getWeatherTool))
MCP — MCPManager

The plugins/mcp package provides NewMCPManager, which holds multiple MCP server connections. Servers may be declared up front via MCPManagerOptions.MCPServers or added at runtime via ConnectServer.

package main

import (
    "context"
    "log"

    "github.com/firebase/genkit/go/genkit"
    "github.com/firebase/genkit/go/plugins/mcp"
)

func main() {
    ctx := context.Background()
    g, _ := genkit.Init(ctx)

    // Configure one or more MCP servers up front
    manager, err := mcp.NewMCPManager(mcp.MCPManagerOptions{
        Name: "my-app",
        MCPServers: map[string]mcp.MCPClientOptions{
            "everything": {
                Name: "everything-server",
                Stdio: &mcp.StdioConfig{
                    Command: "npx",
                    Args:    []string{"-y", "@modelcontextprotocol/server-everything"},
                },
            },
        },
    })
    if err != nil { log.Fatal(err) }

    // Connect additional servers at runtime
    _ = manager.ConnectServer(ctx, "weather", mcp.MCPClientOptions{
        Name: "weather-server",
        Stdio: &mcp.StdioConfig{Command: "python", Args: []string{"weather_server.py"}},
    })
}
OpenAPI

No first-class OpenAPI loader. The documented pattern is to generate a Go client from the spec (e.g. with oapi-codegen) and call it inside a genkit.DefineTool handler.

Remote agent (A2A)

No first-class A2A primitive. The documented pattern is to set WithReturnToolRequests(true) to take over the tool-call loop and proxy requests to remote agents over HTTP.

Generic RPC

No first-class gRPC binding. The documented pattern is to call a Go gRPC client inside a tool handler.

Mechanism

Compiles to a single Go binary. The MCP manager uses goroutines to multiplex connections to multiple servers.

Notes

Of the four remote sources, only MCP has first-class support; OpenAPI and A2A require custom code.

Rg

Rig Rust

MCP via the rmcp Cargo feature; tool servers can mix static, dynamic, and MCP tools.
As of Rig 0.16, MCP integration uses the official Rust MCP SDK (rmcp) under the rmcp Cargo feature. The earlier mcp-core integration is deprecated and scheduled for removal in 0.18.
Internal

Recap: implement the Tool trait or use the #[rig_tool] macro; attach via the agent builder's .tool(...).

let agent = client.agent("gpt-4o")
    .preamble("You are a calculator.")
    .tool(Adder)
    .build();
MCP — rmcp integration

The rmcp Cargo feature enables the official Rust MCP SDK. The application connects to an MCP server through rmcp transports, lists tools, and attaches each to a Rig agent (or to a shared ToolServer handle) via .rmcp_tool(tool, peer) on the agent builder. The earlier mcp-core integration is deprecated and scheduled for removal in 0.18.

# Cargo.toml
rig-core = { version = "0.16", features = ["rmcp"] }
rmcp     = { version = "0.x", features = ["client", "transport-child-process"] }
use rig::providers::openai;
use rmcp::{ServiceExt, transport::child_process::TokioChildProcess};
use tokio::process::Command;

// 1. Connect to the MCP server as a subprocess
let transport = TokioChildProcess::new(
    Command::new("npx").args(["-y", "@modelcontextprotocol/server-everything"]),
)?;
let mcp_client = ().serve(transport).await?;

// 2. Discover its tools and attach them to a Rig agent
let tools = mcp_client.list_tools(Default::default()).await?;
let mut builder = openai::Client::from_env()
    .agent("gpt-4o")
    .preamble("Use the available MCP tools.");

for tool in tools.tools {
    builder = builder.rmcp_tool(tool, mcp_client.peer().clone());
}
let agent = builder.build();
OpenAPI

No first-class OpenAPI loader. The documented pattern is to generate a typed client from the spec (e.g. with progenitor or openapitor) and call it from a Rig Tool implementation.

Remote agent (A2A)

No first-class A2A primitive. Tool sharing across agents in the same process is supported by the ToolServer handle. For cross-process coordination, the documented pattern is to expose the agent as an MCP server via rmcp and consume it via the MCP pattern above.

Generic RPC

No first-class gRPC binding. The documented pattern is to call Tonic (or another Rust RPC client) inside a Tool trait implementation.

Mechanism

MCP integration uses the official Rust MCP SDK (rmcp) under the rmcp Cargo feature.

Notes

The mcp-core → rmcp migration is in progress; the mcp-core integration is scheduled for removal in 0.18.

Side-by-side · first-class remote-tool support

● = first-class API · ◐ = supported via wrapper / sibling library · ○ = no first-class support; the documented pattern is to call the source from an in-process tool body.

LangChain LangGraph CrewAI ADK Semantic Kernel Mastra MS Agent Fw LangChain4j Genkit Go Rig
Internal tools ●●●●● ●●●●●
MCP (consumer) ●●●●● ●●●●●
MCP server (exposing your tools) ◐◐◐●● ●◐◐●●
OpenAPI / REST loader ◐◐○●● ○●○○○
Remote agent (A2A) ○●●●○ ●○○○○
A2A primitive name — RemoteGraph A2AClientConfig RemoteA2aAgent via MCP A2AAgent via MCP via MCP — via MCP
MCP transports stdio · SSE · HTTP inherits LC stdio · SSE · HTTP stdio · SSE stdio · SSE stdio · HTTP stdio · HTTP stdio · HTTP · WS · Docker stdio · HTTP via rmcp
Canonical doc MCP RemoteGraph MCP · A2A MCP · A2A MCP MCP · A2A MCP MCP mcp pkg rmcp release

Summary observations

Six factual observations drawn from the per-framework data above.

MCP

1 · MCP consumer support is present in all ten frameworks

Every framework profiled here ships first-class MCP consumer support, either in core or via an officially-maintained adapter package. MCP server (producer) support is present in six: ADK, Semantic Kernel, Mastra, Genkit Go, Rig, and via separate packages in the LangChain/LangGraph and CrewAI ecosystems.

DECLARATION

2 · Declaration patterns fall into three shapes

A single constructor field listing all sources (CrewAI mcps=, Mastra tools: / agents:); a dedicated class per source (ADK MCPToolset / OpenAPIToolset / RemoteA2aAgent); or a builder pipeline (LangChain4j transport → client → provider → AI service).

A2A

3 · Four frameworks expose a first-class A2A primitive

ADK (RemoteA2aAgent), CrewAI (A2AClientConfig), Mastra (A2AAgent), and LangGraph (RemoteGraph, requires a LangGraph Platform endpoint). In the other six frameworks, remote-agent access is documented via MCP composition.

OPENAPI

4 · First-class OpenAPI ingestion is present in three frameworks

ADK (OpenAPIToolset), Semantic Kernel (kernel.add_plugin_from_openapi), and Microsoft Agent Framework (via the Semantic Kernel plugin pipeline). In the other seven frameworks, the documented pattern is to wrap an HTTP call in a custom tool body or front the API with an MCP server.

RPC

5 · No framework provides a first-class gRPC generator

None of the ten frameworks generates tools from a gRPC, Thrift, or other RPC schema. In every case the documented pattern is to call the RPC client from within an in-process tool body.

PORTABILITY

6 · MCP is the most portable tool-definition surface

An MCP server can be consumed by all ten frameworks via their documented MCP integration. Tool definitions placed inside an MCP server are therefore portable across frameworks without modification; framework-specific tool definitions are not.

Mini-glossary

Terms used above that mean slightly different things in different frameworks.

MCP (Model Context Protocol) — open JSON-RPC protocol for exposing tools, resources, and prompts to LLM agents. See modelcontextprotocol.io.
MCP transport — how the client talks to the server: stdio (server runs as a subprocess) and POST-only streamable-http are current; sse is the deprecated legacy transport retained by some frameworks, and websocket is a LangChain4j extension outside the core protocol.
Agent card (A2A) — a .well-known/agent-card.json document at the remote agent's URL describing its capabilities, skills, auth, and supported modes.
A2A (Agent-to-Agent Protocol) — open standard for one agent calling another over HTTP using the agent-card discovery + JSON-RPC messaging. Cross-framework by design.
OpenAPI toolset / toolkit — a framework-level converter from an OpenAPI 3.x spec to a set of tools, one per operation (or one generic verb-based tool).
RemoteGraph (LangGraph) — proxy object that talks to a graph deployed on LangGraph Platform; behaves identically to a local compiled graph for callers.
RemoteA2aAgent (ADK) — agent class that wraps a remote A2A endpoint; attached to a parent agent's sub_agents= list.
MCPManager (Genkit Go) — top-level container for multiple MCP server connections, with ConnectServer for dynamic add and per-server tool resolution.
ToolProvider (LangChain4j) — interface that supplies a list of tools to AiServices at build or invocation time; MCP is one such provider.
rmcp — the official Rust SDK for MCP; in Rig 0.16+ it's the canonical MCP integration (the older mcp-core is being phased out).