diff --git a/scripts/migrate_all.py b/scripts/migrate_all.py
index 1dca7acb..d77f0356 100644
--- a/scripts/migrate_all.py
+++ b/scripts/migrate_all.py
@@ -658,6 +658,7 @@ class MigrationRunner:
url=sqlite_server.url,
command=sqlite_server.command,
args=sqlite_server.args,
+ env=getattr(sqlite_server, "env", None),
headers=sqlite_server.headers,
timeout=sqlite_server.timeout,
sse_read_timeout=sqlite_server.sse_read_timeout,
diff --git a/scripts/migrate_business_from_sqlite.py b/scripts/migrate_business_from_sqlite.py
index 252ee2d0..3d8447b8 100644
--- a/scripts/migrate_business_from_sqlite.py
+++ b/scripts/migrate_business_from_sqlite.py
@@ -532,6 +532,7 @@ async def migrate_mcp_servers(sqlite_reader: SQLiteReader, dry_run: bool, execut
url=sqlite_server.url,
command=sqlite_server.command,
args=sqlite_server.args,
+ env=getattr(sqlite_server, "env", None),
headers=sqlite_server.headers,
timeout=sqlite_server.timeout,
sse_read_timeout=sqlite_server.sse_read_timeout,
diff --git a/server/routers/mcp_router.py b/server/routers/mcp_router.py
index 66b31f0d..f1c9828c 100644
--- a/server/routers/mcp_router.py
+++ b/server/routers/mcp_router.py
@@ -33,6 +33,7 @@ class CreateMcpServerRequest(BaseModel):
url: str | None = Field(None, description="服务器 URL(sse/streamable_http)")
command: str | None = Field(None, description="命令(stdio)")
args: list | None = Field(None, description="命令参数数组(stdio)")
+ env: dict | None = Field(None, description="环境变量(stdio)")
description: str | None = Field(None, description="描述")
headers: dict | None = Field(None, description="HTTP 请求头")
timeout: int | None = Field(None, description="HTTP 超时时间(秒)")
@@ -46,6 +47,7 @@ class UpdateMcpServerRequest(BaseModel):
url: str | None = Field(None, description="服务器 URL")
command: str | None = Field(None, description="命令(stdio)")
args: list | None = Field(None, description="命令参数数组(stdio)")
+ env: dict | None = Field(None, description="环境变量(stdio)")
description: str | None = Field(None, description="描述")
headers: dict | None = Field(None, description="HTTP 请求头")
timeout: int | None = Field(None, description="HTTP 超时时间(秒)")
@@ -112,6 +114,7 @@ async def create_mcp_server_route(
url=request.url,
command=request.command,
args=request.args,
+ env=request.env,
description=request.description,
headers=request.headers,
timeout=request.timeout,
@@ -159,6 +162,11 @@ async def update_mcp_server_route(
raise HTTPException(status_code=400, detail=f"传输类型必须是 {', '.join(valid_transports)} 之一")
try:
+ fields_set = getattr(request, "model_fields_set", getattr(request, "__fields_set__", set()))
+ update_kwargs = {}
+ if "env" in fields_set:
+ update_kwargs["env"] = request.env
+
server = await update_mcp_server(
db,
name=name,
@@ -173,6 +181,7 @@ async def update_mcp_server_route(
tags=request.tags,
icon=request.icon,
updated_by=current_user.username,
+ **update_kwargs,
)
return {"success": True, "data": server.to_dict()}
except ValueError as ve:
diff --git a/src/services/mcp_service.py b/src/services/mcp_service.py
index d3d9da0e..4385820b 100644
--- a/src/services/mcp_service.py
+++ b/src/services/mcp_service.py
@@ -35,6 +35,7 @@ _mcp_tools_stats: dict[str, dict[str, int]] = {}
# MCP Server configurations (Runtime cache, loaded from DB)
MCP_SERVERS: dict[str, dict[str, Any]] = {}
+_UNSET = object()
# Default MCP Server configurations (Imported to DB on first run)
_DEFAULT_MCP_SERVERS = {
@@ -130,6 +131,7 @@ async def init_mcp_servers() -> None:
url=config.get("url"),
command=config.get("command"),
args=config.get("args"),
+ env=config.get("env"),
headers=config.get("headers"),
timeout=config.get("timeout"),
sse_read_timeout=config.get("sse_read_timeout"),
@@ -155,6 +157,7 @@ async def init_mcp_servers() -> None:
url=config.get("url"),
command=config.get("command"),
args=config.get("args"),
+ env=config.get("env"),
headers=config.get("headers"),
timeout=config.get("timeout"),
sse_read_timeout=config.get("sse_read_timeout"),
@@ -368,6 +371,7 @@ async def create_mcp_server(
url: str = None,
command: str = None,
args: list = None,
+ env: dict = None,
description: str = None,
headers: dict = None,
timeout: int = None,
@@ -389,6 +393,7 @@ async def create_mcp_server(
url=url,
command=command,
args=args,
+ env=env,
headers=headers,
timeout=timeout,
sse_read_timeout=sse_read_timeout,
@@ -417,6 +422,7 @@ async def update_mcp_server(
url: str = None,
command: str = None,
args: list = None,
+ env: Any = _UNSET,
headers: dict = None,
timeout: int = None,
sse_read_timeout: int = None,
@@ -439,6 +445,8 @@ async def update_mcp_server(
server.command = command
if args is not None:
server.args = args
+ if env is not _UNSET:
+ server.env = env
if headers is not None:
server.headers = headers
if timeout is not None:
diff --git a/src/storage/db/models.py b/src/storage/db/models.py
index c1edfc3e..7c464c05 100644
--- a/src/storage/db/models.py
+++ b/src/storage/db/models.py
@@ -341,6 +341,7 @@ class MCPServer(Base):
url = Column(String(500), nullable=True, comment="服务器 URL(sse/streamable_http)")
command = Column(String(500), nullable=True, comment="命令(stdio)")
args = Column(JSON, nullable=True, comment="命令参数数组(stdio)")
+ env = Column(JSON, nullable=True, comment="环境变量(stdio)")
headers = Column(JSON, nullable=True, comment="HTTP 请求头")
timeout = Column(Integer, nullable=True, comment="HTTP 超时时间(秒)")
sse_read_timeout = Column(Integer, nullable=True, comment="SSE 读取超时(秒)")
@@ -369,6 +370,7 @@ class MCPServer(Base):
"url": self.url,
"command": self.command,
"args": self.args or [],
+ "env": self.env or {},
"headers": self.headers or {},
"timeout": self.timeout,
"sse_read_timeout": self.sse_read_timeout,
@@ -402,6 +404,14 @@ class MCPServer(Base):
config["args"] = json.loads(self.args)
except json.JSONDecodeError:
pass
+ if self.transport == "stdio" and self.env:
+ if isinstance(self.env, dict):
+ config["env"] = self.env
+ elif isinstance(self.env, str):
+ try:
+ config["env"] = json.loads(self.env)
+ except json.JSONDecodeError:
+ pass
# headers 只用于 sse/streamable_http 传输类型
if self.transport in ("sse", "streamable_http") and self.headers:
if isinstance(self.headers, dict):
diff --git a/src/storage/postgres/manager.py b/src/storage/postgres/manager.py
index 390f00f4..b499a568 100644
--- a/src/storage/postgres/manager.py
+++ b/src/storage/postgres/manager.py
@@ -171,6 +171,7 @@ class PostgresManager(metaclass=SingletonMeta):
"ALTER TABLE IF EXISTS skills ADD COLUMN IF NOT EXISTS tool_dependencies JSONB DEFAULT '[]'::jsonb",
"ALTER TABLE IF EXISTS skills ADD COLUMN IF NOT EXISTS mcp_dependencies JSONB DEFAULT '[]'::jsonb",
"ALTER TABLE IF EXISTS skills ADD COLUMN IF NOT EXISTS skill_dependencies JSONB DEFAULT '[]'::jsonb",
+ "ALTER TABLE IF EXISTS mcp_servers ADD COLUMN IF NOT EXISTS env JSONB",
]
async with self.async_engine.begin() as conn:
for stmt in stmts:
diff --git a/src/storage/postgres/models_business.py b/src/storage/postgres/models_business.py
index a5e7cabd..b7e57478 100644
--- a/src/storage/postgres/models_business.py
+++ b/src/storage/postgres/models_business.py
@@ -416,6 +416,7 @@ class MCPServer(Base):
url = Column(String(500), nullable=True, comment="服务器 URL(sse/streamable_http)")
command = Column(String(500), nullable=True, comment="命令(stdio)")
args = Column(JSON, nullable=True, comment="命令参数数组(stdio)")
+ env = Column(JSON, nullable=True, comment="环境变量(stdio)")
headers = Column(JSON, nullable=True, comment="HTTP 请求头")
timeout = Column(Integer, nullable=True, comment="HTTP 超时时间(秒)")
sse_read_timeout = Column(Integer, nullable=True, comment="SSE 读取超时(秒)")
@@ -444,6 +445,7 @@ class MCPServer(Base):
"url": self.url,
"command": self.command,
"args": self.args or [],
+ "env": self.env or {},
"headers": self.headers or {},
"timeout": self.timeout,
"sse_read_timeout": self.sse_read_timeout,
@@ -475,6 +477,14 @@ class MCPServer(Base):
config["args"] = json.loads(self.args)
except json.JSONDecodeError:
pass
+ if self.transport == "stdio" and self.env:
+ if isinstance(self.env, dict):
+ config["env"] = self.env
+ elif isinstance(self.env, str):
+ try:
+ config["env"] = json.loads(self.env)
+ except json.JSONDecodeError:
+ pass
# headers 只用于 sse/streamable_http 传输类型
if self.transport in ("sse", "streamable_http") and self.headers:
if isinstance(self.headers, dict):
diff --git a/web/src/components/McpEnvEditor.vue b/web/src/components/McpEnvEditor.vue
new file mode 100644
index 00000000..77600c49
--- /dev/null
+++ b/web/src/components/McpEnvEditor.vue
@@ -0,0 +1,145 @@
+
+
{{ JSON.stringify(server.env, null, 2) }}
+