diff --git a/mealie_mcp/server.py b/mealie_mcp/server.py index 6ef44ce..6fbbfa5 100644 --- a/mealie_mcp/server.py +++ b/mealie_mcp/server.py @@ -53,6 +53,17 @@ READ_ONLY_TOOL_NAMES = frozenset( } ) +IMPORT_TOOL_NAMES = READ_ONLY_TOOL_NAMES | frozenset( + { + "import_recipe_url", + "import_recipe_text", + "import_recipe_image", + "patch_recipe", + "parse_ingredients", + "set_cover_image", + } +) + class MealieAuthError(RuntimeError): """The Mealie token is missing, expired, or wrong (HTTP 401).""" @@ -776,3 +787,13 @@ def main_read_only() -> None: """Run the same server without import, patch, delete, parse, image, or HA writes.""" _configure_read_only(mcp) mcp.run() + + +def main_import() -> None: + """Run the server with the import pipeline but without delete or HA writes. + + Adding a recipe is recoverable through the Mealie UI; removing one is not, + so `delete_recipe` stays out even though this profile writes. + """ + _configure_read_only(mcp, IMPORT_TOOL_NAMES) + mcp.run() diff --git a/pyproject.toml b/pyproject.toml index 3d26762..934b039 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dev = ["pytest>=8.0"] [project.scripts] mealie-mcp = "mealie_mcp.server:main" mealie-mcp-read-only = "mealie_mcp.server:main_read_only" +mealie-mcp-import = "mealie_mcp.server:main_import" [build-system] requires = ["hatchling"] diff --git a/tests/test_server_contracts.py b/tests/test_server_contracts.py index 7ab5a80..b8fdec2 100644 --- a/tests/test_server_contracts.py +++ b/tests/test_server_contracts.py @@ -60,6 +60,19 @@ def test_read_only_tool_allowlist_is_explicit(): } +def test_import_profile_adds_the_pipeline_but_never_delete(): + assert server.IMPORT_TOOL_NAMES == server.READ_ONLY_TOOL_NAMES | { + "import_recipe_url", + "import_recipe_text", + "import_recipe_image", + "patch_recipe", + "parse_ingredients", + "set_cover_image", + } + assert "delete_recipe" not in server.IMPORT_TOOL_NAMES + assert "shopping_list_add" not in server.IMPORT_TOOL_NAMES + + class TestUrlImportResponse: def test_slug_only_import_response_is_resolved_before_reporting(self, monkeypatch): recipe = {"slug": "lax-med-citron", "name": "Lax med citron", "recipeIngredient": []}