From faa6d6baea1092dccb9b98db9b1236fb1a73937b Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 6 Jul 2026 03:40:36 +0200 Subject: [PATCH] refactor(mcp): rename SURFSENSE_PAT to SURFSENSE_API_KEY --- surfsense_mcp/.env.example | 4 ++-- surfsense_mcp/README.md | 10 +++++----- surfsense_mcp/src/surfsense_mcp/__init__.py | 2 +- surfsense_mcp/src/surfsense_mcp/config.py | 12 ++++++------ surfsense_mcp/src/surfsense_mcp/core/client.py | 6 +++--- surfsense_mcp/src/surfsense_mcp/selfcheck.py | 2 +- surfsense_mcp/src/surfsense_mcp/server.py | 2 +- surfsense_mcp/tests/test_client_errors.py | 2 +- surfsense_mcp/tests/test_client_params.py | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/surfsense_mcp/.env.example b/surfsense_mcp/.env.example index e85ec65c7..3267324f4 100644 --- a/surfsense_mcp/.env.example +++ b/surfsense_mcp/.env.example @@ -1,7 +1,7 @@ # Copy to .env (or set these in your MCP client config) and fill in your token. -# Personal Access Token from SurfSense (Settings -> API). Required. -SURFSENSE_PAT=ss_pat_your_token_here +# API key from SurfSense (Settings -> API). Required. +SURFSENSE_API_KEY=ss_pat_your_token_here # Base URL of the SurfSense backend. Defaults to http://localhost:8000 SURFSENSE_BASE_URL=http://localhost:8000 diff --git a/surfsense_mcp/README.md b/surfsense_mcp/README.md index a15bdfeac..47dabf55c 100644 --- a/surfsense_mcp/README.md +++ b/surfsense_mcp/README.md @@ -2,8 +2,8 @@ A [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes SurfSense to MCP clients like **Claude Code**, **Cursor**, and **Claude Desktop**. -It talks to a running SurfSense backend purely over its REST API using a Personal -Access Token — it imports no backend code and can point at any instance (local or +It talks to a running SurfSense backend purely over its REST API using a SurfSense +API key — it imports no backend code and can point at any instance (local or hosted) by changing two environment variables. ## Tools @@ -32,7 +32,7 @@ model carries them between calls. ## Prerequisites 1. A running SurfSense backend (default `http://localhost:8000`). -2. A **Personal Access Token**: SurfSense → Settings → API → create token (`ss_pat_…`). +2. A **SurfSense API key**: SurfSense → Settings → API → create key (`ss_pat_…`). 3. **API access enabled** on the workspace(s) you want to use (workspace settings). ## Setup @@ -59,7 +59,7 @@ Add to `~/.cursor/mcp.json` (or a project `.cursor/mcp.json`): "args": ["run", "--directory", "/absolute/path/to/SurfSense/surfsense_mcp", "python", "-m", "surfsense_mcp"], "env": { "SURFSENSE_BASE_URL": "http://localhost:8000", - "SURFSENSE_PAT": "ss_pat_your_token_here" + "SURFSENSE_API_KEY": "ss_pat_your_token_here" } } } @@ -71,7 +71,7 @@ Add to `~/.cursor/mcp.json` (or a project `.cursor/mcp.json`): ```bash claude mcp add surfsense \ -e SURFSENSE_BASE_URL=http://localhost:8000 \ - -e SURFSENSE_PAT=ss_pat_your_token_here \ + -e SURFSENSE_API_KEY=ss_pat_your_token_here \ -- uv run --directory /absolute/path/to/SurfSense/surfsense_mcp python -m surfsense_mcp ``` diff --git a/surfsense_mcp/src/surfsense_mcp/__init__.py b/surfsense_mcp/src/surfsense_mcp/__init__.py index 57b21443b..24f6ebae4 100644 --- a/surfsense_mcp/src/surfsense_mcp/__init__.py +++ b/surfsense_mcp/src/surfsense_mcp/__init__.py @@ -2,7 +2,7 @@ Exposes SurfSense's scrapers, knowledge base, and workspaces to MCP clients (Claude Code, Cursor, Claude Desktop). Connects to a SurfSense backend over its -REST API, authenticating with a Personal Access Token. +REST API, authenticating with a SurfSense API key. """ __version__ = "0.1.0" diff --git a/surfsense_mcp/src/surfsense_mcp/config.py b/surfsense_mcp/src/surfsense_mcp/config.py index d67e635ab..5d3646545 100644 --- a/surfsense_mcp/src/surfsense_mcp/config.py +++ b/surfsense_mcp/src/surfsense_mcp/config.py @@ -19,7 +19,7 @@ class Settings: """Resolved configuration for a server process.""" base_url: str - pat: str + api_key: str api_prefix: str timeout: float default_workspace: str | None @@ -30,11 +30,11 @@ class Settings: @classmethod def from_env(cls) -> Settings: - pat = os.environ.get("SURFSENSE_PAT", "").strip() - if not pat: + api_key = os.environ.get("SURFSENSE_API_KEY", "").strip() + if not api_key: raise SystemExit( - "SURFSENSE_PAT is required. Create a Personal Access Token in " - "SurfSense (Settings -> API) and pass it via the SURFSENSE_PAT " + "SURFSENSE_API_KEY is required. Create an API key in SurfSense " + "(Settings -> API) and pass it via the SURFSENSE_API_KEY " "environment variable." ) @@ -55,7 +55,7 @@ class Settings: return cls( base_url=base_url, - pat=pat, + api_key=api_key, api_prefix=api_prefix, timeout=timeout, default_workspace=default_workspace, diff --git a/surfsense_mcp/src/surfsense_mcp/core/client.py b/surfsense_mcp/src/surfsense_mcp/core/client.py index 6635d011b..55f9d6048 100644 --- a/surfsense_mcp/src/surfsense_mcp/core/client.py +++ b/surfsense_mcp/src/surfsense_mcp/core/client.py @@ -13,7 +13,7 @@ import httpx from .errors import ToolError _FAILURE_HINTS: dict[int, str] = { - 401: "Authentication failed — check that SURFSENSE_PAT is a valid, unexpired token.", + 401: "Authentication failed — check that SURFSENSE_API_KEY is a valid, unexpired key.", 402: "The workspace is out of credits for this operation.", 403: ( "Access denied — the token lacks permission, or API access is disabled " @@ -27,12 +27,12 @@ _FAILURE_HINTS: dict[int, str] = { class SurfSenseClient: """Issues authenticated requests against ``{base_url}{api_prefix}``.""" - def __init__(self, *, api_base: str, pat: str, timeout: float) -> None: + def __init__(self, *, api_base: str, api_key: str, timeout: float) -> None: self._api_base = api_base self._http = httpx.AsyncClient( base_url=api_base, headers={ - "Authorization": f"Bearer {pat}", + "Authorization": f"Bearer {api_key}", "Accept": "application/json", }, timeout=timeout, diff --git a/surfsense_mcp/src/surfsense_mcp/selfcheck.py b/surfsense_mcp/src/surfsense_mcp/selfcheck.py index c4cba397e..c1813938c 100644 --- a/surfsense_mcp/src/surfsense_mcp/selfcheck.py +++ b/surfsense_mcp/src/surfsense_mcp/selfcheck.py @@ -43,7 +43,7 @@ _MIN_DESCRIPTION_CHARS = 40 async def _collect_tools() -> dict[str, object]: settings = Settings( base_url="http://localhost:8000", - pat="ss_pat_selfcheck", + api_key="ss_pat_selfcheck", api_prefix="/api/v1", timeout=5.0, default_workspace=None, diff --git a/surfsense_mcp/src/surfsense_mcp/server.py b/surfsense_mcp/src/surfsense_mcp/server.py index e1413dadf..1c47753ee 100644 --- a/surfsense_mcp/src/surfsense_mcp/server.py +++ b/surfsense_mcp/src/surfsense_mcp/server.py @@ -17,7 +17,7 @@ from .features import knowledge_base, scrapers, workspaces def build_server(settings: Settings) -> tuple[FastMCP, SurfSenseClient]: """Assemble a configured server and the client whose lifecycle it shares.""" client = SurfSenseClient( - api_base=settings.api_base, pat=settings.pat, timeout=settings.timeout + api_base=settings.api_base, api_key=settings.api_key, timeout=settings.timeout ) context = WorkspaceContext(client, preferred_reference=settings.default_workspace) diff --git a/surfsense_mcp/tests/test_client_errors.py b/surfsense_mcp/tests/test_client_errors.py index fc953410d..648bfde3e 100644 --- a/surfsense_mcp/tests/test_client_errors.py +++ b/surfsense_mcp/tests/test_client_errors.py @@ -15,7 +15,7 @@ def _response(status: int, **kwargs) -> httpx.Response: def test_explains_401_with_token_hint(): message = SurfSenseClient._explain_failure(_response(401, json={"detail": "bad"})) - assert "SURFSENSE_PAT" in message + assert "SURFSENSE_API_KEY" in message assert "bad" in message diff --git a/surfsense_mcp/tests/test_client_params.py b/surfsense_mcp/tests/test_client_params.py index 9812cdb2c..4840ad488 100644 --- a/surfsense_mcp/tests/test_client_params.py +++ b/surfsense_mcp/tests/test_client_params.py @@ -25,7 +25,7 @@ def _capture(client: SurfSenseClient) -> dict: def test_none_params_are_dropped(): - client = SurfSenseClient(api_base="http://test/api/v1", pat="ss_pat_x", timeout=5) + client = SurfSenseClient(api_base="http://test/api/v1", api_key="ss_pat_x", timeout=5) seen = _capture(client) asyncio.run( client.request(