stratus

MCP Client

Connect to external MCP servers and use their tools locally

The MCP client connects to Model Context Protocol servers, discovers their tools, and wraps them as FunctionTool instances for use with Stratus agents.

This is different from the built-in mcpTool() which sends the MCP definition to Azure for server-side execution. McpClient connects to MCP servers locally — tools execute through the MCP server process, not through Azure.

Quick Start

mcp-client.ts
import { McpClient, Agent, run } from "@usestratus/sdk/core";

const client = new McpClient({
  command: "npx",
  args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
});

await client.connect();
const tools = await client.getTools();

const agent = new Agent({
  name: "file-assistant",
  model,
  tools, // MCP tools work like any other FunctionTool
});

const result = await run(agent, "List all files in /tmp");
await client.disconnect();

Configuration

McpClient supports local stdio servers and remote Streamable HTTP servers.

Stdio

const client = new McpClient({
  transport: "stdio", // optional when command is present
  command: "node", // Command to spawn
  args: ["mcp-server.js"], // Arguments
  env: { API_KEY: "..." }, // Environment variables
  cwd: "/path/to/server", // Working directory
});
OptionTypeDescription
transport"stdio" | "streamable-http"Transport. Defaults to "stdio" when command is provided, otherwise "streamable-http"
commandstringCommand to spawn for stdio MCP servers
argsstring[]Arguments passed to the command
envRecord<string, string>Environment variables (merged with process.env)
cwdstringWorking directory for the server process
requestTimeoutMsnumberJSON-RPC request timeout. Defaults to 30000

Streamable HTTP

Use Streamable HTTP for remote MCP servers:

remote-mcp.ts
const client = new McpClient({
  transport: "streamable-http",
  url: "https://mcp.example.com",
  headers: {
    Authorization: `Bearer ${process.env.MCP_API_KEY}`,
  },
});
OptionTypeDescription
urlstringStreamable HTTP MCP endpoint
headersRecord<string, string> | () => Record<string, string> | Promise<Record<string, string>>Static or async headers for HTTP requests
cacheToolsListbooleanCache tools/list results after the first call
toolFilterstring[] | (tool) => boolean | Promise<boolean>Filter discovered tools before exposing them
namePrefixstringPrefix exposed tool names to avoid collisions across servers
requestTimeoutMsnumberJSON-RPC request timeout. Defaults to 30000

Azure Authentication

For remote MCP servers protected by Entra ID or another bearer token provider, use azureMcpHeaders():

azure-mcp.ts
import {
  DefaultAzureCredential,
  getBearerTokenProvider,
} from "@azure/identity";
import { McpClient, azureMcpHeaders } from "@usestratus/sdk/core";

const tokenProvider = getBearerTokenProvider(
  new DefaultAzureCredential(),
  "api://your-mcp-server/.default",
);

const client = new McpClient({
  transport: "streamable-http",
  url: "https://mcp.contoso.com",
  headers: azureMcpHeaders(tokenProvider, {
    "x-tenant": "contoso",
  }),
});

headers can be async, so tokens are fetched when each JSON-RPC request is sent.

Lifecycle

// 1. Connect — spawns process, runs MCP initialize handshake
await client.connect();

// 2. Discover tools
const tools = await client.getTools();

// 3. Use tools with agents (tools call back to MCP server on execute)
const result = await run(agent, input);

// 4. Disconnect — kills process, rejects pending requests
await client.disconnect();

Async Dispose

McpClient supports Symbol.asyncDispose for automatic cleanup:

await using client = new McpClient({ command: "node", args: ["server.js"] });
await client.connect();
const tools = await client.getTools();
// client.disconnect() called automatically when scope exits

Tool Discovery

getTools() returns FunctionTool[] instances that proxy execution to the MCP server:

const tools = await client.getTools();

for (const tool of tools) {
  console.log(tool.name); // MCP tool name
  console.log(tool.description); // MCP tool description
}

Each tool's inputSchema from the MCP server is forwarded to the LLM as the JSON Schema parameter definition, so the model knows exactly which arguments to provide.

Filtering and Prefixing

When connecting multiple MCP servers, filter the tools you expose and prefix names to prevent collisions:

const client = new McpClient({
  transport: "streamable-http",
  url: "https://mcp.example.com",
  namePrefix: "docs__",
  toolFilter: ["search", "fetch_page"],
  cacheToolsList: true,
});

const tools = await client.getTools();
console.log(tools.map((tool) => tool.name)); // ["docs__search", "docs__fetch_page"]

Low-Level API

For advanced use cases, you can call MCP methods directly:

// List available tools
const definitions = await client.listTools();

// Call a specific tool
const result = await client.callTool("read_file", { path: "/tmp/test.txt" });

Transport

McpClient supports:

  • stdio — spawns a local MCP server process and communicates with JSON-RPC over stdin/stdout using Content-Length framing.
  • streamable-http — sends JSON-RPC over HTTP POST and accepts JSON or text/event-stream JSON-RPC responses.

Use local McpClient when you want tools to execute in your app process or infrastructure. Use the built-in mcpTool() when you want Azure's Responses API to connect to the remote MCP server server-side.

Edit on GitHub

Last updated on

On this page