From 52a3e2564f49b466170d57361450d534174d0cf2 Mon Sep 17 00:00:00 2001 From: Wenchao An Date: Mon, 28 Sep 2026 16:02:34 +0800 Subject: [PATCH] fix(mcp): isolate personal connections from deployment configuration (#5966) * fix(mcp): isolate personal connections from deployment configuration * fix(mcp): preserve personal task ownership and repair plugin E2E fixtures * fix(mcp): handle corrupt personal config and cache owned calls * fix(mcp): pin personal endpoint addresses and handle invalid listings * fix(mcp): recheck current authority for privileged personal connections --- README.md | 5 +- backend/app/gateway/AGENTS.md | 9 +- backend/app/gateway/app.py | 16 +- backend/app/gateway/capabilities.py | 35 +- backend/app/gateway/personal_mcp_access.py | 33 ++ backend/app/gateway/routers/capabilities.py | 21 +- backend/app/gateway/routers/personal_mcp.py | 126 ++++ .../packages/harness/deerflow/mcp/AGENTS.md | 3 + .../packages/harness/deerflow/mcp/client.py | 4 + .../harness/deerflow/mcp/personal_access.py | 46 ++ .../harness/deerflow/mcp/personal_network.py | 62 ++ .../harness/deerflow/mcp/task_tool_caller.py | 66 ++- .../harness/deerflow/mcp/tasks/ordinary.py | 12 + .../packages/harness/deerflow/mcp/tools.py | 21 +- .../harness/deerflow/mcp/user_config.py | 77 +++ .../harness/deerflow/mcp/user_tools.py | 61 ++ .../packages/harness/deerflow/tools/tools.py | 21 +- .../tests/test_gateway_lifespan_shutdown.py | 24 + .../tests/test_mcp_task_ordinary_driver.py | 32 + backend/tests/test_mcp_task_tool_wrapping.py | 5 +- backend/tests/test_personal_mcp.py | 545 ++++++++++++++++++ backend/tests/test_personal_mcp_access.py | 172 ++++++ backend/tests/test_personal_mcp_network.py | 154 +++++ docs/capability-center.md | 66 ++- ...connection-ownership-capability-center.png | Bin 0 -> 262503 bytes frontend/src/AGENTS.md | 11 +- .../capabilities/plugin-directory.tsx | 10 +- .../workspace/capabilities/plugin-gallery.tsx | 182 ++++-- frontend/src/core/capabilities/copy.ts | 13 +- frontend/src/core/capabilities/hooks.ts | 14 +- frontend/src/core/mcp/api.ts | 33 +- frontend/src/core/mcp/hooks.ts | 31 +- frontend/tests/e2e/business-plugins.spec.ts | 46 +- frontend/tests/e2e/capability-center.spec.ts | 14 +- frontend/tests/e2e/mcp-settings.spec.ts | 8 +- frontend/tests/e2e/plugin-icons.spec.ts | 12 +- frontend/tests/e2e/utils/mock-api.ts | 4 +- .../unit/core/mcp/ownership.dom.test.tsx | 66 +++ 38 files changed, 1893 insertions(+), 167 deletions(-) create mode 100644 backend/app/gateway/personal_mcp_access.py create mode 100644 backend/app/gateway/routers/personal_mcp.py create mode 100644 backend/packages/harness/deerflow/mcp/personal_access.py create mode 100644 backend/packages/harness/deerflow/mcp/personal_network.py create mode 100644 backend/packages/harness/deerflow/mcp/user_config.py create mode 100644 backend/packages/harness/deerflow/mcp/user_tools.py create mode 100644 backend/tests/test_personal_mcp.py create mode 100644 backend/tests/test_personal_mcp_access.py create mode 100644 backend/tests/test_personal_mcp_network.py create mode 100644 docs/pr-evidence/connection-ownership-capability-center.png create mode 100644 frontend/tests/unit/core/mcp/ownership.dom.test.tsx diff --git a/README.md b/README.md index 121628c7ce..ac4a4f33b0 100644 --- a/README.md +++ b/README.md @@ -645,6 +645,9 @@ Signed-in users' notification toggle, default model, conversation mode, and reas Capability Center groups plugins by office collaboration, documents and knowledge, search and research, business and data, and development and operations. The directory includes setup references alongside existing MCP configurations and Lark. Recommended integrations and built-in support do not imply an installed or verified connection; the Installed filter shows configured MCP entries and installed Lark only. +Personal MCP connections configured in the web interface are persisted per user. +Deployment tools remain shared. See [connection ownership](docs/capability-center.md#personal-and-deployment-mcp-configuration). + For plugin manifests, adapter registration, and Agent capability selection, see [Capability Center integration contract](docs/capability-center.md). @@ -655,7 +658,7 @@ contacts. Saving configuration performs no external write. These plugins reuse the existing MCP lifecycle and require no separate plugin service. See the integration contract above for required fields, scopes, and feature boundaries. -Plugin brand icons are bundled locally. When adding or editing one MCP plugin, administrators can upload a PNG, JPG, or WebP image (up to 2 MB), preview it, or restore the default icon. Changes take effect only after Save; custom icons persist across browsers as a normalized 128px PNG in the server entry's display-only `presentation.icon` metadata. They are not sent to the MCP transport. +Plugin brand icons are bundled locally. When adding or editing one personal MCP plugin, users can upload a PNG, JPG, or WebP image (up to 2 MB), preview it, or restore the default icon. Changes take effect only after Save; custom icons persist across browsers as a normalized 128px PNG in the server entry's display-only `presentation.icon` metadata. They are not sent to the MCP transport. Capability Center > Plugins adds, replaces, and deletes one MCP server at a time through targeted mutations that preserve concurrent sibling changes; deletes use a bodyless URL-addressed request. An invalid stdio command on one server no longer blocks toggling another, while enabling that invalid server remains protected by the command allowlist and surfaces the backend validation message in the UI. Targeted updates accept both DeerFlow's `type` field and the MCP-spec `transport` field for SSE/HTTP servers. diff --git a/backend/app/gateway/AGENTS.md b/backend/app/gateway/AGENTS.md index 95df8dd1e4..2a574c5eae 100644 --- a/backend/app/gateway/AGENTS.md +++ b/backend/app/gateway/AGENTS.md @@ -211,10 +211,9 @@ archive/search behavior, read [Thread lifecycle invariants](../../docs/THREAD_LI It owns lineage and settled-checkpoint rules, legacy fallback boundaries, archive filtering before pagination, owner isolation, and activity-time preservation. -Capability installation IDs must be unique for MCP create/replace/state writes. -Single-server DELETE keeps schema validation but allows residual identity -collisions, so legacy duplicate groups can be repaired incrementally. Runtime -explicit selections still exclude ambiguous identities until repaired. +MCP create/replace/state writes require unique installation IDs. DELETE validates +schema but permits residual legacy ID collisions. Explicit selections exclude +ambiguous IDs until repaired. Capability skill discovery (`/api/capabilities/installations/skills`) reuses `routers/skills.py::_filter_visible_skills` after its off-thread storage read. @@ -234,3 +233,5 @@ model, while provider failures honor `authorization.fail_closed`. Skill listing authorization mirrors the models pattern: `routers/skills.py` routes resolve `(provider, principal)` through `authz.py::resolve_skill_authorization()` — a thin sibling of `resolve_model_authorization`, both delegating to the shared `_resolve_route_scoped_authorization` core — and the shared `_filter_visible_skills` helper filters the user-scoped catalog (public + caller's custom skills) through `filter_resources(principal, "skill", ...)` by name. Three user-facing surfaces apply it: `GET /api/skills` (the frontend skill list / slash-command autocomplete), `GET /api/skills/custom`, and `GET /api/skills/{name}` — the detail endpoint returns the standard 404 for an invisible skill so it cannot become an existence oracle the filtered list closed (`get_model`'s 403 is an execution decision via `authorize("model", "use")`, which has no skills equivalent in this layer). Anonymous callers are unfiltered (mirroring `list_models`), and provider resolution/decision errors follow `authorization.fail_closed` (fail-closed → empty listing / 404, fail-open → full listing). Skill management endpoints (`install`/`upload`/`reload`/custom-skill CRUD) stay `require_admin_user`-gated, and runtime activation authorization is a separate layer. The built-in RBAC provider maps this to the per-role `skills` policy key. Tests: `tests/test_skills_listing_authorization.py`. Batch workers pin `app.state.extensions`; never persist snapshots. + +Personal MCP uses owner-only files/calls; platform MCP stays shared. diff --git a/backend/app/gateway/app.py b/backend/app/gateway/app.py index f748c75bff..af5c27fb8f 100644 --- a/backend/app/gateway/app.py +++ b/backend/app/gateway/app.py @@ -40,6 +40,7 @@ from app.gateway.routers import ( mcp_tasks, memory, models, + personal_mcp, plugins, project_documents, project_thread_files, @@ -526,7 +527,9 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: logger.warning("Upload staging file cleanup skipped", exc_info=True) # Initialize LangGraph runtime components (StreamBridge, RunManager, checkpointer, store) - async with _runtime_with_mcp_pool_shutdown(app, startup_config): + from app.gateway.personal_mcp_access import personal_mcp_authority + + async with personal_mcp_authority(), _runtime_with_mcp_pool_shutdown(app, startup_config): logger.info("LangGraph runtime initialised") # Check admin bootstrap state and migrate orphan threads after admin exists. @@ -603,7 +606,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: OrdinaryMcpTaskDriver, ) from deerflow.mcp.tasks.runtime import ( - configured_task_toolset_count, set_mcp_task_config_snapshot, set_mcp_task_submitter, validate_mcp_task_runtime_configuration, @@ -622,11 +624,10 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: ) if mcp_task_repo is not None: mcp_task_drivers = McpTaskDriverRegistry() - if configured_task_toolset_count(task_extensions_config): - mcp_task_drivers.register( - ORDINARY_MCP_TASK_DRIVER, - OrdinaryMcpTaskDriver(McpTaskToolCaller(task_extensions_config)), - ) + mcp_task_drivers.register( + ORDINARY_MCP_TASK_DRIVER, + OrdinaryMcpTaskDriver(McpTaskToolCaller(task_extensions_config)), + ) mcp_task_service = McpTaskService( repository=mcp_task_repo, drivers=mcp_task_drivers, @@ -1053,6 +1054,7 @@ This gateway provides runtime endpoints for agent runs plus custom endpoints for # MCP API is mounted at /api/mcp app.include_router(capabilities.router) app.include_router(mcp.router) + app.include_router(personal_mcp.router) # Durable MCP tasks are scoped to their owning thread. app.include_router(mcp_tasks.router) diff --git a/backend/app/gateway/capabilities.py b/backend/app/gateway/capabilities.py index 61425e42c1..8dfc600657 100644 --- a/backend/app/gateway/capabilities.py +++ b/backend/app/gateway/capabilities.py @@ -2,13 +2,13 @@ import asyncio from dataclasses import dataclass -from typing import Any, Protocol +from typing import Any, Literal, Protocol from uuid import uuid4 from fastapi import HTTPException, Request from pydantic import BaseModel, Field, ValidationError -from app.gateway.deps import is_admin_user +from app.gateway.deps import get_current_user_from_request, is_admin_user from app.gateway.routers import integrations, mcp, skills from deerflow.capabilities.business import connection_config from deerflow.capabilities.catalog import PluginManifest @@ -46,6 +46,7 @@ class AdapterContext: request: Request config: AppConfig user_id: str + scope: Literal["deployment", "user"] = "deployment" class CapabilityAdapter(Protocol): @@ -87,7 +88,16 @@ def validate_mcp_connection(configuration: dict[str, Any]) -> None: class MCPAdapter: async def list_installations(self, context: AdapterContext) -> list[CapabilityInstallation]: - servers = await asyncio.to_thread(mcp._load_raw_mcp_server_responses) + if context.scope == "user": + from deerflow.mcp.user_config import read_user_mcp_config + + try: + raw_config = await asyncio.to_thread(read_user_mcp_config, context.user_id) + except ValueError as exc: + mcp._raise_invalid_mcp_configuration(str(exc), cause=exc) + servers = mcp._mcp_server_responses_from_raw(raw_config) + else: + servers = await asyncio.to_thread(mcp._load_raw_mcp_server_responses) result = [] ambiguous = ambiguous_installation_ids({name: server.model_dump() for name, server in servers.items()}) for name, server in servers.items(): @@ -113,6 +123,7 @@ class MCPAdapter: health="ambiguous" if identity in ambiguous else "unknown", plugin_id=metadata.get("plugin_id") if isinstance(metadata.get("plugin_id"), str) else None, adapter="mcp", + scope=context.scope, name=name, reference=name, description=server.description or "", @@ -134,7 +145,12 @@ class MCPAdapter: body = mcp.McpConfigUpdateRequest(mcp_servers={name: mcp.McpServerConfigResponse.model_validate(definition)}) except ValidationError as error: raise HTTPException(422, "Invalid MCP configuration") from error - await mcp.create_mcp_servers(context.request, body) + if context.scope == "user": + from app.gateway.routers.personal_mcp import create_servers + + await create_servers(context.request, body) + else: + await mcp.create_mcp_servers(context.request, body) class BusinessAdapter(MCPAdapter): @@ -222,7 +238,12 @@ registry.register("lark", LarkAdapter()) registry.register("skills", SkillAdapter()) -async def list_installations(adapter: str, request: Request, config: AppConfig) -> InstallationList: - context = AdapterContext(request, config, get_effective_user_id()) +async def list_installations(adapter: str, request: Request, config: AppConfig, scope: Literal["deployment", "user", "all"] = "deployment") -> InstallationList: + user_id = get_effective_user_id() + if scope != "deployment": + user_id = str((await get_current_user_from_request(request)).id) + context = AdapterContext(request, config, user_id, "user" if scope == "user" else "deployment") items = await registry.get(adapter).list_installations(context) - return InstallationList(items=items, can_manage=await is_admin_user(request)) + if scope == "all" and adapter in {"mcp", "business"}: + items += await registry.get(adapter).list_installations(AdapterContext(request, config, user_id, "user")) + return InstallationList(items=items, can_manage=scope == "user" or await is_admin_user(request)) diff --git a/backend/app/gateway/personal_mcp_access.py b/backend/app/gateway/personal_mcp_access.py new file mode 100644 index 0000000000..d8399a7cd8 --- /dev/null +++ b/backend/app/gateway/personal_mcp_access.py @@ -0,0 +1,33 @@ +"""Bind personal MCP authority to fresh Gateway account records.""" + +import asyncio +from contextlib import asynccontextmanager + +from app.gateway.auth_disabled import AUTH_DISABLED_USER_ID, is_auth_disabled +from app.gateway.deps import get_local_provider +from deerflow.mcp.personal_access import set_personal_mcp_admin_checker + + +async def _is_current_admin(owner: str) -> bool: + if is_auth_disabled() and owner == AUTH_DISABLED_USER_ID: + return True + user = await get_local_provider().get_user(owner) + return user is not None and user.system_role == "admin" + + +@asynccontextmanager +async def personal_mcp_authority(): + loop = asyncio.get_running_loop() + + async def check(owner: str) -> bool: + if asyncio.get_running_loop() is loop: + return await _is_current_admin(owner) + # Synchronous tool discovery runs on assembly workers with their own + # loops. Keep the shared database pool on the Gateway's owning loop. + return await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_is_current_admin(owner), loop)) + + previous = set_personal_mcp_admin_checker(check) + try: + yield + finally: + set_personal_mcp_admin_checker(previous) diff --git a/backend/app/gateway/routers/capabilities.py b/backend/app/gateway/routers/capabilities.py index ea071ce47e..24cce2de67 100644 --- a/backend/app/gateway/routers/capabilities.py +++ b/backend/app/gateway/routers/capabilities.py @@ -1,13 +1,13 @@ """Capability discovery is public to authenticated users; mutations reuse existing policy.""" import asyncio -from typing import Any +from typing import Any, Literal from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel, ConfigDict, Field from app.gateway.capabilities import AdapterContext, InstallationList, list_installations, registry -from app.gateway.deps import get_config, require_admin_user +from app.gateway.deps import get_config, get_current_user_from_request, require_admin_user from deerflow.capabilities.catalog import PluginManifest, load_catalog from deerflow.config.app_config import AppConfig from deerflow.runtime.user_context import get_effective_user_id @@ -20,6 +20,7 @@ class InstallRequest(BaseModel): plugin_id: str name: str = Field(default="", max_length=128) configuration: dict[str, Any] = Field(default_factory=dict) + scope: Literal["deployment", "user"] = "deployment" @router.get("/catalog", response_model=list[PluginManifest]) @@ -28,17 +29,23 @@ async def catalog() -> list[PluginManifest]: @router.get("/installations/{adapter}", response_model=InstallationList) -async def installations(adapter: str, request: Request, config: AppConfig = Depends(get_config)) -> InstallationList: - return await list_installations(adapter, request, config) +async def installations(adapter: str, request: Request, config: AppConfig = Depends(get_config), scope: Literal["deployment", "user", "all"] = "deployment") -> InstallationList: + return await list_installations(adapter, request, config, scope) @router.post("/installations", response_model=InstallationList) async def install(body: InstallRequest, request: Request, config: AppConfig = Depends(get_config)) -> InstallationList: - await require_admin_user(request, detail="Admin privileges required to install capabilities.") + user_id = get_effective_user_id() + if body.scope == "deployment": + await require_admin_user(request, detail="Admin privileges required to install capabilities.") + else: + user_id = str((await get_current_user_from_request(request)).id) entries = await asyncio.to_thread(load_catalog) manifest = next((entry for entry in entries if entry.id == body.plugin_id), None) if manifest is None: raise HTTPException(404, "Plugin not found") - context = AdapterContext(request, config, get_effective_user_id()) + if body.scope == "user" and manifest.adapter not in {"mcp", "business"}: + await require_admin_user(request, detail="Admin privileges required to install this capability.") + context = AdapterContext(request, config, user_id, body.scope) await registry.get(manifest.adapter).install(context, manifest, body.name, body.configuration) - return await list_installations(manifest.adapter, request, config) + return await list_installations(manifest.adapter, request, config, body.scope) diff --git a/backend/app/gateway/routers/personal_mcp.py b/backend/app/gateway/routers/personal_mcp.py new file mode 100644 index 0000000000..81585ea2de --- /dev/null +++ b/backend/app/gateway/routers/personal_mcp.py @@ -0,0 +1,126 @@ +"""Owner-only MCP configuration. Deployment MCP routes remain admin-only.""" + +import asyncio +from typing import Literal +from uuid import uuid4 + +from fastapi import APIRouter, HTTPException, Request +from pydantic import ValidationError + +from app.gateway.deps import get_current_user_from_request, is_admin_user +from app.gateway.routers import mcp +from deerflow.capabilities.runtime import ambiguous_installation_ids +from deerflow.config.extensions_config import ExtensionsConfig, atomic_write_extensions_config, extensions_config_file_lock, extensions_config_write_lock +from deerflow.mcp.user_config import read_user_mcp_config, user_mcp_config_path + +router = APIRouter(prefix="/api/mcp/personal/config", tags=["mcp"]) + + +async def _owner(request: Request) -> str: + user = await get_current_user_from_request(request) + return str(user.id) + + +def _response(raw: dict) -> mcp.McpConfigResponse: + return mcp.McpConfigResponse(mcp_servers={name: mcp._mask_server_config(server) for name, server in mcp._mcp_server_responses_from_raw(raw).items()}) + + +def _read_personal_config(user_id: str) -> dict: + try: + return read_user_mcp_config(user_id) + except ValueError as exc: + mcp._raise_invalid_mcp_configuration(str(exc), cause=exc) + + +def _validate_personal_server(server: mcp.McpServerConfigResponse, *, admin: bool) -> None: + # A personal store must not grant ordinary users the deployment operator's + # ability to launch arbitrary host packages or query internal services. + if not admin: + from deerflow.capabilities.business import is_bundled_connection + + bundled = server.type == "stdio" and is_bundled_connection(server.command, server.args, server.env) and not server.cwd + if (server.type not in {"http", "sse"} and not bundled) or server.oauth is not None: + raise HTTPException(403, "Only an administrator may configure host commands or OAuth token endpoints") + from deerflow.community.url_safety import validate_public_http_url + + if not bundled and (not server.url or validate_public_http_url(server.url, action="connect to")): + raise HTTPException(400, "Personal MCP connections require a public HTTP(S) endpoint") + mcp._validate_mcp_update_request(mcp.McpConfigUpdateRequest(mcp_servers={"personal": server})) + + +def _mutate(user_id: str, operation: Literal["create", "update", "delete", "state"], body, *, admin: bool) -> dict: + path = user_mcp_config_path(user_id) + path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) + with extensions_config_write_lock, extensions_config_file_lock(path): + raw = _read_personal_config(user_id) + servers = mcp._raw_mcp_servers(raw) + if operation == "create": + for name, incoming in body.mcp_servers.items(): + if name in servers: + raise HTTPException(409, f"MCP server '{name}' already exists") + mcp._ensure_no_masked_secrets(incoming) + _validate_personal_server(incoming, admin=admin) + definition = incoming.model_dump() + metadata = definition.get("capability") + definition["capability"] = {**(metadata if isinstance(metadata, dict) else {}), "id": str(uuid4())} + definition["personal_public_network"] = not admin + servers[name] = definition + else: + name = body if operation == "delete" else body.server_name + if name not in servers: + raise HTTPException(404, "MCP server not found") + if operation == "delete": + del servers[name] + elif operation == "update": + merged = mcp._merge_preserving_secrets(body.server, mcp._mcp_server_response_from_raw(name, servers[name]), preserve_omitted_fields=False) + _validate_personal_server(merged, admin=admin) + servers[name] = {**merged.model_dump(), "personal_public_network": not admin} + else: + if body.enabled: + _validate_personal_server(mcp._mcp_server_response_from_raw(name, servers[name]), admin=admin) + servers[name]["personal_public_network"] = not admin + servers[name]["enabled"] = body.enabled + candidate = {"mcpServers": servers} + try: + # No environment expansion: these are caller-owned literal values. + ExtensionsConfig.model_validate(candidate) + except ValidationError: + raise HTTPException(400, "Invalid personal MCP configuration") from None + if ambiguous_installation_ids(servers): + raise HTTPException(400, "Duplicate MCP installation IDs") + atomic_write_extensions_config(path, candidate) + path.chmod(0o600) + return candidate + + +@router.get("", response_model=mcp.McpConfigResponse) +async def get_configuration(request: Request): + owner = await _owner(request) + return _response(await asyncio.to_thread(_read_personal_config, owner)) + + +async def _write(request: Request, operation: str, body): + owner = await _owner(request) + admin = await is_admin_user(request) + raw = await asyncio.to_thread(_mutate, owner, operation, body, admin=admin) + return _response(raw) + + +@router.post("/servers", response_model=mcp.McpConfigResponse) +async def create_servers(request: Request, body: mcp.McpConfigUpdateRequest): + return await _write(request, "create", body) + + +@router.put("/server", response_model=mcp.McpConfigResponse) +async def update_server(request: Request, body: mcp.McpServerConfigUpdateRequest): + return await _write(request, "update", body) + + +@router.patch("", response_model=mcp.McpConfigResponse) +async def update_state(request: Request, body: mcp.McpServerStateUpdateRequest): + return await _write(request, "state", body) + + +@router.delete("/servers/{server_name:path}", response_model=mcp.McpConfigResponse) +async def delete_server(request: Request, server_name: str): + return await _write(request, "delete", server_name) diff --git a/backend/packages/harness/deerflow/mcp/AGENTS.md b/backend/packages/harness/deerflow/mcp/AGENTS.md index 456e27117d..3103304bdb 100644 --- a/backend/packages/harness/deerflow/mcp/AGENTS.md +++ b/backend/packages/harness/deerflow/mcp/AGENTS.md @@ -58,3 +58,6 @@ [backend/AGENTS.md](../../../../AGENTS.md)): Long-running MCP work uses a separate durable task runtime rather than keeping remote task IDs or status polling inside the Agent loop. Explicit `task_toolsets` bind raw submit/status/cancel names; only submit remains Agent-visible, and its wrapper persists the remote handle before returning a local ID. `McpTaskService` claims due rows with leases, resolves a protocol-specific `McpTaskDriver`, and writes normalized snapshots back to `mcp_tasks`; expired leases are the restart-recovery mechanism, and a result returned after expiry or after a cancel request must be discarded even when the owner token still matches. The first cancel request fences an in-flight poll lease, while repeats preserve an active cancellation lease so they cannot issue concurrent remote cancels; cancellation backoff starts when the remote attempt finishes, so a slow timeout cannot consume the retry delay. Cancellation, polling, and notification batches isolate per-task exceptions; an unexpected cancellation/poll failure leaves that record's lease to expire, while notification failures release only the affected lease for retry. Input-required and terminal event snapshots are delivered by idempotent Agent runs and marked delivered only after run success; the trusted notification instruction stays outside the input boundary while the serialized remote event is framed as untrusted data. A busy-thread conflict is normalized back to the service boundary so the queued snapshot coalesces to the latest task event. A missing dispatched run becomes a failed delivery attempt, while transient run-store hydration errors stay distinguishable and retry the same lookup. The database is the source of truth; `ThreadState` receives only a bounded current-thread projection, and display names are neutralized at that model-state boundary. The installed process-local submitter is the source of truth for management-tool exposure; hot `mcp_tasks` edits take effect only after restart, and active skills must explicitly declare the list/cancel business tools. **Task notification failure handling**: MCP notification failures use a consecutive counter separate from the idempotency-key `dispatch_attempt`, capped exponential backoff, latest-event rebuilding before a run launches, and a five-attempt budget before `dead_letter`. A permanently missing/mismatched target thread is dead-lettered immediately instead of being recreated or reclaimed. HTTP and Agent cancellation requests return after the durable cancel fence; the background loop alone owns the potentially slow remote call and retry schedule. The HTTP cancel endpoint rejects requests with 503 when the loop is not running (`mcp_tasks_available` false, e.g. `mcp_tasks.enabled=false` with SQL persistence), so a cancellation is never acknowledged without a worker to perform it. The bounded notification error/count/status join poll and cancellation diagnostics in the task detail API and expanded card. + +- Personal MCP configs live under the authenticated user root (`user_config.py`), use literal values, and never enter the deployment tool cache. `user_tools.py` guards owner and current revision before every call. Runtime server names bind owner/name/revision; durable task `driver_data.connection_scope` records personal or deployment ownership at submission, and background calls resolve a personal key only in the persisted task owner's file. Older rows without that field are deployment-owned. When a deployment name matches a personal runtime name, Agent assembly omits the personal tool rather than exposing two tools under one name; an already submitted personal task still uses its recorded personal scope. Keep catalog IDs stable across edits; never fall back by display name. +- Personal definitions without `personal_public_network: true` require current administrator authority at discovery and every tool/durable call. `personal_access.py` uses a host-installed async lookup, never a captured role or cached decision; absent/failed lookup denies privileged connections. The Gateway binds the lookup to fresh account records on its database-owning loop for its lifespan. Deployment connections are unaffected. This is admission control, not termination of operations already admitted before a role change. diff --git a/backend/packages/harness/deerflow/mcp/client.py b/backend/packages/harness/deerflow/mcp/client.py index 70e53a9ebf..f02bb32dc7 100644 --- a/backend/packages/harness/deerflow/mcp/client.py +++ b/backend/packages/harness/deerflow/mcp/client.py @@ -36,6 +36,10 @@ def build_server_params(server_name: str, config: McpServerConfig) -> dict[str, if not config.url: raise ValueError(f"MCP server '{server_name}' with {transport_type} transport requires 'url' field") params["url"] = config.url + if (config.model_extra or {}).get("personal_public_network") is True: + from deerflow.mcp.personal_network import personal_httpx_client_factory + + params["httpx_client_factory"] = personal_httpx_client_factory # Add headers if present if config.headers: # A statically configured value the transport would refuse gets the diff --git a/backend/packages/harness/deerflow/mcp/personal_access.py b/backend/packages/harness/deerflow/mcp/personal_access.py new file mode 100644 index 0000000000..277438314a --- /dev/null +++ b/backend/packages/harness/deerflow/mcp/personal_access.py @@ -0,0 +1,46 @@ +"""Current host authority for personal connections saved with admin privileges.""" + +from collections.abc import Awaitable, Callable + +from langchain_core.tools import ToolException + +from deerflow.config.extensions_config import ExtensionsConfig, McpServerConfig + +PersonalMcpAdminChecker = Callable[[str], Awaitable[bool]] +_admin_checker: PersonalMcpAdminChecker | None = None + + +def set_personal_mcp_admin_checker(checker: PersonalMcpAdminChecker | None) -> PersonalMcpAdminChecker | None: + """Install the host's fresh authority lookup, returning the previous lookup.""" + global _admin_checker + previous, _admin_checker = _admin_checker, checker + return previous + + +def requires_admin(server: McpServerConfig) -> bool: + # Only definitions admitted under the ordinary-user policy carry True. + # Missing markers, including legacy personal files, require authority too. + return (server.model_extra or {}).get("personal_public_network") is not True + + +async def _is_current_admin(owner: str) -> bool: + checker = _admin_checker + if checker is None: + return False + try: + return await checker(owner) is True + except Exception: + # Authority lookup failures must not reuse a previous admin decision. + return False + + +async def require_personal_mcp_access(owner: str, server: McpServerConfig) -> None: + if requires_admin(server) and not await _is_current_admin(owner): + raise ToolException("Current administrator access is required for this personal MCP connection; save it again under your current permissions") + + +async def authorized_personal_config(owner: str, config: ExtensionsConfig) -> ExtensionsConfig: + """Omit privileged connections before discovery can launch or contact them.""" + if not any(server.enabled and requires_admin(server) for server in config.mcp_servers.values()) or await _is_current_admin(owner): + return config + return config.model_copy(update={"mcp_servers": {name: server for name, server in config.mcp_servers.items() if not requires_admin(server)}}) diff --git a/backend/packages/harness/deerflow/mcp/personal_network.py b/backend/packages/harness/deerflow/mcp/personal_network.py new file mode 100644 index 0000000000..5e11f46ffb --- /dev/null +++ b/backend/packages/harness/deerflow/mcp/personal_network.py @@ -0,0 +1,62 @@ +"""Public-network policy for non-operator personal HTTP MCP connections.""" + +import asyncio +import ipaddress + +import httpx + +from deerflow.community.url_safety import resolve_host_addresses, validate_public_http_url + + +def _public_addresses(url: httpx.URL) -> list[str]: + addresses = [] + + def resolve(host): + candidates = resolve_host_addresses(host) + addresses.extend(str(address) for address in candidates) + return candidates + + if validate_public_http_url(str(url), action="connect to", resolver=resolve): + raise ValueError("Personal MCP connections require a public HTTP(S) endpoint") + # Literal IP URLs are validated without calling the resolver. + return list(dict.fromkeys(addresses)) if addresses else [str(ipaddress.ip_address(url.host))] + + +class _PersonalTransport(httpx.AsyncBaseTransport): + def __init__(self): + self._ssl_context = httpx.create_ssl_context(trust_env=False) + self._transports: dict[tuple[bytes, bytes, int | None], httpx.AsyncHTTPTransport] = {} + + async def handle_async_request(self, request: httpx.Request) -> httpx.Response: + addresses = await asyncio.to_thread(_public_addresses, request.url) + origin = (request.url.raw_scheme, request.url.raw_host, request.url.port) + # Pin TCP to the vetted IP, but isolate pools by original origin so two + # names sharing an IP cannot reuse a connection authenticated for one. + if origin not in self._transports: + self._transports[origin] = httpx.AsyncHTTPTransport(verify=self._ssl_context, trust_env=False) + transport = self._transports[origin] + for index, address in enumerate(addresses): + pinned = httpx.Request( + request.method, + request.url.copy_with(host=address), + headers=request.headers, + stream=request.stream, + extensions={**request.extensions, "sni_hostname": request.url.raw_host.decode("ascii")}, + ) + try: + return await transport.handle_async_request(pinned) + except (httpx.ConnectError, httpx.ConnectTimeout): + if index == len(addresses) - 1: + raise + raise AssertionError("Public address validation returned no addresses") + + async def aclose(self): + for transport in self._transports.values(): + await transport.aclose() + + +def personal_httpx_client_factory(headers=None, timeout=None, auth=None): + kwargs = {"headers": headers, "auth": auth, "follow_redirects": False, "trust_env": False, "transport": _PersonalTransport()} + if timeout is not None: + kwargs["timeout"] = timeout + return httpx.AsyncClient(**kwargs) diff --git a/backend/packages/harness/deerflow/mcp/task_tool_caller.py b/backend/packages/harness/deerflow/mcp/task_tool_caller.py index 6290736377..ba73a3dbd2 100644 --- a/backend/packages/harness/deerflow/mcp/task_tool_caller.py +++ b/backend/packages/harness/deerflow/mcp/task_tool_caller.py @@ -4,10 +4,12 @@ from __future__ import annotations import asyncio import logging +import threading +from collections import OrderedDict from collections.abc import Mapping from dataclasses import dataclass from datetime import timedelta -from typing import Any +from typing import Any, Literal from deerflow.config.extensions_config import ExtensionsConfig from deerflow.config.paths import get_paths @@ -17,11 +19,15 @@ from deerflow.mcp.context_headers import build_context_headers_interceptor from deerflow.mcp.headers import apply_header_overrides from deerflow.mcp.interceptors import build_mcp_tool_interceptors from deerflow.mcp.oauth import OAuthTokenManager, build_oauth_tool_interceptor +from deerflow.mcp.personal_access import require_personal_mcp_access from deerflow.mcp.session_pool import MCPSessionPool, call_pooled_session_tool, get_session_pool +from deerflow.mcp.user_config import PersonalMcpConfigSnapshot, load_user_mcp_config_if_changed from deerflow.mcp_scope import mcp_session_scope_key from deerflow.runtime.user_context import reset_current_user, set_current_user logger = logging.getLogger(__name__) +# Cached callers retain personal credentials and OAuth state in memory. +_MAX_PERSONAL_CALLERS = 128 @dataclass @@ -64,6 +70,8 @@ class McpTaskToolCaller: oauth_token_manager: OAuthTokenManager | None = None, ) -> None: self._extensions_config = extensions_config + self._personal_callers: OrderedDict[str, tuple[PersonalMcpConfigSnapshot, McpTaskToolCaller | None]] = OrderedDict() + self._personal_callers_lock = threading.Lock() self._oauth_token_manager = oauth_token_manager or OAuthTokenManager.from_extensions_config(extensions_config) context_headers_interceptor = build_context_headers_interceptor(extensions_config) # Built once so the two chains keep an identical interceptor order and a @@ -100,14 +108,68 @@ class McpTaskToolCaller: thread_id: str, thread_incarnation: str | None = None, request_scoped_headers: bool = False, + connection_scope: Literal["deployment", "personal"] = "deployment", ) -> Any: """Call a raw MCP tool. ``request_scoped_headers`` opts this call into the ``headers_from_context`` interceptor. Only the durable *submit* may set it: submit is awaited inside the Agent run that carries the secrets, while status and cancel - run after that run ended. + run after that run ended. ``connection_scope`` is captured in the task + binding, so an equal deployment server name cannot change its owner. """ + if connection_scope == "personal": + caller = await asyncio.to_thread(self._personal_caller_for, user_id, server_name) + await require_personal_mcp_access(user_id, caller._extensions_config.mcp_servers[server_name]) + return await caller._call_configured_tool( + server_name=server_name, + tool_name=tool_name, + arguments=arguments, + user_id=user_id, + thread_id=thread_id, + thread_incarnation=thread_incarnation, + request_scoped_headers=request_scoped_headers, + ) + if connection_scope != "deployment": + raise ValueError("Invalid MCP task connection scope") + return await self._call_configured_tool( + server_name=server_name, + tool_name=tool_name, + arguments=arguments, + user_id=user_id, + thread_id=thread_id, + thread_incarnation=thread_incarnation, + request_scoped_headers=request_scoped_headers, + ) + + def _personal_caller_for(self, user_id: str, server_name: str) -> McpTaskToolCaller: + with self._personal_callers_lock: + previous = self._personal_callers.get(user_id) + snapshot = load_user_mcp_config_if_changed(user_id, previous[0] if previous else None) + caller = previous[1] if previous and previous[0] is snapshot else None + self._personal_callers[user_id] = (snapshot, caller) + self._personal_callers.move_to_end(user_id) + if len(self._personal_callers) > _MAX_PERSONAL_CALLERS: + self._personal_callers.popitem(last=False) + server = snapshot.config.mcp_servers.get(server_name) + if server is None or not server.enabled: + raise LookupError("Personal MCP task connection is missing, disabled or changed") + if caller is None: + caller = McpTaskToolCaller(snapshot.config) + self._personal_callers[user_id] = (snapshot, caller) + return caller + + async def _call_configured_tool( + self, + *, + server_name: str, + tool_name: str, + arguments: dict[str, Any], + user_id: str, + thread_id: str, + thread_incarnation: str | None, + request_scoped_headers: bool, + ) -> Any: is_background_call = not request_scoped_headers interceptors = self._interceptors if is_background_call else self._submit_interceptors server_config = self._extensions_config.get_enabled_mcp_servers().get(server_name) diff --git a/backend/packages/harness/deerflow/mcp/tasks/ordinary.py b/backend/packages/harness/deerflow/mcp/tasks/ordinary.py index c80e172f7f..c743735bb2 100644 --- a/backend/packages/harness/deerflow/mcp/tasks/ordinary.py +++ b/backend/packages/harness/deerflow/mcp/tasks/ordinary.py @@ -34,6 +34,7 @@ class McpTaskToolCaller(Protocol): thread_id: str, thread_incarnation: str | None = None, request_scoped_headers: bool = False, + connection_scope: Literal["deployment", "personal"] = "deployment", ) -> Any: ... @@ -86,6 +87,14 @@ def _tool_name(data: dict[str, Any], role: str) -> str: return value +def _connection_scope(data: dict[str, Any]) -> Literal["deployment", "personal"]: + # Older task rows predate personal connections and are deployment-owned. + scope = data.get("connection_scope", "deployment") + if scope not in ("deployment", "personal"): + raise McpTaskProtocolError("Task driver_data has an invalid connection scope") + return scope + + def _first_error_text(call_result: Any) -> str | None: content = getattr(call_result, "content", None) if not isinstance(content, (list, tuple)): @@ -165,6 +174,7 @@ class OrdinaryMcpTaskDriver: # durable-task call that can carry the run's request-scoped # credentials; status and cancel run after that run ended. request_scoped_headers=True, + connection_scope=_connection_scope(request.driver_data), ) payload = _parse( _SubmitPayload, @@ -187,6 +197,7 @@ class OrdinaryMcpTaskDriver: user_id=task.user_id, thread_id=task.thread_id, thread_incarnation=task.thread_incarnation, + connection_scope=_connection_scope(task.driver_data), ) payload = _parse( _StatusPayload, @@ -206,6 +217,7 @@ class OrdinaryMcpTaskDriver: user_id=task.user_id, thread_id=task.thread_id, thread_incarnation=task.thread_incarnation, + connection_scope=_connection_scope(task.driver_data), ) payload = _parse( _CancelPayload, diff --git a/backend/packages/harness/deerflow/mcp/tools.py b/backend/packages/harness/deerflow/mcp/tools.py index 298f55a99e..524338a86e 100644 --- a/backend/packages/harness/deerflow/mcp/tools.py +++ b/backend/packages/harness/deerflow/mcp/tools.py @@ -726,6 +726,7 @@ def _make_background_submit_tool( submit_tool: str, status_tool: str, cancel_tool: str, + connection_scope: str, ) -> BaseTool: background_contract = f"Submitted as durable background task {task_name!r}; returns a DeerFlow task ID immediately and status polling is handled automatically." @@ -755,6 +756,7 @@ def _make_background_submit_tool( "submit_tool": submit_tool, "status_tool": status_tool, "cancel_tool": cancel_tool, + "connection_scope": connection_scope, }, ), ) @@ -780,6 +782,7 @@ def _configure_task_tools_for_server( server_name: str, server_config: McpServerConfig, tool_name_prefix: bool, + connection_scope: str = "deployment", ) -> list[BaseTool]: """Hide driver-only tools and replace submit with a durable wrapper.""" if not server_config.task_toolsets: @@ -829,12 +832,13 @@ def _configure_task_tools_for_server( submit_tool=toolset.submit_tool, status_tool=toolset.status_tool, cancel_tool=toolset.cancel_tool, + connection_scope=connection_scope, ) ) return configured -async def get_mcp_tools(extensions_config: ExtensionsConfig | None = None) -> list[BaseTool]: +async def get_mcp_tools(extensions_config: ExtensionsConfig | None = None, *, personal_user_id: str | None = None) -> list[BaseTool]: """Get all tools from enabled MCP servers. Tools using stdio transport are wrapped with persistent-session logic so @@ -864,7 +868,19 @@ async def get_mcp_tools(extensions_config: ExtensionsConfig | None = None) -> li # reflected when initializing MCP tools. Callers that need to prove which # revision produced these tools pass the instance they snapshotted instead. extensions_config = ExtensionsConfig.from_file() - validate_mcp_task_config_snapshot(extensions_config) + if personal_user_id is None: + validate_mcp_task_config_snapshot(extensions_config) + else: + from deerflow.mcp.personal_access import authorized_personal_config + from deerflow.mcp.tasks.runtime import is_mcp_task_runtime_available + from deerflow.mcp.user_config import load_user_mcp_config + + current = await asyncio.to_thread(load_user_mcp_config, personal_user_id) + if current != extensions_config: + raise McpTaskConfigurationError("Personal MCP configuration changed during discovery; retry the run") + extensions_config = current = await authorized_personal_config(personal_user_id, current) + if any(server.task_toolsets for server in current.mcp_servers.values()) and not is_mcp_task_runtime_available(): + raise McpTaskConfigurationError("Personal MCP task toolsets require the platform's durable task runtime") servers_config = build_servers_config(extensions_config) if not servers_config: @@ -1022,6 +1038,7 @@ async def get_mcp_tools(extensions_config: ExtensionsConfig | None = None) -> li server_name=source_name, server_config=server_cfg, tool_name_prefix=tool_name_prefix, + connection_scope="personal" if personal_user_id is not None else "deployment", ) wrapped_tools.extend(current_server_tools) diff --git a/backend/packages/harness/deerflow/mcp/user_config.py b/backend/packages/harness/deerflow/mcp/user_config.py new file mode 100644 index 0000000000..6c8ad2d990 --- /dev/null +++ b/backend/packages/harness/deerflow/mcp/user_config.py @@ -0,0 +1,77 @@ +"""Persistent personal MCP connections, separate from deployment configuration.""" + +from __future__ import annotations + +import hashlib +import json +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from deerflow.config.extensions_config import ExtensionsConfig, read_raw_extensions_config +from deerflow.config.paths import get_paths + +PERSONAL_SERVER_PREFIX = "personal_" +_FileSignature = tuple[int, int, int, int, int] | None + + +@dataclass(frozen=True) +class PersonalMcpConfigSnapshot: + path: Path + signature: _FileSignature + config: ExtensionsConfig + + +def user_mcp_config_path(user_id: str) -> Path: + """Use the same validated user root as skills and account integrations.""" + return get_paths().user_dir(user_id) / "integrations" / "mcp.json" + + +def read_user_mcp_config(user_id: str) -> dict[str, Any]: + path = user_mcp_config_path(user_id) + if not path.exists(): + return {"mcpServers": {}} + return read_raw_extensions_config(path) + + +def personal_server_name(user_id: str, name: str, configuration: dict[str, Any]) -> str: + """Bind runtime sessions and durable tasks to one owner and config revision. + + Display names remain local to each user's file. Never let a same-named + personal connection replace a deployment connection or another user's tools. + """ + identity = json.dumps([user_id, name, configuration], sort_keys=True) + return PERSONAL_SERVER_PREFIX + hashlib.sha256(identity.encode()).hexdigest()[:32] + + +def load_user_mcp_config(user_id: str) -> ExtensionsConfig: + """Read literal user values; personal files cannot resolve host $ENV secrets.""" + raw = read_user_mcp_config(user_id) + servers = {} + for name, definition in raw.get("mcpServers", {}).items(): + runtime_name = personal_server_name(user_id, name, definition) + servers[runtime_name] = {**definition, "tool_name_prefix": True} + return ExtensionsConfig.model_validate({"mcpServers": servers}) + + +def _file_signature(path: Path) -> _FileSignature: + try: + stat = path.stat() + except FileNotFoundError: + return None + # Include file identity and ctime: atomic replacement can preserve size and + # mtime, while an in-place edit can preserve the inode. + return stat.st_dev, stat.st_ino, stat.st_size, stat.st_mtime_ns, stat.st_ctime_ns + + +def load_user_mcp_config_if_changed(user_id: str, cached: PersonalMcpConfigSnapshot | None = None) -> PersonalMcpConfigSnapshot: + """Reuse validated config while the owner's on-disk file is unchanged.""" + path = user_mcp_config_path(user_id) + for _ in range(3): + signature = _file_signature(path) + if cached is not None and cached.path == path and cached.signature == signature: + return cached + config = load_user_mcp_config(user_id) + if _file_signature(path) == signature: + return PersonalMcpConfigSnapshot(path, signature, config) + raise RuntimeError("Personal MCP configuration changed during loading; retry the call") diff --git a/backend/packages/harness/deerflow/mcp/user_tools.py b/backend/packages/harness/deerflow/mcp/user_tools.py new file mode 100644 index 0000000000..b76ced9689 --- /dev/null +++ b/backend/packages/harness/deerflow/mcp/user_tools.py @@ -0,0 +1,61 @@ +"""Load only the caller's personal tools; never publish them in the global cache.""" + +from __future__ import annotations + +import asyncio +import functools +import threading + +from langchain_core.tools import ToolException + +from deerflow.mcp.personal_access import require_personal_mcp_access +from deerflow.mcp.user_config import PersonalMcpConfigSnapshot, load_user_mcp_config, load_user_mcp_config_if_changed +from deerflow.runtime.user_context import resolve_runtime_user_id +from deerflow.tools.mcp_metadata import get_mcp_source +from deerflow.tools.sync import make_sync_tool_wrapper + + +def _guard(tool, owner: str, server_name: str): + original = tool.coroutine + snapshot: PersonalMcpConfigSnapshot | None = None + snapshot_lock = threading.Lock() + + def current_connection(): + nonlocal snapshot + with snapshot_lock: + snapshot = load_user_mcp_config_if_changed(owner, snapshot) + server = snapshot.config.mcp_servers.get(server_name) + return server if server is not None and server.enabled else None + + @functools.wraps(original) + async def invoke(*args, **kwargs): + if resolve_runtime_user_id(kwargs.get("runtime")) != owner: + raise ToolException("This MCP connection belongs to another user") + server = await asyncio.to_thread(current_connection) + if server is None: + raise ToolException("This personal MCP connection was changed, disabled or removed; start a new run") + await require_personal_mcp_access(owner, server) + return await original(*args, **kwargs) + + return tool.model_copy(update={"coroutine": invoke, "func": make_sync_tool_wrapper(invoke, tool.name)}) + + +async def _load(owner: str, config): + from deerflow.mcp.tools import get_mcp_tools + + tools = await get_mcp_tools(config, personal_user_id=owner) + return [_guard(tool, owner, source["server_name"]) for tool in tools if (source := get_mcp_source(tool))] + + +def get_user_mcp_tools(): + """Discover from one literal snapshot, with an ownership check on every call. + + Deliberately keep personal discovery out of the deployment's singleton + cache. Stdio session reuse remains owner/thread/revision-scoped. + """ + owner = resolve_runtime_user_id(None) + config = load_user_mcp_config(owner) + if not config.get_enabled_mcp_servers(): + return [], config + tools = make_sync_tool_wrapper(_load, "personal MCP discovery")(owner, config) + return tools, config diff --git a/backend/packages/harness/deerflow/tools/tools.py b/backend/packages/harness/deerflow/tools/tools.py index d894a05d56..1bd5a1d57c 100644 --- a/backend/packages/harness/deerflow/tools/tools.py +++ b/backend/packages/harness/deerflow/tools/tools.py @@ -26,7 +26,7 @@ from deerflow.tools.builtins import ( task_tool, view_image_tool, ) -from deerflow.tools.mcp_metadata import tag_mcp_tool +from deerflow.tools.mcp_metadata import get_mcp_source, tag_mcp_tool from deerflow.tools.sync import make_sync_tool_wrapper logger = logging.getLogger(__name__) @@ -262,6 +262,25 @@ def get_available_tools( # MCP pays no config-hashing or discovery cost, while one that # did still checks the existing cache for staleness. refresh_mcp_cache_if_active() + from deerflow.mcp.user_tools import get_user_mcp_tools + + try: + personal_tools, personal_config = get_user_mcp_tools() + except Exception as exc: + # Invalid personal credentials must neither be logged nor + # bypass explicit selection of the platform tools. + logger.warning("Could not load personal MCP tools (%s)", type(exc).__name__) + personal_tools, personal_config = [], ExtensionsConfig() + colliding_names = extensions_config.mcp_servers.keys() & personal_config.mcp_servers.keys() + if colliding_names: + # A deployment name can match a generated personal runtime + # name. Keep only the deployment tool and its routing metadata. + logger.warning("Skipping personal MCP tools with deployment name collisions: %s", sorted(colliding_names)) + personal_tools = [tool for tool in personal_tools if (source := get_mcp_source(tool)) and source["server_name"] not in colliding_names] + available_servers = {name: server for name, server in personal_config.mcp_servers.items() if name not in colliding_names} + personal_config = personal_config.model_copy(update={"mcp_servers": available_servers}) + mcp_tools = [*mcp_tools, *personal_tools] + extensions_config = extensions_config.model_copy(update={"mcp_servers": {**extensions_config.mcp_servers, **personal_config.mcp_servers}}) if mcp_plugins is not None: from deerflow.capabilities.runtime import filter_mcp_plugins diff --git a/backend/tests/test_gateway_lifespan_shutdown.py b/backend/tests/test_gateway_lifespan_shutdown.py index 2316bcf68e..44edb3a9b7 100644 --- a/backend/tests/test_gateway_lifespan_shutdown.py +++ b/backend/tests/test_gateway_lifespan_shutdown.py @@ -172,6 +172,30 @@ def test_lifespan_sweeps_upload_staging_files_on_startup(): stop_channel_service.assert_awaited_once() +def test_personal_mcp_authority_spans_runtime_startup_and_shutdown(): + from deerflow.mcp import personal_access + + events = [] + previous = personal_access._admin_checker + + @asynccontextmanager + async def checked_runtime(_app, _config): + checker = personal_access._admin_checker + assert checker is not None and checker is not previous + events.append("start") + try: + yield + finally: + assert personal_access._admin_checker is checker + events.append("stop") + + with patch(f"{__name__}._noop_langgraph_runtime", checked_runtime): + asyncio.run(_run_lifespan_with_upload_staging_cleanup()) + + assert events == ["start", "stop"] + assert personal_access._admin_checker is previous + + async def _run_lifespan_with_mcp_task_config_snapshot() -> None: from app.gateway.app import lifespan from deerflow.config.extensions_config import ExtensionsConfig diff --git a/backend/tests/test_mcp_task_ordinary_driver.py b/backend/tests/test_mcp_task_ordinary_driver.py index ddc3025e41..5720e1532b 100644 --- a/backend/tests/test_mcp_task_ordinary_driver.py +++ b/backend/tests/test_mcp_task_ordinary_driver.py @@ -1,3 +1,4 @@ +from dataclasses import replace from types import SimpleNamespace import pytest @@ -80,10 +81,41 @@ async def test_submit_uses_structured_content_and_keeps_remote_id_out_of_driver_ # Submit is the one durable-task call awaited inside the Agent run, # so it is the only one that may carry request-scoped credentials. "request_scoped_headers": True, + "connection_scope": "deployment", } ] +@pytest.mark.asyncio +async def test_personal_connection_scope_survives_submit_status_and_cancel() -> None: + caller = FakeCaller( + _result({"task_id": "remote-1", "status": "running"}), + _result({"task_id": "remote-1", "status": "completed"}), + _result({"task_id": "remote-1", "status": "cancelled"}), + ) + driver = OrdinaryMcpTaskDriver(caller) + request = _request() + request = replace(request, driver_data={**request.driver_data, "connection_scope": "personal"}) + submission = await driver.submit(request) + reference = replace(_reference(), driver_data=submission.driver_data) + + await driver.get_status(reference) + await driver.cancel(reference) + + assert submission.driver_data["connection_scope"] == "personal" + assert [call["connection_scope"] for call in caller.calls] == ["personal"] * 3 + + +@pytest.mark.asyncio +async def test_unknown_task_connection_scope_fails_closed() -> None: + driver = OrdinaryMcpTaskDriver(FakeCaller()) + reference = _reference() + reference = replace(reference, driver_data={**reference.driver_data, "connection_scope": "unknown"}) + + with pytest.raises(McpTaskProtocolError, match="connection scope"): + await driver.get_status(reference) + + @pytest.mark.asyncio @pytest.mark.parametrize( "structured_content,match", diff --git a/backend/tests/test_mcp_task_tool_wrapping.py b/backend/tests/test_mcp_task_tool_wrapping.py index 6cf262d6d5..d7ab4e8775 100644 --- a/backend/tests/test_mcp_task_tool_wrapping.py +++ b/backend/tests/test_mcp_task_tool_wrapping.py @@ -124,7 +124,8 @@ def test_configured_task_toolsets_fail_when_a_raw_tool_is_missing() -> None: @pytest.mark.asyncio -async def test_submit_wrapper_persists_before_returning_only_the_local_handle() -> None: +@pytest.mark.parametrize("connection_scope", ["deployment", "personal"]) +async def test_submit_wrapper_persists_before_returning_only_the_local_handle(connection_scope: str) -> None: submitter = FakeSubmitter() set_mcp_task_submitter(submitter) try: @@ -137,6 +138,7 @@ async def test_submit_wrapper_persists_before_returning_only_the_local_handle() server_name="reports", server_config=_server_config(), tool_name_prefix=False, + connection_scope=connection_scope, ) submit_tool = configured[0] runtime = SimpleNamespace( @@ -165,6 +167,7 @@ async def test_submit_wrapper_persists_before_returning_only_the_local_handle() "submit_tool": "submit_report", "status_tool": "get_report_status", "cancel_tool": "cancel_report", + "connection_scope": connection_scope, } finally: set_mcp_task_submitter(None) diff --git a/backend/tests/test_personal_mcp.py b/backend/tests/test_personal_mcp.py new file mode 100644 index 0000000000..9e8c8aa367 --- /dev/null +++ b/backend/tests/test_personal_mcp.py @@ -0,0 +1,545 @@ +"""Personal MCP persistence, HTTP ownership and real MCP credential routing.""" + +import asyncio +import ipaddress +import json +from contextlib import asynccontextmanager +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock, patch + +import httpx +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from langchain_core.tools import ToolException + +from app.gateway.routers import personal_mcp +from deerflow.config.extensions_config import ExtensionsConfig +from deerflow.mcp.user_config import load_user_mcp_config, read_user_mcp_config, user_mcp_config_path +from deerflow.runtime.user_context import reset_current_user, set_current_user + + +@pytest.fixture +def personal_client(tmp_path, monkeypatch): + from deerflow.config.paths import Paths + + monkeypatch.setattr("deerflow.mcp.user_config.get_paths", lambda: Paths(base_dir=tmp_path)) + monkeypatch.setattr("deerflow.mcp.personal_access._admin_checker", AsyncMock(return_value=True)) + monkeypatch.setattr("app.gateway.personal_mcp_access._is_current_admin", AsyncMock(return_value=True)) + app = FastAPI() + + @app.middleware("http") + async def identity(request, call_next): + if name := request.headers.get("test-user"): + request.state.user = SimpleNamespace(id=name, system_role=request.headers.get("test-role", "admin")) + request.state.auth_source = "session" + return await call_next(request) + + app.include_router(personal_mcp.router) + with TestClient(app) as client: + yield client + + +def create(client, user, *, name="github", token=None, role="admin"): + return client.post( + "/api/mcp/personal/config/servers", + headers={"test-user": user, "test-role": role}, + json={"mcp_servers": {name: {"type": "http", "url": "https://example.com/mcp", "headers": {"Authorization": token or f"Bearer {user}"}}}}, + ) + + +def test_persistent_same_name_connections_are_owner_only(personal_client): + client = personal_client + assert client.get("/api/mcp/personal/config").status_code == 401 + for user in ("alice", "bob"): + result = create(client, user) + assert result.status_code == 200, result.text + assert result.json()["mcp_servers"]["github"]["headers"]["Authorization"] == "***" + assert read_user_mcp_config("alice")["mcpServers"]["github"]["headers"]["Authorization"] == "Bearer alice" + assert read_user_mcp_config("bob")["mcpServers"]["github"]["headers"]["Authorization"] == "Bearer bob" + assert user_mcp_config_path("alice").stat().st_mode & 0o777 == 0o600 + assert create(client, "alice").status_code == 409 + assert create(client, "alice", name="only-alice").status_code == 200 + assert client.delete("/api/mcp/personal/config/servers/only-alice", headers={"test-user": "bob"}).status_code == 404 + result = client.get("/api/mcp/personal/config", headers={"test-user": "bob"}) + assert "only-alice" not in result.text + before = user_mcp_config_path("alice").read_bytes() + assert client.patch("/api/mcp/personal/config", headers={"test-user": "bob"}, json={"server_name": "github", "enabled": False}).status_code == 200 + assert user_mcp_config_path("alice").read_bytes() == before + # A fresh read from disk, not a request-local cache, retains each owner. + assert len(load_user_mcp_config("alice").get_enabled_mcp_servers()) == 2 + assert not load_user_mcp_config("bob").get_enabled_mcp_servers() + + +@pytest.mark.parametrize("contents", ["{", "[]"]) +def test_corrupt_personal_config_returns_client_errors_without_overwriting_it(personal_client, contents): + path = user_mcp_config_path("alice") + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(contents) + headers = {"test-user": "alice"} + requests = [ + ("GET", "/api/mcp/personal/config", None), + ("POST", "/api/mcp/personal/config/servers", {"mcp_servers": {"new": {"type": "http", "url": "https://example.com/mcp"}}}), + ("PUT", "/api/mcp/personal/config/server", {"server_name": "new", "server": {"type": "http", "url": "https://example.com/mcp"}}), + ("PATCH", "/api/mcp/personal/config", {"server_name": "new", "enabled": False}), + ("DELETE", "/api/mcp/personal/config/servers/new", None), + ] + for method, url, body in requests: + response = personal_client.request(method, url, headers=headers, json=body) + assert response.status_code == 400, (method, response.text) + assert "Extensions configuration" in response.json()["detail"] + assert path.read_text() == contents + assert personal_client.get("/api/mcp/personal/config", headers={"test-user": "bob"}).status_code == 200 + + +def test_masked_edit_and_delete_do_not_touch_platform_or_peer(personal_client, tmp_path, monkeypatch): + client = personal_client + platform = tmp_path / "platform.json" + platform.write_text(json.dumps({"mcpServers": {"github": {"type": "http", "url": "https://example.com/platform"}}})) + monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(platform)) + before = platform.read_bytes() + create(client, "alice") + create(client, "bob") + result = client.put("/api/mcp/personal/config/server", headers={"test-user": "alice"}, json={"server_name": "github", "server": {"type": "http", "url": "https://example.com/updated", "headers": {"Authorization": "***"}}}) + assert result.status_code == 200, result.text + assert read_user_mcp_config("alice")["mcpServers"]["github"]["headers"]["Authorization"] == "Bearer alice" + assert client.delete("/api/mcp/personal/config/servers/github", headers={"test-user": "alice"}).status_code == 200 + assert not load_user_mcp_config("alice").mcp_servers + assert read_user_mcp_config("bob")["mcpServers"]["github"]["headers"]["Authorization"] == "Bearer bob" + assert platform.read_bytes() == before + + +@pytest.mark.parametrize("contents", ["{", "[]"]) +@pytest.mark.parametrize("adapter", ["mcp", "business"]) +def test_corrupt_personal_config_in_capability_listings(personal_client, tmp_path, monkeypatch, contents, adapter): + from app.gateway.deps import get_config + from app.gateway.routers import capabilities + + app = personal_client.app + app.include_router(capabilities.router) + app.dependency_overrides[get_config] = lambda: SimpleNamespace() + deployment = tmp_path / "deployment.json" + deployment.write_text(json.dumps({"mcpServers": {"shared": {"type": "http", "url": "https://example.com/mcp"}}})) + monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(deployment)) + path = user_mcp_config_path("alice") + path.parent.mkdir(parents=True) + path.write_text(contents) + url = f"/api/capabilities/installations/{adapter}" + for scope in ("user", "all"): + response = personal_client.get(url, params={"scope": scope}, headers={"test-user": "alice"}) + assert response.status_code == 400 + assert "Extensions configuration" in response.json()["detail"] + assert personal_client.get(url, params={"scope": "deployment"}, headers={"test-user": "alice"}).status_code == 200 + assert personal_client.get(url, params={"scope": "all"}, headers={"test-user": "bob"}).status_code == 200 + assert path.read_text() == contents + + +def test_personal_values_do_not_resolve_platform_environment(personal_client, monkeypatch): + monkeypatch.setenv("PLATFORM_ONLY_KEY", "platform-secret") + assert create(personal_client, "alice", token="$PLATFORM_ONLY_KEY").status_code == 200 + config = load_user_mcp_config("alice") + assert next(iter(config.mcp_servers.values())).headers["Authorization"] == "$PLATFORM_ONLY_KEY" + + +def test_catalog_listing_and_installation_respect_owner(personal_client, monkeypatch, tmp_path): + from app.gateway.deps import get_config + from app.gateway.routers import capabilities + + app = personal_client.app + app.include_router(capabilities.router) + app.dependency_overrides[get_config] = lambda: SimpleNamespace() + platform = tmp_path / "deployment.json" + platform.write_text(json.dumps({"mcpServers": {"shared": {"type": "http", "url": "https://example.com/platform"}}})) + monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(platform)) + monkeypatch.setattr("deerflow.community.url_safety.validate_public_http_url", lambda *a, **k: None) + response = personal_client.post( + "/api/capabilities/installations", + headers={"test-user": "alice", "test-role": "user"}, + json={"plugin_id": "github", "name": "my-github", "scope": "user", "configuration": {"type": "http", "url": "https://example.com/mcp", "headers": {"Authorization": "Bearer alice"}}}, + ) + assert response.status_code == 200, response.text + assert response.json()["can_manage"] is True + assert response.json()["items"][0]["scope"] == "user" + for user, expected in (("alice", {"shared", "my-github"}), ("bob", {"shared"})): + response = personal_client.get("/api/capabilities/installations/mcp?scope=all", headers={"test-user": user}) + assert response.status_code == 200, response.text + assert {item["name"] for item in response.json()["items"]} == expected + assert "Bearer alice" not in response.text + + +def test_tool_assembly_combines_platform_and_only_current_owner(personal_client, monkeypatch, tmp_path): + from langchain_core.tools import StructuredTool + + from deerflow.tools.mcp_metadata import tag_mcp_tool + from deerflow.tools.tools import get_available_tools + + path = tmp_path / "deployment.json" + path.write_text(json.dumps({"mcpServers": {"shared": {"type": "http", "url": "https://example.com/mcp"}}})) + monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(path)) + shared = tag_mcp_tool(StructuredTool.from_function(lambda: "platform", name="shared_test", description="Shared tool"), server_name="shared") + monkeypatch.setattr("deerflow.mcp.cache.get_cached_mcp_tools", lambda: [shared]) + + async def discover(config, **kwargs): + name = next(iter(config.mcp_servers)) + + async def echo(): + return config.mcp_servers[name].headers["Authorization"] + + return [tag_mcp_tool(StructuredTool.from_function(coroutine=echo, name=name + "_test", description="Personal tool"), server_name=name)] + + monkeypatch.setattr("deerflow.mcp.tools.get_mcp_tools", discover) + config = SimpleNamespace(tools=[], models=[], get_model_config=lambda _: None) + for user in ("alice", "bob"): + create(personal_client, user) + identity = set_current_user(SimpleNamespace(id=user)) + try: + tools = get_available_tools(app_config=config) + assert shared in tools + personal = [tool for tool in tools if tool.name.startswith("personal_")] + assert len(personal) == 1 + assert personal[0].invoke({}) == f"Bearer {user}" + # Selecting the peer's installation cannot add that peer's tools. + peer = "bob" if user == "alice" else "alice" + peer_id = next(iter(read_user_mcp_config(peer)["mcpServers"].values()), {}).get("capability", {}).get("id", "not-owned") + assert not any(tool.name.startswith("personal_") for tool in get_available_tools(app_config=config, mcp_plugins=[peer_id])) + finally: + reset_current_user(identity) + + +def test_deployment_name_collision_does_not_publish_a_personal_tool(personal_client, monkeypatch, tmp_path): + from langchain_core.tools import StructuredTool + + from deerflow.tools.mcp_metadata import tag_mcp_tool + from deerflow.tools.tools import get_available_tools + + assert create(personal_client, "alice").status_code == 200 + name = next(iter(load_user_mcp_config("alice").mcp_servers)) + path = tmp_path / "deployment.json" + path.write_text(json.dumps({"mcpServers": {name: {"type": "http", "url": "https://example.com/platform"}}})) + monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(path)) + platform = tag_mcp_tool(StructuredTool.from_function(lambda: "platform", name=name + "_test", description="Shared tool"), server_name=name) + monkeypatch.setattr("deerflow.mcp.cache.get_cached_mcp_tools", lambda: [platform]) + + async def discover(config, **kwargs): + async def personal(): + return "personal" + + return [tag_mcp_tool(StructuredTool.from_function(coroutine=personal, name=name + "_test", description="Personal tool"), server_name=name)] + + monkeypatch.setattr("deerflow.mcp.tools.get_mcp_tools", discover) + config = SimpleNamespace(tools=[], models=[], get_model_config=lambda _: None) + identity = set_current_user(SimpleNamespace(id="alice")) + try: + tools = get_available_tools(app_config=config) + assert [tool for tool in tools if tool.name == name + "_test"] == [platform] + personal_id = read_user_mcp_config("alice")["mcpServers"]["github"]["capability"]["id"] + selected = get_available_tools(app_config=config, mcp_plugins=[personal_id]) + assert not any(tool.name == name + "_test" for tool in selected) + finally: + reset_current_user(identity) + + +def test_untrusted_users_cannot_launch_packages_or_connect_to_private_hosts(personal_client, monkeypatch): + client = personal_client + response = client.post("/api/mcp/personal/config/servers", headers={"test-user": "alice", "test-role": "user"}, json={"mcp_servers": {"shell": {"type": "stdio", "command": "npx", "args": ["untrusted-package"]}}}) + assert response.status_code == 403 + response = client.post("/api/mcp/personal/config/servers", headers={"test-user": "alice", "test-role": "user"}, json={"mcp_servers": {"private": {"type": "http", "url": "http://127.0.0.1/mcp", "personal_public_network": False}}}) + assert response.status_code == 400 + monkeypatch.setattr("deerflow.community.url_safety.validate_public_http_url", lambda *a, **k: None) + assert create(client, "alice", role="user").status_code == 200 + assert read_user_mcp_config("alice")["mcpServers"]["github"]["personal_public_network"] is True + + +@pytest.mark.asyncio +async def test_personal_network_rechecks_destination_before_each_request(monkeypatch): + from deerflow.mcp import personal_network + + received = [] + blocked = False + + def transport(request): + received.append(str(request.url)) + return httpx.Response(302, headers={"location": "http://127.0.0.1/private"}) + + monkeypatch.setattr(personal_network.httpx, "AsyncHTTPTransport", lambda **kwargs: httpx.MockTransport(transport)) + monkeypatch.setattr(personal_network, "resolve_host_addresses", lambda host: [ipaddress.ip_address("127.0.0.1" if blocked else "8.8.8.8")]) + async with personal_network.personal_httpx_client_factory() as client: + assert (await client.get("https://example.com/mcp")).status_code == 302 + assert received == ["https://8.8.8.8/mcp"] + blocked = True + with pytest.raises(ValueError, match="public HTTP"): + await client.get("https://example.com/mcp") + assert len(received) == 1 + + +@pytest.mark.asyncio +async def test_real_mcp_calls_keep_credentials_separate_and_reject_stale_tools(personal_client, monkeypatch): + from mcp.server.fastmcp import Context, FastMCP + from mcp.server.transport_security import TransportSecuritySettings + + from deerflow.mcp import client as mcp_client + from deerflow.mcp.user_tools import _load + + server = FastMCP("identity", stateless_http=True, json_response=True, transport_security=TransportSecuritySettings(allowed_hosts=["example.com"])) + received = [] + + @server.tool() + async def whoami(ctx: Context) -> str: + credential = ctx.request_context.request.headers.get("authorization") + received.append(credential) + return credential + + app = server.streamable_http_app() + build = mcp_client.build_server_params + + def params(name, config): + result = build(name, config) + result["httpx_client_factory"] = lambda headers=None, timeout=None, auth=None: httpx.AsyncClient(transport=httpx.ASGITransport(app=app), headers=headers, timeout=timeout or 30, auth=auth) + return result + + monkeypatch.setattr(mcp_client, "build_server_params", params) + for user in ("alice", "bob"): + assert create(personal_client, user).status_code == 200 + + async def run(user): + identity = set_current_user(SimpleNamespace(id=user)) + try: + tools = await _load(user, load_user_mcp_config(user)) + assert len(tools) == 1 + await tools[0].ainvoke({}) + return tools[0] + finally: + reset_current_user(identity) + + async with app.router.lifespan_context(app): + alice_tool, bob_tool = await asyncio.gather(run("alice"), run("bob")) + assert sorted(received) == ["Bearer alice", "Bearer bob"] + assert alice_tool.name != bob_tool.name + identity = set_current_user(SimpleNamespace(id="bob")) + try: + with pytest.raises(ToolException, match="another user"): + await alice_tool.ainvoke({}) + assert personal_client.patch("/api/mcp/personal/config", headers={"test-user": "bob"}, json={"server_name": "github", "enabled": False}).status_code == 200 + with pytest.raises(ToolException, match="changed, disabled or removed"): + await bob_tool.ainvoke({}) + assert len(received) == 2 + finally: + reset_current_user(identity) + + +@pytest.mark.asyncio +async def test_personal_tool_guard_reuses_validation_and_rejects_file_replacement(personal_client, monkeypatch): + import os + + from langchain_core.tools import StructuredTool + + import deerflow.mcp.user_config as user_config + from deerflow.config.extensions_config import atomic_write_extensions_config + from deerflow.mcp.user_tools import _guard + + assert create(personal_client, "alice").status_code == 200 + old_name = next(iter(load_user_mcp_config("alice").mcp_servers)) + load = user_config.load_user_mcp_config + reads = 0 + + def counted_load(user_id): + nonlocal reads + reads += 1 + return load(user_id) + + monkeypatch.setattr(user_config, "load_user_mcp_config", counted_load) + calls = [] + + async def invoke(): + calls.append(True) + return "ok" + + tool = _guard(StructuredTool.from_function(coroutine=invoke, name="personal_test", description="Personal test"), "alice", old_name) + identity = set_current_user(SimpleNamespace(id="alice")) + try: + assert await tool.ainvoke({}) == "ok" + assert await tool.ainvoke({}) == "ok" + assert reads == 1 + + path = user_mcp_config_path("alice") + before = path.stat() + raw = read_user_mcp_config("alice") + raw["mcpServers"]["github"]["headers"]["Authorization"] = "Bearer ALICE" + atomic_write_extensions_config(path, raw) + os.utime(path, ns=(before.st_atime_ns, before.st_mtime_ns)) + after = path.stat() + assert (after.st_size, after.st_mtime_ns) == (before.st_size, before.st_mtime_ns) + assert after.st_ino != before.st_ino + with pytest.raises(ToolException, match="changed, disabled or removed"): + await tool.ainvoke({}) + assert reads == 2 + assert len(calls) == 2 + finally: + reset_current_user(identity) + + +@pytest.mark.asyncio +async def test_background_calls_resolve_only_persisted_task_owner(personal_client, monkeypatch): + import deerflow.mcp.user_config as user_config + from deerflow.mcp.task_tool_caller import McpTaskToolCaller + + create(personal_client, "alice") + name = next(iter(load_user_mcp_config("alice").mcp_servers)) + received = [] + loaded = [] + load = user_config.load_user_mcp_config + + def counted_load(user_id): + loaded.append(user_id) + return load(user_id) + + monkeypatch.setattr(user_config, "load_user_mcp_config", counted_load) + + async def invoke(self, **kwargs): + received.append(self._extensions_config.mcp_servers[kwargs["server_name"]].headers["Authorization"]) + + monkeypatch.setattr(McpTaskToolCaller, "_call_configured_tool", invoke) + caller = McpTaskToolCaller(ExtensionsConfig()) + await caller.call_tool(server_name=name, tool_name="status", arguments={}, user_id="alice", thread_id="thread", connection_scope="personal") + first_caller = caller._personal_callers["alice"][1] + await caller.call_tool(server_name=name, tool_name="status", arguments={}, user_id="alice", thread_id="thread", connection_scope="personal") + assert caller._personal_callers["alice"][1] is first_caller + assert loaded == ["alice"] + with pytest.raises(LookupError, match="Personal MCP"): + await caller.call_tool(server_name=name, tool_name="status", arguments={}, user_id="bob", thread_id="thread", connection_scope="personal") + assert received == ["Bearer alice", "Bearer alice"] + + # The same deployment name must not steal an existing personal task after + # the Gateway restarts with that deployment entry in its startup snapshot. + deployment = McpTaskToolCaller(ExtensionsConfig.model_validate({"mcpServers": {name: {"type": "http", "url": "https://example.com/platform", "headers": {"Authorization": "platform"}}}})) + await deployment.call_tool(server_name=name, tool_name="status", arguments={}, user_id="alice", thread_id="thread", connection_scope="personal") + await deployment.call_tool(server_name=name, tool_name="status", arguments={}, user_id="bob", thread_id="thread") + assert received == ["Bearer alice", "Bearer alice", "Bearer alice", "platform"] + assert personal_client.patch("/api/mcp/personal/config", headers={"test-user": "alice"}, json={"server_name": "github", "enabled": False}).status_code == 200 + with pytest.raises(LookupError, match="Personal MCP"): + await deployment.call_tool(server_name=name, tool_name="status", arguments={}, user_id="alice", thread_id="thread", connection_scope="personal") + + +@pytest.mark.asyncio +async def test_background_caller_rebuilds_after_personal_credential_edit(personal_client, monkeypatch): + from deerflow.config.extensions_config import atomic_write_extensions_config + from deerflow.mcp.task_tool_caller import McpTaskToolCaller + + assert create(personal_client, "alice").status_code == 200 + old_name = next(iter(load_user_mcp_config("alice").mcp_servers)) + + async def credential(self, **kwargs): + return self._extensions_config.mcp_servers[kwargs["server_name"]].headers["Authorization"] + + monkeypatch.setattr(McpTaskToolCaller, "_call_configured_tool", credential) + caller = McpTaskToolCaller(ExtensionsConfig()) + request = {"tool_name": "status", "arguments": {}, "user_id": "alice", "thread_id": "thread", "connection_scope": "personal"} + assert await caller.call_tool(server_name=old_name, **request) == "Bearer alice" + previous = caller._personal_callers["alice"][1] + + path = user_mcp_config_path("alice") + raw = read_user_mcp_config("alice") + raw["mcpServers"]["github"]["headers"]["Authorization"] = "Bearer ALICE" + atomic_write_extensions_config(path, raw) + with pytest.raises(LookupError, match="Personal MCP"): + await caller.call_tool(server_name=old_name, **request) + new_name = next(iter(load_user_mcp_config("alice").mcp_servers)) + assert await caller.call_tool(server_name=new_name, **request) == "Bearer ALICE" + assert caller._personal_callers["alice"][1] is not previous + path.unlink() + with pytest.raises(LookupError, match="Personal MCP"): + await caller.call_tool(server_name=new_name, **request) + + +@pytest.mark.asyncio +async def test_background_caller_keeps_only_recent_owners(personal_client, monkeypatch): + import deerflow.mcp.task_tool_caller as task_tool_caller + + monkeypatch.setattr(task_tool_caller, "_MAX_PERSONAL_CALLERS", 2) + + async def credential(self, **kwargs): + return self._extensions_config.mcp_servers[kwargs["server_name"]].headers["Authorization"] + + monkeypatch.setattr(task_tool_caller.McpTaskToolCaller, "_call_configured_tool", credential) + caller = task_tool_caller.McpTaskToolCaller(ExtensionsConfig()) + names = {} + for user_id in ("alice", "bob", "carol"): + assert create(personal_client, user_id).status_code == 200 + names[user_id] = next(iter(load_user_mcp_config(user_id).mcp_servers)) + assert await caller.call_tool(server_name=names[user_id], tool_name="status", arguments={}, user_id=user_id, thread_id="thread", connection_scope="personal") == f"Bearer {user_id}" + assert list(caller._personal_callers) == ["bob", "carol"] + assert await caller.call_tool(server_name=names["alice"], tool_name="status", arguments={}, user_id="alice", thread_id="thread", connection_scope="personal") == "Bearer alice" + assert list(caller._personal_callers) == ["carol", "alice"] + + +@pytest.mark.asyncio +async def test_gateway_registers_driver_for_personal_only_task_toolsets(personal_client): + from langchain_core.tools import StructuredTool + + from app.gateway.app import lifespan + from deerflow.config.extensions_config import atomic_write_extensions_config + from deerflow.config.mcp_tasks_config import McpTasksConfig + from deerflow.mcp.tasks import ORDINARY_MCP_TASK_DRIVER + from deerflow.mcp.tools import get_mcp_tools + + path = user_mcp_config_path("alice") + atomic_write_extensions_config( + path, + {"mcpServers": {"reports": {"type": "http", "url": "https://example.com/mcp", "task_toolsets": [{"name": "reports", "submit_tool": "submit_report", "status_tool": "status_report", "cancel_tool": "cancel_report"}]}}}, + ) + personal = load_user_mcp_config("alice") + server_name = next(iter(personal.mcp_servers)) + deployment = ExtensionsConfig() + startup = SimpleNamespace(log_level="INFO", memory=SimpleNamespace(enabled=False, token_counting="char", shutdown_flush_timeout_seconds=5.0), mcp_tasks=McpTasksConfig(enabled=True)) + app = FastAPI() + + @asynccontextmanager + async def runtime(gateway, _config): + gateway.state.mcp_task_repo = object() + yield + + class FakeClient: + def __init__(self, _servers, *, tool_interceptors, **_kwargs): + self.tool_interceptors = tool_interceptors + self.callbacks = None + + async def get_tools(self, *, server_name): + async def call(topic: str) -> str: + return topic + + return [StructuredTool.from_function(coroutine=call, name=f"{server_name}_{name}", description=name) for name in ("submit_report", "status_report", "cancel_report")] + + channel = MagicMock() + channel.get_status.return_value = {} + with ( + patch("app.gateway.app.get_app_config", return_value=startup), + patch("app.gateway.app.get_gateway_config", return_value=MagicMock(host="x", port=0)), + patch("app.gateway.app.langgraph_runtime", runtime), + patch("app.gateway.app.auth.close_oidc_service", AsyncMock()), + patch("app.channels.service.start_channel_service", AsyncMock(return_value=channel)), + patch("app.channels.service.stop_channel_service", AsyncMock()), + patch("deerflow.skills.projection.ensure_public_skill_projection"), + patch("deerflow.agents.memory.get_memory_manager", return_value=MagicMock()), + patch("deerflow.config.extensions_config.ExtensionsConfig.from_file", return_value=deployment), + patch("app.mcp_tasks.McpTaskService.start", AsyncMock()), + patch("app.mcp_tasks.McpTaskService.stop", AsyncMock()), + patch("langchain_mcp_adapters.client.MultiServerMCPClient", FakeClient), + ): + async with lifespan(app): + assert not deployment.mcp_servers + assert app.state.mcp_task_service.drivers.get(ORDINARY_MCP_TASK_DRIVER) is not None + tools = await get_mcp_tools(personal, personal_user_id="alice") + assert [tool.name for tool in tools] == [f"{server_name}_submit_report"] + app.state.mcp_task_service.submit = AsyncMock(return_value={"id": "local-1", "status": "submitted"}) + identity = set_current_user(SimpleNamespace(id="alice")) + try: + result = await tools[0].coroutine( + runtime=SimpleNamespace(context={"thread_id": "thread-1", "thread_incarnation": "incarnation-1", "run_id": "run-1"}, config={}, tool_call_id="call-1"), + topic="MCP", + ) + finally: + reset_current_user(identity) + assert result["task_id"] == "local-1" + submitted = app.state.mcp_task_service.submit.await_args.kwargs + assert submitted["driver_name"] == ORDINARY_MCP_TASK_DRIVER + assert submitted["request"].driver_data["connection_scope"] == "personal" diff --git a/backend/tests/test_personal_mcp_access.py b/backend/tests/test_personal_mcp_access.py new file mode 100644 index 0000000000..0bfba74d13 --- /dev/null +++ b/backend/tests/test_personal_mcp_access.py @@ -0,0 +1,172 @@ +"""Privilege revocation uses current accounts even with stale tools and caches.""" + +import asyncio +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import pytest +import pytest_asyncio +from langchain_core.tools import StructuredTool, ToolException +from sqlalchemy import delete +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine + +from app.gateway import personal_mcp_access as gateway_access +from app.gateway.auth.local_provider import LocalAuthProvider +from app.gateway.auth.models import User +from app.gateway.auth.repositories.sqlite import SQLiteUserRepository +from deerflow.config.extensions_config import ExtensionsConfig, McpServerConfig, atomic_write_extensions_config +from deerflow.config.paths import Paths +from deerflow.mcp import personal_access +from deerflow.mcp.task_tool_caller import McpTaskToolCaller +from deerflow.mcp.user_config import load_user_mcp_config, user_mcp_config_path +from deerflow.mcp.user_tools import _guard, _load +from deerflow.persistence.user.model import UserRow +from deerflow.runtime.user_context import reset_current_user, set_current_user + + +@pytest_asyncio.fixture +async def accounts(tmp_path, monkeypatch): + engine = create_async_engine(f"sqlite+aiosqlite:///{tmp_path}/users.db") + async with engine.begin() as connection: + await connection.run_sync(UserRow.__table__.create) + sessions = async_sessionmaker(engine, expire_on_commit=False) + repository = SQLiteUserRepository(sessions) + user = await repository.create_user(User(email="admin@example.com", system_role="admin")) + monkeypatch.setattr(gateway_access, "get_local_provider", lambda: LocalAuthProvider(repository)) + monkeypatch.delenv("DEER_FLOW_AUTH_DISABLED", raising=False) + monkeypatch.setattr("deerflow.mcp.user_config.get_paths", lambda: Paths(base_dir=tmp_path)) + monkeypatch.setattr(personal_access, "_admin_checker", None) + try: + yield repository, user, sessions + finally: + await engine.dispose() + + +def privileged_definition(kind): + if kind == "stdio": + return {"type": "stdio", "command": "npx", "args": ["example-package"], "personal_public_network": False} + definition = {"type": "sse" if kind == "sse" else "http", "url": "http://127.0.0.1/mcp", "personal_public_network": False} + if kind == "legacy": + del definition["personal_public_network"] + if kind == "oauth": + definition["oauth"] = {"token_url": "http://127.0.0.1/token", "client_id": "test-client"} + return definition + + +@pytest.mark.asyncio +@pytest.mark.parametrize("kind", ["http", "sse", "stdio", "legacy", "oauth"]) +@pytest.mark.parametrize("revocation", ["demote", "delete", "lookup_failure"]) +async def test_revocation_blocks_discovery_existing_tools_and_cached_durable_calls(accounts, monkeypatch, kind, revocation): + repository, user, sessions = accounts + owner = str(user.id) + path = user_mcp_config_path(owner) + atomic_write_extensions_config(path, {"mcpServers": {"privileged": privileged_definition(kind), "public": {"type": "http", "url": "https://example.com/mcp", "personal_public_network": True}}}) + before = path.read_bytes() + config = load_user_mcp_config(owner) + privileged = next(name for name, server in config.mcp_servers.items() if personal_access.requires_admin(server)) + public = next(name for name in config.mcp_servers if name != privileged) + observed = [] + + async def execute(): + observed.append("executed") + return "ok" + + class Client: + def __init__(self, connections, **kwargs): + observed.extend(connections) + + async def get_tools(self, server_name): + return [StructuredTool.from_function(coroutine=execute, name=server_name + "_probe", description="Probe")] + + monkeypatch.setattr("langchain_mcp_adapters.client.MultiServerMCPClient", Client) + oauth = AsyncMock(return_value={}) + monkeypatch.setattr("deerflow.mcp.tools.get_initial_oauth_headers", oauth) + invoke = AsyncMock(return_value="ok") + monkeypatch.setattr(McpTaskToolCaller, "_call_configured_tool", invoke) + caller = McpTaskToolCaller(ExtensionsConfig()) + guarded = _guard(StructuredTool.from_function(coroutine=execute, name="probe", description="Probe"), owner, privileged) + public_tool = _guard(StructuredTool.from_function(coroutine=execute, name="public", description="Public"), owner, public) + token = set_current_user(SimpleNamespace(id=owner, system_role="admin")) + try: + async with gateway_access.personal_mcp_authority(): + assert len(await _load(owner, config)) == 2 + assert await guarded.ainvoke({}) == "ok" + call = dict(server_name=privileged, tool_name="submit", arguments={}, user_id=owner, thread_id="thread", connection_scope="personal") + assert await caller.call_tool(**call) == "ok" + cached_caller = caller._personal_callers[owner][1] + observed.clear() + invoke.reset_mock() + oauth.reset_mock() + + if revocation == "demote": + user.system_role = "user" + await repository.update_user(user) + elif revocation == "delete": + async with sessions() as session: + await session.execute(delete(UserRow).where(UserRow.id == owner)) + await session.commit() + else: + monkeypatch.setattr(repository, "get_user_by_id", AsyncMock(side_effect=RuntimeError("database unavailable"))) + + tools = await _load(owner, config) + assert len(tools) == 1 + assert observed == [public] + assert set(oauth.call_args.args[0].mcp_servers) == {public} + with pytest.raises(ToolException, match="Current administrator"): + await guarded.ainvoke({}) + for operation in ("submit", "status", "cancel"): + with pytest.raises(ToolException, match="Current administrator"): + await caller.call_tool(**(call | {"tool_name": operation, "request_scoped_headers": operation == "submit"})) + assert caller._personal_callers[owner][1] is cached_caller + invoke.assert_not_awaited() + assert path.read_bytes() == before + assert await public_tool.ainvoke({}) == "ok" + # Deployment calls retain their existing policy and never query + # personal ownership or the owner's current role. + assert await caller.call_tool(**(call | {"connection_scope": "deployment"})) == "ok" + finally: + reset_current_user(token) + + +@pytest.mark.asyncio +async def test_authority_lookup_stays_on_gateway_loop_during_sync_discovery(accounts, monkeypatch): + repository, user, _ = accounts + owner = str(user.id) + loop = asyncio.get_running_loop() + original = repository.get_user_by_id + reads = [] + + async def get_user(user_id): + reads.append(asyncio.get_running_loop()) + return await original(user_id) + + monkeypatch.setattr(repository, "get_user_by_id", get_user) + server = McpServerConfig.model_validate(privileged_definition("http")) + async with gateway_access.personal_mcp_authority(): + await asyncio.wait_for(asyncio.to_thread(lambda: asyncio.run(personal_access.require_personal_mcp_access(owner, server))), timeout=5) + assert reads == [loop] + assert personal_access._admin_checker is None + with pytest.raises(ToolException, match="Current administrator"): + await personal_access.require_personal_mcp_access(owner, server) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("production", [False, True]) +async def test_auth_disabled_authority_only_grants_the_default_local_user(accounts, monkeypatch, production): + monkeypatch.setenv("DEER_FLOW_AUTH_DISABLED", "1") + monkeypatch.setenv("DEER_FLOW_ENV", "production" if production else "development") + monkeypatch.delenv("ENVIRONMENT", raising=False) + async with gateway_access.personal_mcp_authority(): + assert await personal_access._is_current_admin("default") is (not production) + assert await personal_access._is_current_admin("missing-user") is False + + +@pytest.mark.asyncio +async def test_authority_registration_is_restored_after_startup_failure(monkeypatch): + previous = AsyncMock(return_value=False) + monkeypatch.setattr(personal_access, "_admin_checker", previous) + with pytest.raises(RuntimeError, match="startup failed"): + async with gateway_access.personal_mcp_authority(): + assert personal_access._admin_checker is not previous + raise RuntimeError("startup failed") + assert personal_access._admin_checker is previous diff --git a/backend/tests/test_personal_mcp_network.py b/backend/tests/test_personal_mcp_network.py new file mode 100644 index 0000000000..dc83ba8a05 --- /dev/null +++ b/backend/tests/test_personal_mcp_network.py @@ -0,0 +1,154 @@ +"""Exercise personal MCP policy through the real HTTP transport and TLS boundary.""" + +import ipaddress +import socket + +import httpcore +import pytest +from httpcore._backends.anyio import AnyIOBackend + +from deerflow.mcp.personal_network import personal_httpx_client_factory + + +class RecordingStream(httpcore.AsyncNetworkStream): + def __init__(self): + self.writes = [] + self.tls_hosts = [] + self.closed = False + + async def read(self, max_bytes, timeout=None): + return b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok" + + async def write(self, buffer, timeout=None): + self.writes.append(buffer) + + async def aclose(self): + self.closed = True + + async def start_tls(self, ssl_context, server_hostname=None, timeout=None): + assert ssl_context.check_hostname + self.tls_hosts.append(server_hostname) + return self + + +@pytest.mark.asyncio +@pytest.mark.parametrize("scheme", ["http", "https"]) +async def test_personal_transport_pins_dns_and_preserves_authority(monkeypatch, scheme): + lookups = [] + connections = [] + streams = [] + real_resolve = socket.getaddrinfo + + def resolve(host, port, *args, **kwargs): + if host == "mcp.example": + address = "8.8.8.8" if not lookups else "127.0.0.1" + lookups.append(address) + return real_resolve(address, port, *args, **kwargs) + return real_resolve(host, port, *args, **kwargs) + + async def connect(self, host, port, **kwargs): + addresses = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM) + connections.append((host, addresses[0][4][0])) + stream = RecordingStream() + streams.append(stream) + return stream + + monkeypatch.setattr(socket, "getaddrinfo", resolve) + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory(headers={"Authorization": "Bearer test"}) as client: + response = await client.post(f"{scheme}://mcp.example:8443/mcp?session=1", content=b"payload") + assert response.text == "ok" + assert str(response.request.url) == f"{scheme}://mcp.example:8443/mcp?session=1" + assert connections == [("8.8.8.8", "8.8.8.8")] + assert lookups == ["8.8.8.8"] + wire = b"".join(streams[0].writes) + assert b"POST /mcp?session=1 HTTP/1.1" in wire + assert b"Host: mcp.example:8443" in wire + assert b"Authorization: Bearer test" in wire + assert b"payload" in wire + assert streams[0].tls_hosts == (["mcp.example"] if scheme == "https" else []) + with pytest.raises(ValueError, match="public HTTP"): + await client.get(f"{scheme}://mcp.example:8443/mcp") + assert len(connections) == 1 + assert all(stream.closed for stream in streams) + + +@pytest.mark.asyncio +async def test_personal_transport_does_not_share_tls_across_hostnames(monkeypatch): + streams = [] + real_resolve = socket.getaddrinfo + monkeypatch.setattr(socket, "getaddrinfo", lambda host, port, *args, **kwargs: real_resolve("8.8.8.8", port, *args, **kwargs)) + + async def connect(self, host, port, **kwargs): + stream = RecordingStream() + streams.append(stream) + return stream + + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory() as client: + for host in ("first.example", "second.example"): + assert (await client.get(f"https://{host}/mcp")).status_code == 200 + assert [stream.tls_hosts for stream in streams] == [["first.example"], ["second.example"]] + assert all(stream.closed for stream in streams) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("address", ["127.0.0.1", "10.0.0.1", "169.254.169.254", "::1"]) +async def test_personal_transport_blocks_private_addresses_before_connect(monkeypatch, address): + real_resolve = socket.getaddrinfo + monkeypatch.setattr(socket, "getaddrinfo", lambda host, port, *args, **kwargs: real_resolve(address, port, *args, **kwargs)) + + async def connect(*args, **kwargs): + pytest.fail("Private address reached TCP connect") + + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory() as client: + with pytest.raises(ValueError, match="public HTTP"): + await client.get("https://mcp.example/mcp") + + +@pytest.mark.asyncio +@pytest.mark.parametrize("addresses", [[], ["8.8.8.8", "127.0.0.1"], ["2606:4700:4700::1111", "::1"]]) +async def test_personal_transport_rejects_empty_or_mixed_dns_answers(monkeypatch, addresses): + monkeypatch.setattr("deerflow.mcp.personal_network.resolve_host_addresses", lambda host: [ipaddress.ip_address(address) for address in addresses]) + + async def connect(*args, **kwargs): + pytest.fail("Rejected DNS answers reached TCP connect") + + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory() as client: + with pytest.raises(ValueError, match="public HTTP"): + await client.get("https://mcp.example/mcp") + + +@pytest.mark.asyncio +async def test_personal_transport_tries_only_vetted_addresses(monkeypatch): + addresses = ["2606:4700:4700::1111", "8.8.8.8"] + monkeypatch.setattr("deerflow.mcp.personal_network.resolve_host_addresses", lambda host: [ipaddress.ip_address(address) for address in addresses]) + attempts = [] + + async def connect(self, host, port, **kwargs): + attempts.append(host) + if host == addresses[0]: + raise httpcore.ConnectError("IPv6 unavailable") + return RecordingStream() + + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory() as client: + assert (await client.get("https://mcp.example/mcp")).status_code == 200 + assert attempts == addresses + + +@pytest.mark.asyncio +@pytest.mark.parametrize("address", ["8.8.8.8", "[2606:4700:4700::1111]"]) +async def test_personal_transport_accepts_public_literal_ips(monkeypatch, address): + attempts = [] + + async def connect(self, host, port, **kwargs): + attempts.append(host) + return RecordingStream() + + monkeypatch.setattr(AnyIOBackend, "connect_tcp", connect) + async with personal_httpx_client_factory() as client: + assert (await client.get(f"http://{address}/mcp")).status_code == 200 + assert attempts == [address.strip("[]")] diff --git a/docs/capability-center.md b/docs/capability-center.md index 476c11e5ab..d92b3ab8bf 100644 --- a/docs/capability-center.md +++ b/docs/capability-center.md @@ -20,7 +20,8 @@ deferred to a separate proposal and PR. | Validated manifest schema | `deerflow.capabilities.catalog.PluginManifest` | | Installation/status adapters | `backend/app/gateway/capabilities.py` | | Catalog and safe discovery HTTP API | `backend/app/gateway/routers/capabilities.py` | -| MCP settings, secrets, enable/delete, cache reload | Existing `/api/mcp/config` services and `extensions_config.json` | +| Personal MCP settings, secrets, enable/delete | `/api/mcp/personal/config` and `.deer-flow/users//integrations/mcp.json` | +| Deployment MCP settings and cache reload | Administrator-only `/api/mcp/config` and `extensions_config.json` | | Lark installation and personal account authorization | Existing `/api/integrations/lark` services | | Skill archives, enable state, user storage | Existing `/api/skills` services | | Agent selection | Agent config `mcp_plugins` and existing `skills` | @@ -35,7 +36,7 @@ loader is separate from this user-facing directory. Three entries ship working API clients with the harness, using the `business` configuration adapter and the existing stdio MCP runtime. No separate service, package download, or Dify runtime is needed. Configure them under **Capability -Center → Plugins** as an administrator: +Center → Plugins** using your own account: | Plugin | Configuration | Tools | | --- | --- | --- | @@ -51,10 +52,15 @@ DeerFlow's incoming IM channels. Existing manually configured CLI connections are not rewritten when the catalog entry changes. Configuration saves credentials without sending a message or creating a CRM -record. These deployment credentials are shared by runs allowed to use the -configured MCP server; they are not personal OAuth connections. Credentials -remain in the existing MCP `env` configuration and its masked admin editor. -Edit, toggle and delete the configured entry using the existing MCP controls. +record. Connections created in the web interface are personal: credentials, +enabled state and edits belong to the signed-in user, including administrators. +They are stored under `.deer-flow/users//integrations/mcp.json` and +remain masked in the editor. Existing deployment connections in +`extensions_config.json` stay shared and are not copied to any personal account. +The page separates **Platform provided** (deployment setup guides and shared MCP +entries) from **My plugins** (personal connections, configuration templates and +Lark account authorization). Existing search and category filters apply to both. +Edit, toggle and delete personal entries using the existing MCP controls. An Agent selects these connections through **Plugins and skills**, just like other MCP servers. New tool selection applies on the next run. @@ -118,7 +124,7 @@ only link to setup instructions; listing them never claims they are installed. The catalog version describes this manifest, not a remotely detected server version. A catalog listing does not install, enable, or authorize anything. -The first MCP form supports HTTP endpoints and a deployment Authorization header. +The MCP form saves a personal HTTP endpoint and Authorization header. The advanced JSON editor retains stdio, SSE, OAuth token configuration, and per-user credential mappings supported by the existing MCP runtime. The presence of `oauth` in a manifest is not a new interactive OAuth implementation. Lark @@ -134,8 +140,11 @@ service's authorization and validation; never add a provider branch to the galle - `GET /api/capabilities/catalog`: validated bundled manifests. - `GET /api/capabilities/installations/{adapter}`: safe installation projections for `mcp`, `business`, `lark`, and `skills`; separate requests isolate integration failures. -- `POST /api/capabilities/installations`: administrator installation dispatch. - Body: `plugin_id`, `name`, and adapter `configuration`. MCP configuration uses +- `POST /api/capabilities/installations`: installation dispatch with `plugin_id`, + `name`, adapter `configuration`, and optional `scope` (default `deployment`). + Deployment scope requires an administrator; authenticated users may choose + `scope: "user"` for MCP and bundled business connections. Other adapters + still require an administrator. MCP configuration uses the existing server definition schema. HTTP/SSE connections require a valid HTTP(S) URL without embedded credentials; stdio connections require a command. Invalid transport configuration returns 422 before saving. Duplicate server @@ -204,3 +213,42 @@ Agent persistence, delegated selection, and a real local stdio MCP invocation. Browser tests cover catalog installation, Agent selection save/reopen, existing icon editing, filtering, and desktop/mobile settings layouts. Browser fixture screenshots show sample integrations; they are not production default settings. + +## Personal and deployment MCP configuration + +`config.yaml` tools and existing `extensions_config.json` MCP servers remain +platform-provided capabilities. The administrator-only `/api/mcp/config` API +continues to manage deployment MCP configuration. + +The web editor uses `/api/mcp/personal/config`: GET reads only the authenticated +user's definitions; POST `/servers`, PUT `/server`, PATCH and DELETE +`/servers/{name}` manage that same user's file. No caller-supplied user ID selects +the owner. Catalog installation accepts `scope: "user"`; catalog discovery +accepts `scope=user` or `scope=all` (deployment plus the caller's connections). +The default API scope remains deployment for existing integrations. + +Personal files use the existing atomic file writer and cross-process lock. Keep +`DEER_FLOW_HOME` on persistent storage; multiple Gateway workers must share it. +Personal values are literal and cannot read deployment environment variables. +A missing personal connection never falls back to a same-named shared one. +Personal tools are discovered per run, outside the process-wide platform cache. +Calls check ownership and the current stored revision. Credential edits, disable +and delete invalidate already assembled personal tools; restart the run after +an edit. Durable tasks bind the same owner and connection revision, including +after recovery; changing that connection blocks its pending remote calls until +the original configuration is restored. + +Allowing personal configuration does not grant host execution privileges: +ordinary users may configure public HTTP/SSE endpoints and the fixed bundled +business launchers. Arbitrary stdio commands, internal endpoints and OAuth token +endpoints still require an administrator. Public endpoints are checked again on +HTTP requests, with redirects and environment proxies disabled. + +Connections saved with administrator privileges require the owner's current +administrator role during discovery and before every tool call, including +durable submit, poll and cancel calls. Demotion, account deletion or an authority +lookup failure blocks those connections even if tools or callers are cached. +The owner can save a compatible connection again under ordinary-user policy. +Gateway workers query current account records; standalone hosts must install +an authority lookup to use privileged personal definitions. Already admitted +operations are not forcibly terminated by a subsequent role change. diff --git a/docs/pr-evidence/connection-ownership-capability-center.png b/docs/pr-evidence/connection-ownership-capability-center.png new file mode 100644 index 0000000000000000000000000000000000000000..b9f08201ec42f6c77c4fef6010b5e8c7798f32b1 GIT binary patch literal 262503 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ⅇ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 F`+pi8gJ=K% literal 0 HcmV?d00001 diff --git a/frontend/src/AGENTS.md b/frontend/src/AGENTS.md index 2ec508b0a2..443ebc35d4 100644 --- a/frontend/src/AGENTS.md +++ b/frontend/src/AGENTS.md @@ -39,13 +39,14 @@ against raw artifact paths before encoding. pages; `resolveThreadContext` must neither enqueue account writes nor create a fallback thread override that masks a later server preference. The - Capability Center > Plugins MCP switch calls the targeted `PATCH /api/mcp/config` + Capability Center > My plugins MCP switch calls the targeted `PATCH /api/mcp/personal/config` mutation, disables switches until that mutation's success refetch completes, displays the backend error `detail` through a toast, and invalidates `["mcpConfig"]` only after success. - Server management uses targeted `POST /api/mcp/config/servers`, - `PUT /api/mcp/config/server`, and bodyless - `DELETE /api/mcp/config/servers/{server_name}` mutations. Delete names are + Personal server management uses targeted `POST /api/mcp/personal/config/servers`, + `PUT /api/mcp/personal/config/server`, and bodyless + `DELETE /api/mcp/personal/config/servers/{server_name}` mutations. The deployment + `/api/mcp/config` endpoints remain operator-only and are not used by this editor. Delete names are percent-encoded, including legacy empty and slash-containing names; every successful mutation invalidates `["mcpConfig"]` only after the response. Current-chat MCP background tasks use `core/background-tasks`: the header @@ -196,3 +197,5 @@ are sent on save. Custom-agent chat derives its initial selection from the saved binding; explicit page-local overrides survive new-thread route replacement and reset on conversation changes. Gateway supplies defaults for clients without a selector; frontend visibility must not become a runtime enforcement boundary. + +MCP editor requests use the personal configuration API. Query keys for personal MCP config and capability installations include the authenticated user ID; remount the editor when that ID changes. Deployment installations remain read-only under Platform provided, alongside deployment setup guides. My plugins contains personal configuration templates, Lark account authorization and the personal MCP editor; a deployment installation must not mark a personal template configured. diff --git a/frontend/src/components/workspace/capabilities/plugin-directory.tsx b/frontend/src/components/workspace/capabilities/plugin-directory.tsx index d5c04bce13..6a419db06c 100644 --- a/frontend/src/components/workspace/capabilities/plugin-directory.tsx +++ b/frontend/src/components/workspace/capabilities/plugin-directory.tsx @@ -1,6 +1,6 @@ "use client"; -import { type ReactNode } from "react"; +import { type ReactNode, useId } from "react"; import { useI18n } from "@/core/i18n/hooks"; @@ -77,6 +77,7 @@ export function PluginDirectory({ }) { const { t } = useI18n(); const copy = t.capabilities.directory; + const groupId = useId(); const visible = entries.filter( (entry) => (!installedOnly || entry.installed) && @@ -98,12 +99,9 @@ export function PluginDirectory({ const items = visible.filter((entry) => entry.category === key); if (!items.length) return null; return ( -
+
-

+

{copy.categories[key]}

diff --git a/frontend/src/components/workspace/capabilities/plugin-gallery.tsx b/frontend/src/components/workspace/capabilities/plugin-gallery.tsx index 1e0479ce2f..1fbd58a40c 100644 --- a/frontend/src/components/workspace/capabilities/plugin-gallery.tsx +++ b/frontend/src/components/workspace/capabilities/plugin-gallery.tsx @@ -76,7 +76,7 @@ export function PluginGallery({ query }: { query: string }) { const copy = t.capabilities.directory; const labels = capabilityCopy(locale); const { user } = useAuth(); - const canManage = user?.system_role === "admin" && !isStaticWebsiteOnly(); + const canManage = !!user && !isStaticWebsiteOnly(); const directory = useCapabilityCatalog(); const definitions = directory.data ?? []; const adapterNames = [ @@ -86,7 +86,11 @@ export function PluginGallery({ query }: { query: string }) { .filter((name) => name !== "guide"), ), ]; - const states = useQueries({ queries: adapterNames.map(installationQuery) }); + const states = useQueries({ + queries: adapterNames.map((adapter) => + installationQuery(adapter, user?.id), + ), + }); const client = useQueryClient(); const [filter, setFilter] = useState("all"); const [category, setCategory] = useState("all"); @@ -113,7 +117,9 @@ export function PluginGallery({ query }: { query: string }) { .map((item) => [item.id, item]), ).values(), ]; - const catalog: PluginDirectoryEntry[] = definitions + const platformCatalog: PluginDirectoryEntry[] = []; + const personalCatalog: PluginDirectoryEntry[] = []; + definitions .filter( (plugin) => canManage || @@ -121,12 +127,16 @@ export function PluginGallery({ query }: { query: string }) { (item) => item.adapter === "mcp" && item.plugin_id === plugin.id, ), ) - .map((plugin) => { + .forEach((plugin) => { + const isPlatform = plugin.adapter === "guide"; const status = installations.find( - (item) => item.plugin_id === plugin.id && item.installed, + (item) => + item.plugin_id === plugin.id && + item.installed && + (isPlatform ? item.scope !== "user" : item.scope === "user"), ); const unavailable = states[adapterNames.indexOf(plugin.adapter)]?.isError; - return { + (isPlatform ? platformCatalog : personalCatalog).push({ id: plugin.id, category: plugin.category, installed: !!status, @@ -163,50 +173,49 @@ export function PluginGallery({ query }: { query: string }) { ), - }; - }); - // Non-admins see safe installation projections, not the administrator's raw config editor. - if (!canManage) - for (const item of installations.filter((item) => item.adapter === "mcp")) { - const manifest = definitions.find( - (plugin) => plugin.id === item.plugin_id, - ); - catalog.push({ - id: item.id, - category: manifest?.category ?? "custom", - search: `${item.name} ${item.description}`, - installed: true, - node: ( - - } - label={ - item.selectable === false - ? labels.unavailable - : item.enabled - ? t.capabilities.enabled - : t.capabilities.disabled - } - > - - {item.auth_status === "required" - ? labels.required - : item.auth_status === "configured" - ? labels.configured - : labels.unknown} - - - ), }); - } + }); + // Deployment entries remain read-only; the editor manages personal connections. + for (const item of installations.filter( + (item) => item.adapter === "mcp" && item.scope !== "user", + )) { + const manifest = definitions.find((plugin) => plugin.id === item.plugin_id); + platformCatalog.push({ + id: item.id, + category: manifest?.category ?? "custom", + search: `${item.name} ${item.description}`, + installed: true, + node: ( + + } + label={ + item.selectable === false + ? labels.unavailable + : item.enabled + ? t.capabilities.enabled + : t.capabilities.disabled + } + > + + {item.auth_status === "required" + ? labels.required + : item.auth_status === "configured" + ? labels.configured + : labels.unknown} + + + ), + }); + } const toolbar = ( @@ -250,26 +259,69 @@ export function PluginGallery({ query }: { query: string }) {
{directory.isError &&

{labels.catalogError}

} {directory.isLoading &&

{t.common.loading}

} - {canManage ? ( - +
+

+ {labels.platformTitle} +

+

{labels.platformHint}

+
+ - ) : ( - <> - {toolbar} - +
+ {canManage ? ( + +

+ {labels.personalTitle} +

+

+ {labels.personalHint} +

+ + } category={category} installedOnly={filter === "installed"} - entries={catalog} + catalog={personalCatalog} + definitions={definitions} /> - - )} + ) : ( + <> +
+

+ {labels.personalTitle} +

+

+ {labels.personalHint} +

+
+ + + )} +
!value && close()}> {selected && ( @@ -287,10 +339,14 @@ export function PluginGallery({ query }: { query: string }) { {Settings ? ( ) : (

diff --git a/frontend/src/core/capabilities/copy.ts b/frontend/src/core/capabilities/copy.ts index 382f776339..3c984ad549 100644 --- a/frontend/src/core/capabilities/copy.ts +++ b/frontend/src/core/capabilities/copy.ts @@ -1,4 +1,8 @@ const en = { + platformTitle: "Platform provided", + platformHint: "Shared capabilities managed through deployment configuration.", + personalTitle: "My plugins", + personalHint: "Your connections and settings are only used by your account.", install: "Configure plugin", name: "Connection name", url: "Server URL", @@ -8,7 +12,7 @@ const en = { webhook_key: "Robot webhook key (the key parameter in its URL)", save: "Save configuration", accountHint: - "These are deployment credentials managed by the administrator. Personal authorization, when supported, uses the integration's account flow.", + "This configuration is saved to your account and is only used by your runs.", saved: "Configuration saved", unknown: "Not checked", required: "Account required", @@ -31,6 +35,10 @@ const en = { invalidUrl: "Enter an HTTP or HTTPS server URL.", }; const zh: typeof en = { + platformTitle: "平台提供", + platformHint: "由平台统一配置,供所有用户使用。", + personalTitle: "我的插件", + personalHint: "连接和配置仅属于你的账号,其他用户无法使用。", install: "配置插件", name: "连接名称", url: "服务地址", @@ -39,8 +47,7 @@ const zh: typeof en = { sign_secret: "机器人加签密钥", webhook_key: "机器人 Webhook 密钥(地址中的 key 参数)", save: "保存配置", - accountHint: - "这里配置的是由管理员管理的部署凭据。支持个人授权的集成,通过其账号流程连接。", + accountHint: "这里的配置仅保存到你的账号,其他用户无法查看或使用。", saved: "配置已保存", unknown: "未检测", required: "需要连接账号", diff --git a/frontend/src/core/capabilities/hooks.ts b/frontend/src/core/capabilities/hooks.ts index 43b2e01d1d..52c92c52d1 100644 --- a/frontend/src/core/capabilities/hooks.ts +++ b/frontend/src/core/capabilities/hooks.ts @@ -1,6 +1,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { fetch } from "@/core/api/fetcher"; +import { useAuth } from "@/core/auth/AuthProvider"; import { getBackendBaseURL } from "@/core/config"; import type { InstallationList, PluginManifest } from "./types"; @@ -30,7 +31,8 @@ export function useCapabilityCatalog() { }); } export function useCapabilityInstallations(adapter: string) { - return useQuery(installationQuery(adapter)); + const { user } = useAuth(); + return useQuery(installationQuery(adapter, user?.id)); } export function useInstallCapability() { const client = useQueryClient(); @@ -43,7 +45,7 @@ export function useInstallCapability() { request("installations", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body), + body: JSON.stringify({ ...body, scope: "user" }), }), onSuccess: async () => { await Promise.all([ @@ -55,10 +57,12 @@ export function useInstallCapability() { }); } -export function installationQuery(adapter: string) { +export function installationQuery(adapter: string, userId?: string) { return { - queryKey: ["capabilities", "installations", adapter], + queryKey: ["capabilities", "installations", adapter, userId], queryFn: () => - request(`installations/${encodeURIComponent(adapter)}`), + request( + `installations/${encodeURIComponent(adapter)}?scope=all`, + ), }; } diff --git a/frontend/src/core/mcp/api.ts b/frontend/src/core/mcp/api.ts index b07217acdc..27778000b3 100644 --- a/frontend/src/core/mcp/api.ts +++ b/frontend/src/core/mcp/api.ts @@ -3,6 +3,10 @@ import { getBackendBaseURL } from "@/core/config"; import type { MCPConfig, MCPServerConfig } from "./types"; +export type MCPScope = "deployment" | "user"; +const configPath = (scope: MCPScope) => + scope === "user" ? "/api/mcp/personal/config" : "/api/mcp/config"; + export class MCPConfigRequestError extends Error { readonly status: number; constructor(status: number, message: string) { @@ -25,8 +29,8 @@ async function readErrorDetail( return typeof error.detail === "string" ? error.detail : fallback; } -export async function loadMCPConfig() { - const response = await fetch(`${getBackendBaseURL()}/api/mcp/config`); +export async function loadMCPConfig(scope: MCPScope = "deployment") { + const response = await fetch(`${getBackendBaseURL()}${configPath(scope)}`); if (!response.ok) { throw new MCPConfigRequestError( response.status, @@ -76,27 +80,37 @@ async function mutateMCPServerConfig( return response.json() as Promise; } -export function createMCPServers(servers: Record) { +export function createMCPServers( + servers: Record, + scope: MCPScope = "deployment", +) { return mutateMCPServerConfig( - "/api/mcp/config/servers", + `${configPath(scope)}/servers`, "POST", { mcp_servers: servers }, "Failed to add MCP servers", ); } -export function updateMCPServer(serverName: string, server: MCPServerConfig) { +export function updateMCPServer( + serverName: string, + server: MCPServerConfig, + scope: MCPScope = "deployment", +) { return mutateMCPServerConfig( - "/api/mcp/config/server", + `${configPath(scope)}/server`, "PUT", { server_name: serverName, server }, "Failed to update MCP server", ); } -export function deleteMCPServer(serverName: string) { +export function deleteMCPServer( + serverName: string, + scope: MCPScope = "deployment", +) { return mutateMCPServerConfig( - `/api/mcp/config/servers/${encodeURIComponent(serverName)}`, + `${configPath(scope)}/servers/${encodeURIComponent(serverName)}`, "DELETE", undefined, "Failed to delete MCP server", @@ -106,8 +120,9 @@ export function deleteMCPServer(serverName: string) { export async function updateMCPServerState( serverName: string, enabled: boolean, + scope: MCPScope = "deployment", ) { - const response = await fetch(`${getBackendBaseURL()}/api/mcp/config`, { + const response = await fetch(`${getBackendBaseURL()}${configPath(scope)}`, { method: "PATCH", headers: { "Content-Type": "application/json", diff --git a/frontend/src/core/mcp/hooks.ts b/frontend/src/core/mcp/hooks.ts index f9124c0011..9e14e25649 100644 --- a/frontend/src/core/mcp/hooks.ts +++ b/frontend/src/core/mcp/hooks.ts @@ -6,6 +6,8 @@ import { } from "@tanstack/react-query"; import { toast } from "sonner"; +import { useAuth } from "@/core/auth/AuthProvider"; + import { createMCPServers, deleteMCPServer, @@ -14,12 +16,15 @@ import { updateMCPServer, updateMCPServerState, } from "./api"; +import type { MCPScope } from "./api"; import type { MCPServerConfig } from "./types"; export function useMCPConfig() { + const { user } = useAuth(); const { data, isLoading, error } = useQuery({ - queryKey: ["mcpConfig"], - queryFn: () => loadMCPConfig(), + queryKey: ["mcpConfig", "user", user?.id], + queryFn: () => loadMCPConfig("user"), + enabled: !!user, retry: (count, error) => !(error instanceof MCPConfigRequestError) && count < 3, }); @@ -31,10 +36,13 @@ interface EnableMCPServerVariables { enabled: boolean; } -export function getEnableMCPServerMutationOptions(queryClient: QueryClient) { +export function getEnableMCPServerMutationOptions( + queryClient: QueryClient, + scope: MCPScope = "deployment", +) { return { mutationFn: ({ serverName, enabled }: EnableMCPServerVariables) => - updateMCPServerState(serverName, enabled), + updateMCPServerState(serverName, enabled, scope), onSuccess: async () => { await Promise.all([ queryClient.invalidateQueries({ queryKey: ["mcpConfig"] }), @@ -49,7 +57,7 @@ export function getEnableMCPServerMutationOptions(queryClient: QueryClient) { export function useEnableMCPServer() { const queryClient = useQueryClient(); - return useMutation(getEnableMCPServerMutationOptions(queryClient)); + return useMutation(getEnableMCPServerMutationOptions(queryClient, "user")); } export type MCPServerMutationVariables = @@ -67,16 +75,19 @@ export type MCPServerMutationVariables = serverName: string; }; -export function getMCPServerMutationOptions(queryClient: QueryClient) { +export function getMCPServerMutationOptions( + queryClient: QueryClient, + scope: MCPScope = "deployment", +) { return { mutationFn: (variables: MCPServerMutationVariables) => { switch (variables.operation) { case "create": - return createMCPServers(variables.servers); + return createMCPServers(variables.servers, scope); case "update": - return updateMCPServer(variables.serverName, variables.server); + return updateMCPServer(variables.serverName, variables.server, scope); case "delete": - return deleteMCPServer(variables.serverName); + return deleteMCPServer(variables.serverName, scope); } }, onSuccess: async () => { @@ -93,5 +104,5 @@ export function getMCPServerMutationOptions(queryClient: QueryClient) { export function useMCPServerMutation() { const queryClient = useQueryClient(); - return useMutation(getMCPServerMutationOptions(queryClient)); + return useMutation(getMCPServerMutationOptions(queryClient, "user")); } diff --git a/frontend/tests/e2e/business-plugins.spec.ts b/frontend/tests/e2e/business-plugins.spec.ts index 716d98450c..29d37c2b5f 100644 --- a/frontend/tests/e2e/business-plugins.spec.ts +++ b/frontend/tests/e2e/business-plugins.spec.ts @@ -31,7 +31,7 @@ for (const plugin of [ let submission: Record | undefined; let configured = false; const connectionName = `team-${plugin.id}`; - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: configured @@ -82,6 +82,7 @@ for (const plugin of [ plugin_id: plugin.id, name: connectionName, configuration: plugin.fields, + scope: "user", }); await expect( page.locator("article").filter({ hasText: connectionName }), @@ -113,7 +114,7 @@ test("rejected credentials remain editable and are not marked configured", async ); }); -test("ordinary users see each shared connection once and cannot configure credentials", async ({ +test("ordinary users see shared connections read-only and configure their own credentials", async ({ page, }) => { mockLangGraphAPI(page); @@ -141,8 +142,10 @@ test("ordinary users see each shared connection once and cannot configure creden }; // Both adapters project the same MCP installation, not a second account. for (const adapter of ["mcp", "business"]) { - await page.route(`**/api/capabilities/installations/${adapter}`, (route) => - route.fulfill({ json: { items: [connection], can_manage: false } }), + await page.route( + `**/api/capabilities/installations/${adapter}?scope=all`, + (route) => + route.fulfill({ json: { items: [connection], can_manage: false } }), ); } await page.goto("/workspace/capabilities"); @@ -154,12 +157,41 @@ test("ordinary users see each shared connection once and cannot configure creden await page.evaluate(() => document.dispatchEvent(new Event("visibilitychange")), ); + const platform = page.getByRole("region", { + name: "Platform provided", + exact: true, + }); + const personal = page.getByRole("region", { + name: "My plugins", + exact: true, + }); + await expect( + platform.getByRole("button", { + name: "Setup guide Firecrawl", + exact: true, + }), + ).toBeVisible(); + await expect( + platform.locator("article").filter({ hasText: "Team CRM" }), + ).toHaveCount(1); + await expect( + personal.locator("article").filter({ hasText: "Team CRM" }), + ).toHaveCount(0); + await expect( + personal.getByRole("button", { name: "Add MCP plugin" }), + ).toBeVisible(); + await expect( + personal.locator("article").filter({ hasText: "HubSpot" }), + ).toContainText("Not configured"); await expect( page.locator("article").filter({ hasText: "Team CRM" }), ).toHaveCount(1); await expect( - page.getByRole("button", { name: "Add MCP plugin" }), + page.locator("article").filter({ hasText: "Team CRM" }).getByRole("switch"), ).toHaveCount(0); + await expect( + page.getByRole("button", { name: "Add MCP plugin" }), + ).toHaveCount(1); await page .getByRole("button", { name: "Configure WeCom group notifications", @@ -167,9 +199,9 @@ test("ordinary users see each shared connection once and cannot configure creden }) .click(); const dialog = page.getByRole("dialog"); - await expect(dialog.locator("#plugin-field-webhook_key")).toBeDisabled(); + await expect(dialog.locator("#plugin-field-webhook_key")).toBeEnabled(); await expect(dialog.locator("#plugin-field-webhook_key")).toHaveValue(""); await expect( dialog.getByRole("button", { name: "Save configuration" }), - ).toBeDisabled(); + ).toBeEnabled(); }); diff --git a/frontend/tests/e2e/capability-center.spec.ts b/frontend/tests/e2e/capability-center.spec.ts index 9c0008f3d2..7a7f9f6e57 100644 --- a/frontend/tests/e2e/capability-center.spec.ts +++ b/frontend/tests/e2e/capability-center.spec.ts @@ -53,7 +53,7 @@ const skills = [ async function mockCatalog(page: Page, locale = "en-US") { mockLangGraphAPI(page, { skills, threads: [] }); - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { @@ -218,7 +218,7 @@ test("MCP access errors preserve the independently available Lark integration", page, }) => { await mockCatalog(page); - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ status: 403, json: { detail: "Admin only" } }), ); await page.goto("/workspace/capabilities"); @@ -268,7 +268,7 @@ test("plugin filters remain usable after an MCP refetch fails", async ({ }) => { await mockCatalog(page); let failRead = false; - await page.route("**/api/mcp/config", async (route) => { + await page.route("**/api/mcp/personal/config", async (route) => { if (route.request().method() === "PATCH") { failRead = true; return route.fulfill({ json: { mcp_servers: {} } }); @@ -323,7 +323,7 @@ test("plugin categories, setup guides, and installed state remain distinct", asy "研发与运维", ]) { await expect( - page.getByRole("heading", { name, exact: true }), + page.getByRole("heading", { name, exact: true }).first(), ).toBeVisible(); } await screenshot(page, "capability-catalog-zh.png"); @@ -356,7 +356,7 @@ test("plugin categories, setup guides, and installed state remain distinct", asy await page.getByRole("tab", { name: "已安装", exact: true }).click(); await expect(page.locator("article")).toHaveCount(0); await expect( - page.getByText("没有找到匹配的内容", { exact: true }), + page.getByText("没有找到匹配的内容", { exact: true }).first(), ).toBeVisible(); await page.getByRole("textbox", { name: "搜索插件名称或用途" }).fill(""); await expect(page.locator("article")).toHaveCount(5); @@ -385,7 +385,7 @@ test("manifest installation saves through the adapter and refreshes the catalog" mockLangGraphAPI(page); let installed: Record | null = null; let submission: Record | null = null; - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: installed ? { "team-code": installed } : {} }, }), @@ -443,7 +443,7 @@ test("agent selection saves explicit plugin IDs and an empty skill list", async agents: [{ name: "analyst", description: "Analyze reports" }], skills, }); - await page.route("**/api/capabilities/installations/mcp", (route) => + await page.route("**/api/capabilities/installations/mcp?scope=all", (route) => route.fulfill({ json: { can_manage: false, diff --git a/frontend/tests/e2e/mcp-settings.spec.ts b/frontend/tests/e2e/mcp-settings.spec.ts index cff1a67178..345fa997cf 100644 --- a/frontend/tests/e2e/mcp-settings.spec.ts +++ b/frontend/tests/e2e/mcp-settings.spec.ts @@ -15,7 +15,7 @@ for (const { viewport, name } of [ await page.setViewportSize(viewport); mockLangGraphAPI(page); const server = { enabled: false, command: "npx", args: ["example"] }; - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { [name]: server } } }), ); await page.goto("/workspace/capabilities"); @@ -109,7 +109,7 @@ for (const viewport of [ Array.from({ length: 60 }, (_, index) => [`TEST_${index}`, "example"]), ), }; - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { github: server } } }), ); await page.goto("/workspace/capabilities"); @@ -196,14 +196,14 @@ test.describe("MCP server settings", () => { | { server_name: string; server: (typeof servers)["remote"] } | undefined; - await page.route("**/api/mcp/config", async (route) => { + await page.route("**/api/mcp/personal/config", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ mcp_servers: servers }), }); }); - await page.route("**/api/mcp/config/server", async (route) => { + await page.route("**/api/mcp/personal/config/server", async (route) => { if (route.request().method() !== "PUT") { await route.fallback(); return; diff --git a/frontend/tests/e2e/plugin-icons.spec.ts b/frontend/tests/e2e/plugin-icons.spec.ts index e6fba13a6f..95aed85b93 100644 --- a/frontend/tests/e2e/plugin-icons.spec.ts +++ b/frontend/tests/e2e/plugin-icons.spec.ts @@ -48,7 +48,7 @@ test("brand icons load locally for both recommendations and configured MCP serve .context() .addCookies([{ name: "locale", value: "zh-CN", url: baseURL! }]); mockLangGraphAPI(page); - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { @@ -137,10 +137,10 @@ test("upload previews, cancellation, save, reload, restore default, and create a }, }; let writes = 0; - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: servers } }), ); - await page.route("**/api/mcp/config/server", async (route) => { + await page.route("**/api/mcp/personal/config/server", async (route) => { writes++; const body = route.request().postDataJSON() as { server_name: string; @@ -149,7 +149,7 @@ test("upload previews, cancellation, save, reload, restore default, and create a servers[body.server_name] = body.server; await route.fulfill({ json: { mcp_servers: servers } }); }); - await page.route("**/api/mcp/config/servers", async (route) => { + await page.route("**/api/mcp/personal/config/servers", async (route) => { writes++; const body = route.request().postDataJSON() as { mcp_servers: Record; @@ -254,7 +254,7 @@ test("invalid or oversized uploads do not replace the existing icon", async ({ page, }) => { mockLangGraphAPI(page); - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { @@ -322,7 +322,7 @@ test("a custom MCP name does not impersonate a catalog brand in rows or editing" page, }) => { mockLangGraphAPI(page); - await page.route("**/api/mcp/config", (route) => + await page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: { diff --git a/frontend/tests/e2e/utils/mock-api.ts b/frontend/tests/e2e/utils/mock-api.ts index 358a03190f..3b530b4a43 100644 --- a/frontend/tests/e2e/utils/mock-api.ts +++ b/frontend/tests/e2e/utils/mock-api.ts @@ -1859,7 +1859,7 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) { return route.fallback(); }); - void page.route("**/api/mcp/config", (route) => + void page.route("**/api/mcp/personal/config", (route) => route.fulfill({ json: { mcp_servers: {} } }), ); @@ -1877,7 +1877,7 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) { }), ); void page.route("**/api/capabilities/installations/*", (route) => { - const adapter = route.request().url().split("/").pop(); + const adapter = new URL(route.request().url()).pathname.split("/").pop(); const items = adapter === "lark" ? [ diff --git a/frontend/tests/unit/core/mcp/ownership.dom.test.tsx b/frontend/tests/unit/core/mcp/ownership.dom.test.tsx new file mode 100644 index 0000000000..ece9098b47 --- /dev/null +++ b/frontend/tests/unit/core/mcp/ownership.dom.test.tsx @@ -0,0 +1,66 @@ +import { afterEach, describe, expect, it, rs } from "@rstest/core"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { cleanup, renderHook, waitFor } from "@testing-library/react"; +import type { ReactNode } from "react"; + +const auth = rs.hoisted(() => ({ user: { id: "alice" } })); +rs.mock("@/core/auth/AuthProvider", () => ({ useAuth: () => auth })); +rs.mock("@/core/api/fetcher", () => ({ fetch: rs.fn() })); + +import { fetch } from "@/core/api/fetcher"; +import { useMCPConfig } from "@/core/mcp/hooks"; + +afterEach(cleanup); + +describe("personal MCP ownership", () => { + it("does not display a previous account's late response after switching users", async () => { + auth.user = { id: "alice" }; + let finishAlice!: (response: Response) => void; + rs.mocked(fetch).mockImplementation(async () => { + if (auth.user.id === "alice") { + return new Promise((resolve) => { + finishAlice = resolve; + }); + } + return new Response( + JSON.stringify({ mcp_servers: { "bob-only": { enabled: true } } }), + ); + }); + const client = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + const wrapper = ({ children }: { children: ReactNode }) => ( + {children} + ); + const hook = renderHook(() => useMCPConfig(), { wrapper }); + await waitFor(() => expect(finishAlice).toBeDefined()); + auth.user = { id: "bob" }; + hook.rerender(); + await waitFor(() => + expect(hook.result.current.config?.mcp_servers).toHaveProperty( + "bob-only", + ), + ); + finishAlice( + new Response( + JSON.stringify({ mcp_servers: { "alice-only": { enabled: true } } }), + ), + ); + await waitFor(() => + expect(client.getQueryData(["mcpConfig", "user", "alice"])).toBeDefined(), + ); + expect(hook.result.current.config?.mcp_servers).not.toHaveProperty( + "alice-only", + ); + expect(hook.result.current.config?.mcp_servers).toHaveProperty("bob-only"); + expect( + rs + .mocked(fetch) + .mock.calls.every( + ([url]) => + typeof url === "string" && url.endsWith("/api/mcp/personal/config"), + ), + ).toBe(true); + client.clear(); + }); +});