Skip to main content
The AG-UI integration ships two complementary exports. AguiAdapter converts MCP tools into the AG-UI protocol format so a remote agent can discover and invoke them. McpMiddleware (created via createMcpMiddleware) intercepts tool calls from that remote agent and executes them server-side against your MCP servers — bridging the gap when the agent itself runs in a separate Python process (LangGraph, AutoGen, CopilotKit, etc.).

Installation

npm install @mcp-ts/sdk @ag-ui/client rxjs

Imports

import { AguiAdapter } from '@mcp-ts/sdk/adapters/agui-adapter';
import { createMcpMiddleware, McpMiddleware } from '@mcp-ts/sdk/adapters/agui-middleware';

AguiAdapter

AguiAdapter takes an MCPClient or MultiSessionClient and produces two tool representations:
  • getTools() — returns AguiTool[], each with a handler for server-side execution.
  • getToolDefinitions() — returns AguiToolDefinition[], JSON Schema definitions without handlers, suitable for sending to a remote agent.

new AguiAdapter(client, options?)

ParameterTypeDescription
clientMCPClient | MultiSessionClientThe MCP client to source tools from.
optionsAguiAdapterOptionsOptional configuration (see below).

AguiAdapterOptions

FieldTypeDefaultDescription
prefixstringServer ID (8 chars)Namespace prefix added to every tool name.
toolRouterToolRouterUse a ToolRouter for intelligent tool filtering.

adapter.getTools()

Returns Promise<AguiTool[]>. Each AguiTool includes:
interface AguiTool {
  name: string;                              // tool_<prefix>_<toolName>
  description: string;
  parameters?: Record<string, any>;          // Cleaned JSON Schema
  _meta?: Record<string, any>;               // Session metadata
  handler?: (args: any) => Promise<any>;     // Executes the MCP tool
}

adapter.getToolDefinitions()

Returns Promise<AguiToolDefinition[]>. Same shape as AguiTool but without handler — safe to serialize and send to a remote agent.
interface AguiToolDefinition {
  name: string;
  description: string;
  parameters: Record<string, any>;
  _meta?: Record<string, any>;
}

Schema cleaning

The adapter automatically strips JSON Schema properties that Pydantic’s strict validation rejects ($schema, $id, $comment, $defs, prefill, examples, enumTitles, enumDescriptions, and others). This makes the tool definitions compatible with Google ADK, LangGraph, and other Python frameworks that use Pydantic for schema validation.

McpMiddleware / createMcpMiddleware

McpMiddleware is an AG-UI Middleware subclass. It injects MCP tool schemas into each agent run and intercepts TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, and RUN_FINISHED events. When the agent signals it wants to call an MCP tool, the middleware executes it locally against your MCP server and feeds the result back as a TOOL_CALL_RESULT event, then triggers a continuation run so the agent can incorporate the result.

createMcpMiddleware(config)

Factory function that creates and returns an AG-UI-compatible middleware function.
function createMcpMiddleware(config: McpMiddlewareConfig): (
  input: RunAgentInput,
  next: AbstractAgent
) => Observable<BaseEvent>

McpMiddlewareConfig

FieldTypeDescription
toolsAguiTool[]Pre-loaded tools with handlers, typically from adapter.getTools().

Event flow

EventWhat the middleware does
TOOL_CALL_STARTRecords the tool name and call ID; marks MCP tools as pending.
TOOL_CALL_ARGSAccumulates streamed argument deltas into a buffer.
TOOL_CALL_ENDMarks the call as complete (args are fully buffered).
RUN_FINISHEDExecutes all pending MCP tools, emits TOOL_CALL_RESULT events, then triggers a continuation run with the results appended to message history.
TOOL_CALL_RESULTEmitted by the middleware with the serialized MCP tool output.

Complete Next.js example

The following example shows a complete Next.js App Router API route that connects to a remote Python agent (running LangGraph) and executes MCP tools server-side.
1

Connect to your MCP servers

Create a MultiSessionClient and call connect() inside the route handler. Each request gets its own client scoped to the authenticated user.
// app/api/agent/route.ts
import { NextRequest } from 'next/server';
import { HttpAgent } from '@ag-ui/client';
import { AguiAdapter } from '@mcp-ts/sdk/adapters/agui-adapter';
import { createMcpMiddleware } from '@mcp-ts/sdk/adapters/agui-middleware';

export const POST = async (req: NextRequest) => {
  const { MultiSessionClient } = await import('@mcp-ts/sdk/server');

  const client = new MultiSessionClient('user_123');
  await client.connect();
2

Get tools with AguiAdapter

Call adapter.getTools() to obtain AguiTool[] — each tool has a handler that the middleware will call when the remote agent requests it.
  const adapter = new AguiAdapter(client);
  const mcpTools = await adapter.getTools();
3

Attach middleware to the remote agent

Create an HttpAgent pointing at your Python backend, then attach the MCP middleware. The middleware injects tool schemas into every run and intercepts tool calls.
  const remoteAgent = new HttpAgent({
    url: 'http://127.0.0.1:8000/agent',
  });

  remoteAgent.use(createMcpMiddleware({ tools: mcpTools }));
4

Run the agent and stream the response

Call remoteAgent.run(input) and pipe the resulting observable to your response stream.
  const body = await req.json();
  const observable = remoteAgent.run(body);

  // Convert observable to a readable stream for the response
  const stream = new ReadableStream({
    start(controller) {
      observable.subscribe({
        next: (event) => controller.enqueue(
          new TextEncoder().encode(JSON.stringify(event) + '\n')
        ),
        error: (err) => controller.error(err),
        complete: () => controller.close(),
      });
    },
  });

  return new Response(stream, {
    headers: { 'Content-Type': 'application/x-ndjson' },
  });
};

When to use each export

AguiAdapter.getTools()

Use when your Next.js (or other Node.js) server needs to execute MCP tools on behalf of a remote agent. The handler functions run server-side and have access to authenticated MCP sessions.

AguiAdapter.getToolDefinitions()

Use when you need to send tool schemas to a remote agent without handlers — for example, to pre-populate a client-side agent with available tool names and descriptions.
Use McpMiddleware (via createMcpMiddleware) whenever the AI agent runs on a separate backend that cannot access your MCP sessions directly. The middleware handles the full round-trip: it intercepts the tool call, executes it against your MCP server, and feeds the result back into the agent’s context so it can continue reasoning.

Tool naming

Tool names follow the pattern tool_<prefix>_<toolName>. The prefix defaults to the first eight characters of the server ID with hyphens removed.
tool_abcd1234_web_search
tool_abcd1234_read_file
Set an explicit prefix in AguiAdapterOptions to control the namespace:
const adapter = new AguiAdapter(client, { prefix: 'myapp' });
// tool_myapp_web_search, tool_myapp_read_file, …

Disconnected clients

If client.isConnected() returns false when getTools() or getToolDefinitions() is called, the adapter returns an empty array []. The middleware will have no tools to inject and will pass runs through to the remote agent unmodified.