samai-sdk
Core concepts

MCP (Model Context Protocol)

createMCPClient() connects to any MCP server and exposes its tools as ordinary ToolDefinitions — mix them into an agent's tools array alongside locally-defined tools, createWebSearchTool(), whatever.

Needs the optional @modelcontextprotocol/sdk peer dependency (npm install @modelcontextprotocol/sdk).

Local servers over stdio

A local MCP server spawned as a child process — its tools become part of the agent's normal tool list:

mcp-stdio.ts
import { createClient, anthropic, defineAgent, runAgent, createMCPClient } from "samai-sdk";

// Local server, spawned as a child process over stdio:
const filesystem = createMCPClient({
  transport: { transport: "stdio", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] },
  toolPrefix: "fs", // avoids name collisions if you wire up more than one MCP server
});

const client = createClient({ provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }) });

const agent = defineAgent({
  name: "file_assistant",
  instructions: "Help the user inspect and organize files in /tmp using the fs__ tools.",
  model: "claude-sonnet-4-6",
  tools: await filesystem.tools(),
});

const result = await runAgent(client, agent, "What files are in /tmp?");
await filesystem.close(); // kills the spawned process

Remote servers over HTTP / SSE

Remote servers work the same way, over the current Streamable HTTP transport (or legacy SSE, for older servers):

mcp-http.ts
const acme = createMCPClient({
  transport: { transport: "http", url: "https://mcp.acme.com/mcp", headers: { Authorization: `Bearer ${token}` } },
  toolPrefix: "acme",
});

Gating MCP tools behind approval

Pass requiresApproval (boolean, or (toolName, args) => boolean | Promise<boolean>) to gate every tool from a server behind the same approval flow as any other tool — see Guardrails & approval.

mcp-approval.ts
const acme = createMCPClient({
  transport: { transport: "http", url: "https://mcp.acme.com/mcp" },
  toolPrefix: "acme",
  requiresApproval: (toolName, args) => toolName === "acme__delete_record",
});

Schemas pass through untouched

Each MCP tool's JSON Schema reaches the model exactly as the server declares it (via ToolDefinition.rawJsonSchema, an escape hatch every built-in provider adapter checks first) — nothing is lost round-tripping through zod. Argument validation before a call reaches the server is a permissive “is this an object” check, since the server itself is the source of truth for its own schema. Call results come back as structuredContent when the server provides it, otherwise as flattened text.