Skip to content

Commit a6fa5c0

Browse files
committed
fix(managers): clean up empty tenant scopes after last entry removal
Remove the inner dict from the outer storage when the last entry for a tenant is deleted, preventing unbounded accumulation of empty dicts in long-running servers with transient tenants. Github-Issue:#8
1 parent cd16fd0 commit a6fa5c0

4 files changed

Lines changed: 12 additions & 0 deletions

File tree

src/mcp/server/mcpserver/prompts/manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def remove_prompt(self, name: str, *, tenant_id: str | None = None) -> None:
6464
if name not in scope:
6565
raise ValueError(f"Unknown prompt: {name}")
6666
del scope[name]
67+
if not scope and tenant_id in self._prompts:
68+
del self._prompts[tenant_id]
6769

6870
async def render_prompt(
6971
self,

src/mcp/server/mcpserver/resources/resource_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ def remove_resource(self, uri: AnyUrl | str, *, tenant_id: str | None = None) ->
117117
if uri_str not in scope:
118118
raise ValueError(f"Unknown resource: {uri}")
119119
del scope[uri_str]
120+
if not scope and tenant_id in self._resources:
121+
del self._resources[tenant_id]
120122

121123
async def get_resource(
122124
self,

src/mcp/server/mcpserver/tools/tool_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ def remove_tool(self, name: str, *, tenant_id: str | None = None) -> None:
9393
if name not in scope:
9494
raise ToolError(f"Unknown tool: {name}")
9595
del scope[name]
96+
if not scope and tenant_id in self._tools:
97+
del self._tools[tenant_id]
9698

9799
async def call_tool(
98100
self,

tests/server/mcpserver/test_multi_tenancy_managers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def my_tool() -> str: # pragma: no cover
8484

8585
assert manager.get_tool("my_tool", tenant_id="tenant-a") is None
8686
assert manager.get_tool("my_tool", tenant_id="tenant-b") is not None
87+
# Empty tenant scope is cleaned up
88+
assert "tenant-a" not in manager._tools
8789

8890

8991
def test_remove_tool_wrong_tenant_raises():
@@ -240,6 +242,8 @@ def test_remove_resource_with_tenant():
240242

241243
assert len(manager.list_resources(tenant_id="tenant-a")) == 0
242244
assert len(manager.list_resources(tenant_id="tenant-b")) == 1
245+
# Empty tenant scope is cleaned up
246+
assert "tenant-a" not in manager._resources
243247

244248

245249
def test_remove_resource_wrong_tenant_raises():
@@ -330,6 +334,8 @@ def test_remove_prompt_with_tenant():
330334

331335
assert manager.get_prompt("greet", tenant_id="tenant-a") is None
332336
assert manager.get_prompt("greet", tenant_id="tenant-b") is not None
337+
# Empty tenant scope is cleaned up
338+
assert "tenant-a" not in manager._prompts
333339

334340

335341
def test_remove_prompt_wrong_tenant_raises():

0 commit comments

Comments
 (0)