From 211b39f3113d4e6ecab84be0a83f4e9c9dea127f Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sat, 3 Jan 2026 12:47:11 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20fix:=20Restrict=20MCP=20Stdio=20?= =?UTF-8?q?Transport=20via=20API=20(#11184)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated MCP server configuration tests to reject stdio transport configurations, ensuring that only remote transports (SSE, HTTP, WebSocket) are allowed via the API. - Enhanced documentation to clarify that stdio transport is excluded from user input for security, as it allows arbitrary command execution and should only be configured by administrators through YAML files. --- api/server/routes/__tests__/mcp.spec.js | 16 ++++++---------- packages/data-provider/src/mcp.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/server/routes/__tests__/mcp.spec.js b/api/server/routes/__tests__/mcp.spec.js index 3b0d20feac..26d7988f0a 100644 --- a/api/server/routes/__tests__/mcp.spec.js +++ b/api/server/routes/__tests__/mcp.spec.js @@ -1553,23 +1553,19 @@ describe('MCP Routes', () => { ); }); - it('should create MCP server with valid stdio config', async () => { - const validConfig = { + it('should reject stdio config for security reasons', async () => { + const stdioConfig = { type: 'stdio', command: 'node', args: ['server.js'], title: 'Test Stdio Server', }; - mockRegistryInstance.addServer.mockResolvedValue({ - serverName: 'test-stdio-server', - config: validConfig, - }); + const response = await request(app).post('/api/mcp/servers').send({ config: stdioConfig }); - const response = await request(app).post('/api/mcp/servers').send({ config: validConfig }); - - expect(response.status).toBe(201); - expect(response.body.serverName).toBe('test-stdio-server'); + // Stdio transport is not allowed via API - only admins can configure it via YAML + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); }); it('should return 400 for invalid configuration', async () => { diff --git a/packages/data-provider/src/mcp.ts b/packages/data-provider/src/mcp.ts index 53ca235de5..50a9746357 100644 --- a/packages/data-provider/src/mcp.ts +++ b/packages/data-provider/src/mcp.ts @@ -221,12 +221,16 @@ const omitServerManagedFields = >(schema: T }); /** - * MCP Server configuration that comes from UI input only + * MCP Server configuration that comes from UI/API input only. * Omits server-managed fields like startup, timeout, customUserVars, etc. * Allows: title, description, url, iconPath, oauth (user credentials) + * + * SECURITY: Stdio transport is intentionally excluded from user input. + * Stdio allows arbitrary command execution and should only be configured + * by administrators via the YAML config file (librechat.yaml). + * Only remote transports (SSE, HTTP, WebSocket) are allowed via the API. */ export const MCPServerUserInputSchema = z.union([ - omitServerManagedFields(StdioOptionsSchema), omitServerManagedFields(WebSocketOptionsSchema), omitServerManagedFields(SSEOptionsSchema), omitServerManagedFields(StreamableHTTPOptionsSchema),