Skip to main content
Expose multiple upstream MCP servers through one Fentaris endpoint.
Use multi-server routing when teams ship separate MCP servers but clients should connect to a single proxy. Fentaris keeps tool names, policy, credentials, and observability centralized.

Quick Start

Declare each upstream MCP server with mcp(...) and attach it to one fentaris(...) proxy.
import { fentaris, mcp, stdio, streamableHttp } from "@fentaris/core";

const proxy = fentaris({
  port: 3000,
  path: "/mcp",
  servers: [
    mcp("filesystem", {
      transport: stdio({
        command: "npx",
        args: ["-y", "@modelcontextprotocol/server-filesystem", "/srv/shared"],
      }),
    }),
    mcp("diagnostics", {
      transport: stdio({ command: "diagnostics-mcp-server" }),
    }),
    mcp("analytics", {
      transport: streamableHttp({ url: "https://analytics.internal/mcp" }),
    }),
  ],
});
Keep server names stable across environments. Tool names are derived from the server name, so clients may depend on prefixes such as analytics__query.

List Tools Across Servers

Fentaris prefixes tool names with the upstream server name to avoid collisions.
const tools = await proxy.listTools();
This lets different teams expose similar tool names without sharing one server process. For example, billing__create_invoice and support__create_ticket can live behind the same public endpoint.

Add Per-server Environment

Use env on mcp(...) to inject trusted values into a local stdio server.
const proxy = fentaris({
  servers: [
    mcp("analytics", {
      transport: stdio({ command: "analytics-mcp-server" }),
      env: (user) => ({
        TENANT_ID: user.tenantId ?? "unknown",
      }),
    }),
  ],
});
Use environment injection for tenant identifiers and deployment-owned secrets. Do not copy client-provided values into env without validation.

Route By Group

Use group(...) to expose different upstream MCP servers to different callers.
import { fentaris, group, mcp, policy, stdio, user } from "@fentaris/core";

const proxy = fentaris({
  groups: [
    group({
      id: "support",
      users: [user("alice")],
      servers: [
        mcp("support", {
          transport: stdio({ command: "support-mcp-server" }),
        }),
      ],
      policy: policy("support").mcp("support").allow("*"),
    }),
    group({
      id: "analysts",
      users: [user("bob")],
      servers: [
        mcp("analytics", {
          transport: stdio({ command: "analytics-mcp-server" }),
        }),
      ],
      policy: policy("analysts").mcp("analytics").allow("query"),
    }),
  ],
});
Group-scoped servers reduce accidental discovery. A caller only sees tools from servers available to their resolved subject.

Add Server-specific Middleware

Use server handles when validation only applies to one upstream MCP server. Inside a group handle, server(name) scopes middleware to subjects in that group and that upstream server.
proxy.group("analysts").server("analytics").use(async (ctx, next) => {
  if (!ctx.subject?.hasGroup("analysts")) {
    return ctx.deny("Analytics tools require the analysts group.");
  }

  ctx.log.info("analytics.allowed", {
    subject: ctx.subject.id,
  });

  return next();
});
Use proxy.mcp("analytics") for server-wide middleware, and prefer scoped handles over global middleware when the rule only applies to one domain.

Separate Concerns By Server

Small upstream MCP servers make ownership and policy easier to reason about.
  • filesystem for file operations.
  • analytics for data access.
  • support for tickets and workflows.
  • diagnostics for health checks and runtime inspection.
Use displayName for human-friendly UI labels only. Keep the name stable for tool prefixes, policy, logs, and client expectations.

Low-Level API

The class constructors remain available for compatibility and advanced embedding. Prefer declaration helpers for new applications.
import { McpProxy, McpServer, StdioTransport } from "@fentaris/core";

const analytics = new McpServer({
  name: "analytics",
  transport: new StdioTransport({
    command: "analytics-mcp-server",
  }),
});

const proxy = new McpProxy({
  servers: [analytics],
});