Skip to content

Consuming Remote MCP Tools

registerMcpRemoteTools() imports tools from a remote MCP server into the local ToolRegistry as proxy tools. Each imported tool delegates execution to the remote server via tools/call over Streamable HTTP.

Import

import {
  registerMcpRemoteTools,
  McpRemoteServerConfig,
  McpAuth,
  RegisterMcpRemoteToolsResult,
} from "@modernpath/agent-framework";

Signature

async function registerMcpRemoteTools(
  registry: ToolRegistry,
  config: McpRemoteServerConfig,
): Promise<RegisterMcpRemoteToolsResult>
Parameter Type Description
registry ToolRegistry The tool registry to import tools into.
config McpRemoteServerConfig Remote server configuration.

McpRemoteServerConfig

interface McpRemoteServerConfig {
  name: string;
  url: string;
  auth?: McpAuth;
  toolPrefix?: string;
  category?: string;
  tags?: string[];
  allowTools?: string[];
  denyTools?: string[];
  denyToolPrefix?: string;
  timeoutMs?: number;
  retries?: number;
}
Property Type Default Description
name string -- Required. Friendly name for diagnostics and tagging.
url string -- Required. Base URL for the Streamable HTTP MCP endpoint (typically ends with /mcp).
auth McpAuth { type: "none" } Authentication method.
toolPrefix string "" Prefix applied to imported tool names (e.g. "acme." turns getTelemetry into acme.getTelemetry).
category string "external" Category assigned to imported tools in the registry.
tags string[] [] Additional tags for imported tools. "mcp" and the server name are always added.
allowTools string[] -- Whitelist of remote tool names to import. If provided, only listed tools are imported.
denyTools string[] -- Blacklist of remote tool names to skip.
denyToolPrefix string -- Deny importing tools whose remote name starts with this prefix. Used for loop safety.
timeoutMs number 30000 Request timeout in milliseconds.
retries number 0 Number of retry attempts for transient network errors (connection refused, reset, timeout). Does NOT retry HTTP 4xx errors. Progressive delay: 500ms, 1000ms, 2000ms, etc.

McpAuth

type McpAuth =
  | { type: "none" }
  | { type: "bearer"; getToken: (ctx: { userId: number; auditingId: number }) => Promise<string> | string };
Type Description
{ type: "none" } No authentication.
{ type: "bearer", getToken } Bearer token authentication. getToken is called with the user context from tool execution parameters.

RegisterMcpRemoteToolsResult

interface RegisterMcpRemoteToolsResult {
  registered: string[];
  skipped: Array<{ tool: string; reason: string }>;
  remoteTools: Array<{ name: string; description?: string }>;
}
Field Type Description
registered string[] Local names of successfully registered proxy tools.
skipped array Tools that were not imported, with reasons (denied, duplicate, etc.).
remoteTools array All tools discovered on the remote server.

Handshake Flow

The function performs the full MCP lifecycle:

  1. Initialize -- sends initialize JSON-RPC request, receives server capabilities and session ID
  2. Notification -- sends notifications/initialized to complete handshake
  3. Discovery -- calls tools/list to discover available tools
  4. Registration -- registers each tool as a proxy in the local ToolRegistry

Session Management

The returned proxy tools share a session ID. If the session expires (e.g. server redeployment), the proxy automatically re-establishes the session on the next call. Concurrent callers piggyback on a single re-initialization to avoid duplicate handshakes.

Tool Metadata

Each imported tool is registered with metadata that includes:

  • The original remote tool's inputSchema and outputSchema (stored as schema.inputJsonSchema and schema.outputJsonSchema)
  • Category from config (default: "external")
  • Tags: config tags plus "mcp" and the server name

Agents can retrieve schemas at runtime via registry.getMetadata(toolName)?.schema?.outputJsonSchema.

Code Example

import { ToolRegistry, registerMcpRemoteTools } from "@modernpath/agent-framework";

const registry = new ToolRegistry();

// Import tools from a remote MCP server
const result = await registerMcpRemoteTools(registry, {
  name: "acme-iot",
  url: "https://api.acme.com/mcp",
  auth: {
    type: "bearer",
    getToken: async ({ userId, auditingId }) => {
      return await authService.getServiceToken(userId, auditingId);
    },
  },
  toolPrefix: "acme.",
  category: "telemetry",
  tags: ["iot", "building"],
  allowTools: ["getTelemetryBundle", "getAlerts", "getDeviceStatus"],
  denyToolPrefix: "internal.",
  timeoutMs: 30000,
  retries: 2,
});

console.log("Registered tools:", result.registered);
// ["acme.getTelemetryBundle", "acme.getAlerts", "acme.getDeviceStatus"]

console.log("Skipped:", result.skipped);
// [{ tool: "internal.debug", reason: "Denied by denyToolPrefix (internal.)" }]

// Use the imported tool
const telemetry = await registry.execute("acme.getTelemetryBundle", {
  userId: 1,
  auditingId: 100,
  sensorId: "sensor-123",
  timeRange: "24h",
});

// Access tool schema
const schema = registry.getMetadata("acme.getTelemetryBundle")?.schema;
console.log(schema?.outputJsonSchema);