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),