Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/benchmark/server/mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export async function startMesh(port: number): Promise<MeshServerHandle> {
gatewayId: string,
strategy?: VirtualMCPtoolSelectionStrategy,
): string => {
const url = new URL(`/mcp/gateway/${gatewayId}`, baseUrl);
const url = new URL(`/mcp/${gatewayId}`, baseUrl);
if (strategy) {
url.searchParams.set("mode", strategy);
}
Expand Down
5 changes: 2 additions & 3 deletions apps/docs/client/src/content/en/mcp-mesh/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ Proxies MCP requests to a single upstream connection.

### Agent (virtual server)

- `POST /mcp/gateway/:gatewayId`
- `POST /mcp/gateway` (default Agent; requires `x-org-id` or `x-org-slug`)
- `POST /mcp/:connectionId` (where `connectionId` is the Agent's connection ID)

Aggregates tools/resources/prompts across multiple connections.
Agents are virtual MCPs stored as connections with `connection_type = 'VIRTUAL'`. They aggregate tools/resources/prompts across multiple connections. Supports `?mode=` query parameter for tool selection strategy (passthrough, smart_tool_selection, code_execution).

## OAuth discovery

Expand Down
3 changes: 1 addition & 2 deletions apps/docs/client/src/content/en/mcp-mesh/connect-clients.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ There are two common ways to connect clients to the Mesh:
## Endpoints you’ll use

- **Management MCP**: `POST /mcp`
- **Connection proxy**: `POST /mcp/:connectionId`
- **Agent (virtual server)**: `POST /mcp/gateway/:gatewayId` (or omit `:gatewayId` to use the org default Agent with headers)
- **Connection proxy**: `POST /mcp/:connectionId` (works for both regular connections and Agents/virtual MCPs)

<Callout type="tip">
If your goal is a "single MCP URL" for a curated tool surface, use **Agents**. If you want a direct pipe to one upstream MCP, use **Connection proxy**.
Expand Down
7 changes: 2 additions & 5 deletions apps/docs/client/src/content/en/mcp-mesh/mcp-gateways.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@ This is how you create a curated endpoint for clients without exposing every too

## Endpoint

- `POST /mcp/gateway/:gatewayId`
- `POST /mcp/:connectionId` (where `connectionId` is the Agent's connection ID)

You can also omit `:gatewayId` and rely on the organization's default Agent by providing one of these headers:

- `x-org-id`, or
- `x-org-slug`
Agents are virtual MCPs stored as connections with `connection_type = 'VIRTUAL'`. They use the same endpoint pattern as regular connections. They support the `?mode=` query parameter for the tool exposure strategy (passthrough, smart_tool_selection, code_execution).

## Selection modes

Expand Down
5 changes: 2 additions & 3 deletions apps/docs/client/src/content/pt-br/mcp-mesh/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ Faz proxy de requisições MCP para uma única connection upstream.

### Agent (MCP virtual)

- `POST /mcp/gateway/:gatewayId` (compatibilidade: o path HTTP ainda usa `gateway`, mas refere-se ao Agent)
- `POST /mcp/gateway` (Agent padrão da org; requer `x-org-id` ou `x-org-slug`)
- `POST /mcp/:connectionId` (onde `connectionId` é o ID da connection do Agent)

Faz proxy de requisições MCP para um agent (agregação + estratégia de exposição).
Agents são MCPs virtuais armazenados como connections com `connection_type = 'VIRTUAL'`. Eles agregam tools/resources/prompts de múltiplas connections. Suporta parâmetro de query `?mode=` para estratégia de seleção de tools (passthrough, smart_tool_selection, code_execution).

## Endpoints de autenticação (alto nível)

Expand Down
24 changes: 20 additions & 4 deletions apps/mesh/src/api/routes/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
import { getMonitoringConfig } from "@/core/config";
import { createClient } from "@/mcp-clients";
import { buildRequestHeaders } from "@/mcp-clients/outbound/headers";
import {
parseStrategyFromMode,
type ToolSelectionStrategy,
} from "@/mcp-clients/virtual-mcp";
import type { ConnectionEntity } from "@/tools/connection/schema";
import type { ServerClient } from "@decocms/bindings/mcp";
import { createServerFromClient } from "@decocms/mesh-sdk";
Expand Down Expand Up @@ -230,7 +234,10 @@ const DEFAULT_SERVER_CAPABILITIES = {
async function createMCPProxyDoNotUseDirectly(
connectionIdOrConnection: string | ConnectionEntity,
ctx: MeshContext,
{ superUser }: { superUser: boolean }, // this is basically used for background workers that needs cross-organization access
{
superUser,
strategy = "passthrough",
}: { superUser: boolean; strategy?: ToolSelectionStrategy }, // this is basically used for background workers that needs cross-organization access
): Promise<MCPProxyClient> {
// Get connection details
const connection =
Expand All @@ -255,7 +262,7 @@ async function createMCPProxyDoNotUseDirectly(
}

// Create client early - needed for listTools and other operations
const client = await createClient(connection, ctx, superUser);
const client = await createClient(connection, ctx, { superUser, strategy });

// List tools from downstream connection
// Uses indexed tools if available, falls back to client for connections without cached tools
Expand Down Expand Up @@ -598,24 +605,29 @@ async function createMCPProxyDoNotUseDirectly(
export async function createMCPProxy(
connectionIdOrConnection: string | ConnectionEntity,
ctx: MeshContext,
strategy?: ToolSelectionStrategy,
) {
return createMCPProxyDoNotUseDirectly(connectionIdOrConnection, ctx, {
superUser: false,
strategy,
});
}

/**
* Create a MCP proxy for a downstream connection with super user access
* @param connectionIdOrConnection - The connection ID or connection entity
* @param ctx - The mesh context
* @param strategy - Optional tool selection strategy
* @returns The MCP proxy
*/
export async function dangerouslyCreateSuperUserMCPProxy(
connectionIdOrConnection: string | ConnectionEntity,
ctx: MeshContext,
strategy?: ToolSelectionStrategy,
) {
return createMCPProxyDoNotUseDirectly(connectionIdOrConnection, ctx, {
superUser: true,
strategy,
});
}

Expand Down Expand Up @@ -645,7 +657,9 @@ app.all("/:connectionId", async (c) => {

try {
try {
const client = await ctx.createMCPProxy(connectionId);
// Parse strategy from query string mode parameter (defaults to passthrough)
const strategy = parseStrategyFromMode(c.req.query("mode"));
const client = await ctx.createMCPProxy(connectionId, strategy);

// Create server from client using the bridge
const server = createServerFromClient(client, {
Expand Down Expand Up @@ -710,7 +724,9 @@ app.all("/:connectionId/call-tool/:toolName", async (c) => {
const ctx = c.get("meshContext");

try {
const client = await ctx.createMCPProxy(connectionId);
// Parse strategy from query string mode parameter (defaults to passthrough)
const strategy = parseStrategyFromMode(c.req.query("mode"));
const client = await ctx.createMCPProxy(connectionId, strategy);
const result = await client.callTool({
name: toolName,
arguments: await c.req.json(),
Expand Down
7 changes: 5 additions & 2 deletions apps/mesh/src/core/context-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,8 +827,11 @@ export async function createMeshContextFactory(
),
},
eventBus: config.eventBus,
createMCPProxy: async (conn: string | ConnectionEntity) => {
return await createMCPProxy(conn, ctx);
createMCPProxy: async (
conn: string | ConnectionEntity,
strategy?: Parameters<typeof createMCPProxy>[2],
) => {
return await createMCPProxy(conn, ctx, strategy);
},
getOrCreateClient: clientPool,
};
Expand Down
1 change: 1 addition & 0 deletions apps/mesh/src/core/mesh-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ export interface MeshContext {
// Utility for creating MCP Proxies
createMCPProxy: (
conn: Parameters<typeof createMCPProxy>[0],
strategy?: Parameters<typeof createMCPProxy>[2],
) => ReturnType<typeof createMCPProxy>;

// Client pool for STDIO connection reuse (LRU cache)
Expand Down
9 changes: 5 additions & 4 deletions apps/mesh/src/mcp-clients/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { MeshContext } from "@/core/mesh-context";
import type { ConnectionEntity } from "@/tools/connection/schema";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { createOutboundClient } from "./outbound";
import { createVirtualClient } from "./virtual-mcp";
import { createVirtualClient, type ToolSelectionStrategy } from "./virtual-mcp";

/**
* Create an MCP client from a connection entity
Expand All @@ -20,16 +20,17 @@ import { createVirtualClient } from "./virtual-mcp";
*
* @param connection - Connection entity from database
* @param ctx - Mesh context for creating clients
* @param superUser - Whether to use superuser mode for background processes
* @param options - Options object with superUser flag and optional strategy
* @returns Client instance connected to the MCP server
*/
export async function createClient(
connection: ConnectionEntity,
ctx: MeshContext,
superUser = false,
options: { superUser?: boolean; strategy?: ToolSelectionStrategy } = {},
): Promise<Client> {
const { superUser = false, strategy = "passthrough" } = options;
if (connection.connection_type === "VIRTUAL") {
return createVirtualClient(connection, ctx);
return createVirtualClient(connection, ctx, strategy);
}
return createOutboundClient(connection, ctx, superUser);
}
4 changes: 3 additions & 1 deletion apps/mesh/src/mcp-clients/virtual-mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ function isSelfReferencingVirtual(
*
* @param connection - Connection entity with VIRTUAL type
* @param ctx - Mesh context for creating proxies
* @param strategy - Tool selection strategy (defaults to passthrough)
* @returns Client instance with aggregated tools, resources, and prompts
*/
export async function createVirtualClient(
connection: ConnectionEntity,
ctx: MeshContext,
strategy: ToolSelectionStrategy = "passthrough",
): Promise<Client> {
// Virtual MCP ID is the connection ID
const virtualMcpId = connection.id;
Expand All @@ -49,7 +51,7 @@ export async function createVirtualClient(
}

// Create client from virtual MCP entity
return createVirtualClientFrom(virtualMcp, ctx, "passthrough");
return createVirtualClientFrom(virtualMcp, ctx, strategy);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ export function VirtualMCPShareModal({
};

// Build URL with mode query parameter
// Virtual MCPs (agents) are accessed via the virtual-mcp endpoint
// Virtual MCPs (agents) are accessed via the /mcp/:connectionId endpoint
const virtualMcpUrl = new URL(
`/mcp/virtual-mcp/${virtualMcp.id}`,
`/mcp/${virtualMcp.id}`,
window.location.origin,
);
virtualMcpUrl.searchParams.set("mode", mode);
Expand Down