diff --git a/libs/arcade-mcp-server/arcade_mcp_server/managers/prompt.py b/libs/arcade-mcp-server/arcade_mcp_server/managers/prompt.py index b0c6d0241..7dc6bb32c 100644 --- a/libs/arcade-mcp-server/arcade_mcp_server/managers/prompt.py +++ b/libs/arcade-mcp-server/arcade_mcp_server/managers/prompt.py @@ -111,9 +111,13 @@ async def update_prompt( prompt: Prompt, handler: Callable[[dict[str, str]], list[PromptMessage]] | None = None, ) -> Prompt: - # Ensure exists + # Remove the existing entry (this also verifies it exists). Removing by the + # original ``name`` rather than just upserting by ``prompt.name`` ensures a + # rename (prompt.name != name) does not leave the old entry orphaned in the + # registry. Mirrors ResourceManager.update_resource, which handles URI + # changes the same way. try: - _ = await self.registry.get(name) + await self.registry.remove(name) except KeyError: raise NotFoundError(f"Prompt '{name}' not found") diff --git a/libs/tests/arcade_mcp_server/test_prompt.py b/libs/tests/arcade_mcp_server/test_prompt.py index cf2f69814..0158cafa6 100644 --- a/libs/tests/arcade_mcp_server/test_prompt.py +++ b/libs/tests/arcade_mcp_server/test_prompt.py @@ -239,3 +239,30 @@ async def error_prompt(args: dict[str, str]): with pytest.raises(PromptError): await manager.get_prompt("error_prompt", {}) + + @pytest.mark.asyncio + async def test_update_prompt_rename_does_not_orphan_old_entry( + self, prompt_manager, sample_prompt, prompt_function + ): + """A rename via update_prompt must not leave the old entry orphaned.""" + await prompt_manager.add_prompt(sample_prompt, prompt_function) + + renamed = sample_prompt.model_copy(update={"name": "greeting_v2"}) + await prompt_manager.update_prompt(sample_prompt.name, renamed, prompt_function) + + prompts = await prompt_manager.list_prompts() + assert len(prompts) == 1 + assert prompts[0].name == "greeting_v2" + with pytest.raises(NotFoundError): + await prompt_manager.get_prompt("greeting", {"name": "Bob"}) + result = await prompt_manager.get_prompt("greeting_v2", {"name": "Bob"}) + assert isinstance(result, GetPromptResult) + + @pytest.mark.asyncio + async def test_update_prompt_missing_raises_not_found( + self, prompt_manager, sample_prompt, prompt_function + ): + """Updating a prompt that does not exist raises NotFoundError.""" + missing = sample_prompt.model_copy(update={"name": "nope"}) + with pytest.raises(NotFoundError): + await prompt_manager.update_prompt("nope", missing, prompt_function)