Skip to content

Commit 7ebdcda

Browse files
committed
fix(arcade-mcp-server): drop old key on prompt rename in update_prompt
PromptManager.update_prompt verified a prompt existed under the input name, then upserted under prompt.name. On a rename (prompt.name != name) the old entry was never removed, leaving a stale orphaned prompt in the registry. Mirror ResourceManager.update_resource: remove by the old key first (which also serves as the existence check), then upsert the new one. Adds regression tests for the rename and not-found cases.
1 parent f537771 commit 7ebdcda

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

libs/arcade-mcp-server/arcade_mcp_server/managers/prompt.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,13 @@ async def update_prompt(
111111
prompt: Prompt,
112112
handler: Callable[[dict[str, str]], list[PromptMessage]] | None = None,
113113
) -> Prompt:
114-
# Ensure exists
114+
# Remove the existing entry (this also verifies it exists). Removing by the
115+
# original ``name`` rather than just upserting by ``prompt.name`` ensures a
116+
# rename (prompt.name != name) does not leave the old entry orphaned in the
117+
# registry. Mirrors ResourceManager.update_resource, which handles URI
118+
# changes the same way.
115119
try:
116-
_ = await self.registry.get(name)
120+
await self.registry.remove(name)
117121
except KeyError:
118122
raise NotFoundError(f"Prompt '{name}' not found")
119123

libs/tests/arcade_mcp_server/test_prompt.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,30 @@ async def error_prompt(args: dict[str, str]):
239239

240240
with pytest.raises(PromptError):
241241
await manager.get_prompt("error_prompt", {})
242+
243+
@pytest.mark.asyncio
244+
async def test_update_prompt_rename_does_not_orphan_old_entry(
245+
self, prompt_manager, sample_prompt, prompt_function
246+
):
247+
"""A rename via update_prompt must not leave the old entry orphaned."""
248+
await prompt_manager.add_prompt(sample_prompt, prompt_function)
249+
250+
renamed = sample_prompt.model_copy(update={"name": "greeting_v2"})
251+
await prompt_manager.update_prompt(sample_prompt.name, renamed, prompt_function)
252+
253+
prompts = await prompt_manager.list_prompts()
254+
assert len(prompts) == 1
255+
assert prompts[0].name == "greeting_v2"
256+
with pytest.raises(NotFoundError):
257+
await prompt_manager.get_prompt("greeting", {"name": "Bob"})
258+
result = await prompt_manager.get_prompt("greeting_v2", {"name": "Bob"})
259+
assert isinstance(result, GetPromptResult)
260+
261+
@pytest.mark.asyncio
262+
async def test_update_prompt_missing_raises_not_found(
263+
self, prompt_manager, sample_prompt, prompt_function
264+
):
265+
"""Updating a prompt that does not exist raises NotFoundError."""
266+
missing = sample_prompt.model_copy(update={"name": "nope"})
267+
with pytest.raises(NotFoundError):
268+
await prompt_manager.update_prompt("nope", missing, prompt_function)

0 commit comments

Comments
 (0)