From 601bc1f5d5303e50fbc190b87bc0491c38f788c1 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 12 Feb 2026 14:00:40 -0800 Subject: [PATCH] fix: inject NPM_CONFIG_CACHE into STDIO MCP subprocess env for Docker npm/npx needs a writable cache directory. In containers the default (~/.npm) may not exist or be read-only, causing STDIO MCP servers launched via npx to fail with ENOENT. Inject NPM_CONFIG_CACHE=/tmp/.npm_mcp_cache into the subprocess env when not already set. --- litellm/constants.py | 5 +++++ .../_experimental/mcp_server/mcp_server_manager.py | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index 88c57d3ce4c..c41d2ece4b5 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -101,6 +101,11 @@ MCP_OAUTH2_TOKEN_CACHE_MAX_SIZE = int( MCP_OAUTH2_TOKEN_CACHE_DEFAULT_TTL = int( os.getenv("MCP_OAUTH2_TOKEN_CACHE_DEFAULT_TTL", "3600") ) + +# Default npm cache directory for STDIO MCP servers. +# npm/npx needs a writable cache dir; in containers the default (~/.npm) +# may not exist or be read-only. /tmp is always writable. +MCP_NPM_CACHE_DIR = "/tmp/.npm_mcp_cache" MCP_OAUTH2_TOKEN_CACHE_MIN_TTL = int( os.getenv("MCP_OAUTH2_TOKEN_CACHE_MIN_TTL", "10") ) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 1d7c0eaa116..68b20c5c3f7 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -15,7 +15,6 @@ from typing import Any, Callable, Dict, List, Literal, Optional, Set, Tuple, Uni from urllib.parse import urlparse import anyio - from fastapi import HTTPException from httpx import HTTPStatusError from mcp import ReadResourceResult, Resource @@ -72,7 +71,9 @@ try: from mcp.shared.tool_name_validation import ( validate_tool_name, # pyright: ignore[reportAssignmentType] ) - from mcp.shared.tool_name_validation import SEP_986_URL + from mcp.shared.tool_name_validation import ( + SEP_986_URL, + ) except ImportError: from pydantic import BaseModel @@ -891,6 +892,15 @@ class MCPServerManager: if transport == MCPTransport.stdio: # For stdio, we need to get the stdio config from the server resolved_env = stdio_env if stdio_env is not None else server.env or {} + + # Ensure npm-based STDIO MCP servers have a writable cache dir. + # In containers the default (~/.npm or /app/.npm) may not exist + # or be read-only, causing npx to fail with ENOENT. + if "NPM_CONFIG_CACHE" not in resolved_env: + from litellm.constants import MCP_NPM_CACHE_DIR + + resolved_env["NPM_CONFIG_CACHE"] = MCP_NPM_CACHE_DIR + stdio_config: Optional[MCPStdioConfig] = None if server.command and server.args is not None: stdio_config = MCPStdioConfig(