diff --git a/app-store/arxiv/arxiv_bg_worker.py b/app-store/arxiv/arxiv_bg_worker.py index 3a2c0da..385f917 100644 --- a/app-store/arxiv/arxiv_bg_worker.py +++ b/app-store/arxiv/arxiv_bg_worker.py @@ -8,7 +8,7 @@ import arxiv -from arxiv_common import get_bg_state_path, parse_research_interests +from arxiv_common import get_bg_state_path, is_background_enabled, parse_research_interests logger = logging.getLogger("arxiv.bg_worker") logger.setLevel(logging.INFO) @@ -47,6 +47,10 @@ def verify(self) -> tuple[bool, str]: return True, f"ArXiv background configured with {len(interests)} interest(s)." def run_cycle(self) -> BgRunResult: + if not is_background_enabled(): + logger.info("Background disabled. Skipping cycle.") + return BgRunResult(content=None) + interests = self.interests if not interests: return BgRunResult(error="no_interests") diff --git a/app-store/arxiv/arxiv_common.py b/app-store/arxiv/arxiv_common.py index 7ef1bd8..8795d58 100644 --- a/app-store/arxiv/arxiv_common.py +++ b/app-store/arxiv/arxiv_common.py @@ -1,11 +1,13 @@ from __future__ import annotations +import json import os from pathlib import Path MAX_RESULTS = 50 DEFAULT_STORAGE_PATH = Path.home() / ".arxiv-mcp-server" / "papers" DEFAULT_BG_STATE_PATH = Path("/root/.arxiv-truffle/arxiv_bg_state.json") +DEFAULT_SETTINGS_PATH = Path("/root/.arxiv-truffle/arxiv_settings.json") def get_storage_path() -> Path: @@ -15,6 +17,35 @@ def get_storage_path() -> Path: path.mkdir(parents=True, exist_ok=True) return path +def get_settings_path() -> Path: + raw = str(os.getenv("ARXIV_SETTINGS_PATH", "")).strip() + path = Path(raw) if raw else DEFAULT_SETTINGS_PATH + path.parent.mkdir(parents=True, exist_ok=True) + return path + +def load_settings() -> dict: + path = get_settings_path() + try: + if path.exists(): + data = json.loads(path.read_text(encoding="utf-8")) + return data + except Exception: + pass + return {} + +def save_settings(data: dict) -> None: + path = get_settings_path() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(data, indent=2, sort_keys=True), encoding="utf-8") + +def is_background_enabled() -> bool: + settings = load_settings() + return settings.get("background_enabled", True) + +def set_background_enabled(enabled: bool) -> None: + settings = load_settings() + settings["background_enabled"] = enabled + save_settings(settings) def get_bg_state_path() -> Path: raw = str(os.getenv("ARXIV_BG_STATE_PATH", "")).strip() @@ -39,4 +70,3 @@ def parse_research_interests(raw: str | None) -> list[str]: seen.add(key) out.append(interest) return out - diff --git a/app-store/arxiv/arxiv_foreground.py b/app-store/arxiv/arxiv_foreground.py index ac07513..b0e3c13 100644 --- a/app-store/arxiv/arxiv_foreground.py +++ b/app-store/arxiv/arxiv_foreground.py @@ -5,12 +5,14 @@ from truffile.app_runtime import ForegroundApp, ToolSpec, phosphor_icon_url import arxiv_tools +from arxiv_common import is_background_enabled, set_background_enabled class ArxivForegroundApp(ForegroundApp): def __init__(self) -> None: super().__init__("arxiv", logger_name="arxiv.foreground") self._register_tools() + self._register_settings_tools() self._register_prompts() def _register_tools(self) -> None: @@ -81,6 +83,37 @@ async def list_papers_tool() -> dict[str, Any]: async def read_paper_tool(paper_id: str) -> dict[str, Any]: return await arxiv_tools.read_paper(paper_id=paper_id) + def _register_settings_tools(self) -> None: + @self.tool( + ToolSpec( + name="toggle_background", + description=( + "Enable or disable background paper recommendations. " + "When disabled, the background worker skips its search cycle." + ), + icon=phosphor_icon_url("toggle-right"), + ) + ) + async def toggle_background_tool( + enabled: bool, + ) -> dict[str, Any]: + set_background_enabled(enabled) + state = "enabled" if enabled else "disabled" + return {"status": "success", "background_enabled": enabled, "message": f"Background recommendations {state}."} + + @self.tool( + ToolSpec( + name="background_status", + description="Check whether background paper recommendations are currently enabled or disabled.", + icon=phosphor_icon_url("info"), + readonly=True, + ) + ) + async def background_status_tool() -> dict[str, Any]: + enabled = is_background_enabled() + state = "enabled" if enabled else "disabled" + return {"status": "success", "background_enabled": enabled, "message": f"Background recommendations are {state}."} + def _register_prompts(self) -> None: @self.prompt( "deep-paper-analysis", diff --git a/app-store/arxiv/truffile.yaml b/app-store/arxiv/truffile.yaml index dfdb9bb..4a4c0f7 100644 --- a/app-store/arxiv/truffile.yaml +++ b/app-store/arxiv/truffile.yaml @@ -83,4 +83,5 @@ steps: run: | mkdir -p /root/.arxiv-mcp-server/papers mkdir -p /root/.arxiv-truffle + python -c 'from arxiv_common import set_background_enabled; import os; enabled = os.getenv("ARXIV_BACKGROUND_ENABLED", "true").lower() in ("true", "1", "yes"); set_background_enabled(enabled)' echo "ArXiv MCP installation complete!"