mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
refactor(mcp): resolve API key per request in client
This commit is contained in:
parent
80e032ec3a
commit
8a54e0293e
1 changed files with 29 additions and 7 deletions
|
|
@ -10,10 +10,11 @@ from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from .auth.identity import current_api_key
|
||||||
from .errors import ToolError
|
from .errors import ToolError
|
||||||
|
|
||||||
_FAILURE_HINTS: dict[int, str] = {
|
_FAILURE_HINTS: dict[int, str] = {
|
||||||
401: "Authentication failed — check that SURFSENSE_API_KEY is a valid, unexpired key.",
|
401: "Authentication failed — the SurfSense API key is invalid or expired.",
|
||||||
402: "The workspace is out of credits for this operation.",
|
402: "The workspace is out of credits for this operation.",
|
||||||
403: (
|
403: (
|
||||||
"Access denied — the token lacks permission, or API access is disabled "
|
"Access denied — the token lacks permission, or API access is disabled "
|
||||||
|
|
@ -27,17 +28,31 @@ _FAILURE_HINTS: dict[int, str] = {
|
||||||
class SurfSenseClient:
|
class SurfSenseClient:
|
||||||
"""Issues authenticated requests against ``{base_url}{api_prefix}``."""
|
"""Issues authenticated requests against ``{base_url}{api_prefix}``."""
|
||||||
|
|
||||||
def __init__(self, *, api_base: str, api_key: str, timeout: float) -> None:
|
def __init__(
|
||||||
|
self, *, api_base: str, timeout: float, fallback_api_key: str | None = None
|
||||||
|
) -> None:
|
||||||
self._api_base = api_base
|
self._api_base = api_base
|
||||||
|
# The key is resolved per request (one client serves many users over
|
||||||
|
# http), so none is baked into the shared client. ``fallback_api_key``
|
||||||
|
# is the env-supplied key used under stdio, where there is no header.
|
||||||
|
self._fallback_api_key = fallback_api_key
|
||||||
self._http = httpx.AsyncClient(
|
self._http = httpx.AsyncClient(
|
||||||
base_url=api_base,
|
base_url=api_base,
|
||||||
headers={
|
headers={"Accept": "application/json"},
|
||||||
"Authorization": f"Bearer {api_key}",
|
|
||||||
"Accept": "application/json",
|
|
||||||
},
|
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _auth_headers(self) -> dict[str, str]:
|
||||||
|
"""Resolve the caller's key: the per-request header, else the env key."""
|
||||||
|
api_key = current_api_key() or self._fallback_api_key
|
||||||
|
if not api_key:
|
||||||
|
raise ToolError(
|
||||||
|
"No SurfSense API key supplied. Send it as an 'Authorization: "
|
||||||
|
"Bearer ss_pat_...' header (remote server), or set the "
|
||||||
|
"SURFSENSE_API_KEY environment variable (stdio)."
|
||||||
|
)
|
||||||
|
return {"Authorization": f"Bearer {api_key}"}
|
||||||
|
|
||||||
async def request(
|
async def request(
|
||||||
self,
|
self,
|
||||||
method: str,
|
method: str,
|
||||||
|
|
@ -53,9 +68,16 @@ class SurfSenseClient:
|
||||||
# as a value (e.g. int("") on folder_id) and fail.
|
# as a value (e.g. int("") on folder_id) and fail.
|
||||||
if params is not None:
|
if params is not None:
|
||||||
params = {key: value for key, value in params.items() if value is not None}
|
params = {key: value for key, value in params.items() if value is not None}
|
||||||
|
headers = self._auth_headers()
|
||||||
try:
|
try:
|
||||||
response = await self._http.request(
|
response = await self._http.request(
|
||||||
method, path, params=params, json=json, data=data, files=files
|
method,
|
||||||
|
path,
|
||||||
|
params=params,
|
||||||
|
json=json,
|
||||||
|
data=data,
|
||||||
|
files=files,
|
||||||
|
headers=headers,
|
||||||
)
|
)
|
||||||
except httpx.RequestError as exc:
|
except httpx.RequestError as exc:
|
||||||
raise ToolError(
|
raise ToolError(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue