mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
|
|
"""Strict Notion MCP OAuth/tool fakes for Playwright E2E.
|
||
|
|
|
||
|
|
Notion migrated from indexed OAuth to the hosted Notion MCP server
|
||
|
|
(``https://mcp.notion.com/mcp``, DCR/RFC 7591). This fake mirrors
|
||
|
|
``jira_module`` for the generic MCP OAuth + streamable-HTTP tool boundaries;
|
||
|
|
the older ``notion_module`` (a ``notion_client`` SDK stand-in) stays only to
|
||
|
|
satisfy production's import-time ``import notion_client``.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from types import SimpleNamespace
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from tests.e2e.fakes import mcp_oauth_runtime, mcp_runtime
|
||
|
|
|
||
|
|
_AUTHORIZATION_URL = "https://mcp.notion.com/authorize"
|
||
|
|
_REGISTRATION_URL = "https://mcp.notion.com/register"
|
||
|
|
_TOKEN_URL = "https://mcp.notion.com/token"
|
||
|
|
_MCP_URL = "https://mcp.notion.com/mcp"
|
||
|
|
|
||
|
|
_CLIENT_ID = "fake-notion-mcp-client-id"
|
||
|
|
_CLIENT_SECRET = "fake-notion-mcp-client-secret"
|
||
|
|
_ACCESS_TOKEN = "fake-notion-mcp-access-token"
|
||
|
|
_REFRESH_TOKEN = "fake-notion-mcp-refresh-token"
|
||
|
|
_OAUTH_CODE = "fake-notion-oauth-code"
|
||
|
|
|
||
|
|
# Keep in sync with FAKE_NOTION_PAGES / CANARY_TOKENS.notionCanary in
|
||
|
|
# surfsense_web/tests/helpers/canary.ts.
|
||
|
|
_CANARY_TOKEN = "SURFSENSE_E2E_CANARY_TOKEN_NOTION_001"
|
||
|
|
_CANARY_PAGE_ID = "fake-notion-page-canary-001"
|
||
|
|
_CANARY_TITLE = "E2E Canary Notion Page"
|
||
|
|
_WORKSPACE_NAME = "SurfSense E2E Notion Workspace"
|
||
|
|
|
||
|
|
|
||
|
|
async def _list_tools() -> SimpleNamespace:
|
||
|
|
return SimpleNamespace(
|
||
|
|
tools=[
|
||
|
|
SimpleNamespace(
|
||
|
|
name="search",
|
||
|
|
description="Search the connected Notion workspace for pages and databases.",
|
||
|
|
inputSchema={
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"query": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Text to search Notion pages for.",
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"required": ["query"],
|
||
|
|
},
|
||
|
|
),
|
||
|
|
SimpleNamespace(
|
||
|
|
name="fetch",
|
||
|
|
description="Fetch the full contents of a Notion page by id.",
|
||
|
|
inputSchema={
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"id": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "The Notion page id to fetch.",
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"required": ["id"],
|
||
|
|
},
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
async def _call_tool(
|
||
|
|
tool_name: str, arguments: dict[str, Any] | None = None
|
||
|
|
) -> SimpleNamespace:
|
||
|
|
arguments = arguments or {}
|
||
|
|
|
||
|
|
if tool_name == "search":
|
||
|
|
query = str(arguments.get("query", ""))
|
||
|
|
if _CANARY_TITLE.lower() not in query.lower():
|
||
|
|
raise ValueError(f"Unexpected Notion search query: {query!r}")
|
||
|
|
text = (
|
||
|
|
f"{_CANARY_TITLE}\n"
|
||
|
|
f"id: {_CANARY_PAGE_ID}\n"
|
||
|
|
f"workspace: {_WORKSPACE_NAME}\n"
|
||
|
|
f"snippet: {_CANARY_TOKEN}"
|
||
|
|
)
|
||
|
|
return SimpleNamespace(content=[SimpleNamespace(text=text)])
|
||
|
|
|
||
|
|
if tool_name == "fetch":
|
||
|
|
page_id = str(arguments.get("id", ""))
|
||
|
|
if page_id != _CANARY_PAGE_ID:
|
||
|
|
raise ValueError(f"Unexpected Notion fetch id: {page_id!r}")
|
||
|
|
text = f"{_CANARY_TITLE}\n\n{_CANARY_TOKEN}"
|
||
|
|
return SimpleNamespace(content=[SimpleNamespace(text=text)])
|
||
|
|
|
||
|
|
raise NotImplementedError(f"Unexpected Notion MCP tool call: {tool_name!r}")
|
||
|
|
|
||
|
|
|
||
|
|
def install(active_patches: list[Any]) -> None:
|
||
|
|
"""Register Notion MCP OAuth/tool handlers with the shared dispatchers."""
|
||
|
|
del active_patches
|
||
|
|
mcp_oauth_runtime.register_service(
|
||
|
|
mcp_url=_MCP_URL,
|
||
|
|
discovery_metadata={
|
||
|
|
"issuer": "https://mcp.notion.com",
|
||
|
|
"authorization_endpoint": _AUTHORIZATION_URL,
|
||
|
|
"token_endpoint": _TOKEN_URL,
|
||
|
|
"registration_endpoint": _REGISTRATION_URL,
|
||
|
|
"code_challenge_methods_supported": ["S256"],
|
||
|
|
"grant_types_supported": ["authorization_code", "refresh_token"],
|
||
|
|
"response_types_supported": ["code"],
|
||
|
|
},
|
||
|
|
client_id=_CLIENT_ID,
|
||
|
|
client_secret=_CLIENT_SECRET,
|
||
|
|
token_endpoint=_TOKEN_URL,
|
||
|
|
registration_endpoint=_REGISTRATION_URL,
|
||
|
|
oauth_code=_OAUTH_CODE,
|
||
|
|
access_token=_ACCESS_TOKEN,
|
||
|
|
refresh_token=_REFRESH_TOKEN,
|
||
|
|
scope="read write",
|
||
|
|
redirect_uri_substring="/api/v1/auth/mcp/notion/connector/callback",
|
||
|
|
)
|
||
|
|
mcp_runtime.register(
|
||
|
|
url=_MCP_URL,
|
||
|
|
expected_bearer=_ACCESS_TOKEN,
|
||
|
|
list_tools=_list_tools,
|
||
|
|
call_tool=_call_tool,
|
||
|
|
)
|