mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(mcp): delete per-user env vars with their server and document POST replace semantics
This commit is contained in:
parent
4b31a6d711
commit
ca1e837532
3 changed files with 47 additions and 1 deletions
|
|
@ -469,6 +469,9 @@ async def delete_mcp_server(
|
|||
"""
|
||||
Delete the mcp server from the db by server_id
|
||||
|
||||
Also removes any per-user env var rows for the server, which have no FK
|
||||
cascade, so deleting a server never leaves orphaned credential rows behind.
|
||||
|
||||
Returns the deleted mcp server record if it exists, otherwise None
|
||||
"""
|
||||
deleted_server = await prisma_client.db.litellm_mcpservertable.delete(
|
||||
|
|
@ -476,6 +479,10 @@ async def delete_mcp_server(
|
|||
"server_id": server_id,
|
||||
},
|
||||
)
|
||||
if deleted_server is not None:
|
||||
await prisma_client.db.litellm_mcpuserenvvars.delete_many(
|
||||
where={"server_id": server_id}
|
||||
)
|
||||
return deleted_server
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2273,7 +2273,12 @@ if MCP_AVAILABLE:
|
|||
|
||||
@router.post(
|
||||
"/server/{server_id}/user-env-vars",
|
||||
description="Store the calling user's per-user MCP env var values for this server.",
|
||||
description=(
|
||||
"Store the calling user's per-user MCP env var values for this "
|
||||
"server. This fully replaces any previously stored values: a "
|
||||
"variable omitted from the request (or sent empty) is cleared, "
|
||||
"not preserved. Send the complete set you want retained."
|
||||
),
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
response_model=MCPUserEnvVarsStatus,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -673,6 +673,40 @@ async def test_delete_user_env_vars_is_idempotent_delete_many():
|
|||
assert call.kwargs["where"] == {"user_id": "alice", "server_id": "srv-1"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_mcp_server_removes_orphaned_user_env_vars():
|
||||
"""Deleting a server must also drop every user's per-user env var rows for
|
||||
it; there is no FK cascade, so skipping this leaves orphaned credentials."""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.db import delete_mcp_server
|
||||
|
||||
prisma = _mock_env_vars_prisma()
|
||||
prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=object())
|
||||
|
||||
await delete_mcp_server(prisma, "srv-1")
|
||||
|
||||
prisma.db.litellm_mcpuserenvvars.delete_many.assert_awaited_once()
|
||||
call = prisma.db.litellm_mcpuserenvvars.delete_many.call_args
|
||||
assert call.kwargs["where"] == {"server_id": "srv-1"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_mcp_server_skips_env_var_cleanup_when_server_missing():
|
||||
"""A no-op delete (server not found) must not touch the env var table."""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.db import delete_mcp_server
|
||||
|
||||
prisma = _mock_env_vars_prisma()
|
||||
prisma.db.litellm_mcpservertable.delete = AsyncMock(return_value=None)
|
||||
|
||||
result = await delete_mcp_server(prisma, "srv-1")
|
||||
|
||||
assert result is None
|
||||
prisma.db.litellm_mcpuserenvvars.delete_many.assert_not_awaited()
|
||||
|
||||
|
||||
# ── DB helpers: global env vars encrypted at rest ─────────────────────────
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue