refactor(mcp): rename SURFSENSE_PAT to SURFSENSE_API_KEY

This commit is contained in:
CREDO23 2026-07-06 03:40:36 +02:00
parent b2bf925f63
commit faa6d6baea
9 changed files with 21 additions and 21 deletions

View file

@ -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

View file

@ -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
```

View file

@ -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"

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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(