mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
refactor(mcp): rename SURFSENSE_PAT to SURFSENSE_API_KEY
This commit is contained in:
parent
b2bf925f63
commit
faa6d6baea
9 changed files with 21 additions and 21 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue