docs: capture exploration — generic MCP module (phase 19.7, note, two prerequisite todos)

This commit is contained in:
Jakub Zych
2026-10-17 20:31:25 +02:00
parent f3dcbd6625
commit eab4528a5a
8 changed files with 96 additions and 6 deletions

View File

@@ -0,0 +1,38 @@
---
title: Generic MCP module for SummerCMS applications
date: 2026-10-17
context: /msd-explore session. Agents already drive applications (for example a quiz/presentation app) through plugin management APIs with personal tokens; an MCP surface would be smoother. Records the shape agreed before Phase 19.7 is planned.
---
# Generic MCP module for SummerCMS applications
## Starting point
- Agents mostly call plugin-specific management routes (`user.api_token` + `user.scope:*` + `user.permission:<code>`), not cabana's generic admin CRUD. Generating tools from `fields.yaml` therefore does not cover the use case.
- The route table (`pact.Router`) holds only method, path, handler and middleware names. From that alone the token-accepting routes, read or write (scope middleware or method), path parameters and the required permission code can be derived. Descriptions, request body shapes and response meaning cannot.
- `surf.APIDocsProvider` (one OpenAPI document per application) does not fit applications assembled from plugins in several repositories.
## Decisions
1. **In-binary, opt-in module.** A new framework module (beach name to pick) mounts an MCP endpoint (for example `/mcp`) when enabled in config, in the same way `http.api_docs.enabled` mounts `/api-docs`. Disabled by default; an application without it is unaffected.
2. **Plugins declare their tools.** A new optional `pact` capability, the counterpart of WinterCMS `registerMcpTools()` in `Plugin.php`: each tool has a name, a description, an input Go struct (its JSON schema is reflected) and the route it maps to. A plugin that declares nothing exposes nothing. A shared plugin carries its tools into every application that mounts it.
3. **Each tool declares its guard** (`user` for sm-user-plugin principals, `backend` for cabana principals) and inherits the permission requirements of its route.
4. **`tools/list` is computed per principal.** A tool is offered only when the principal passes the route's checks: `user.scope:*` and `user.permission:<code>` through `MergedPermissions` / `HasPermission` for frontend users, `cabana.Allows` with the token's scope limit for backend users. The user's own tri-state permissions (allow / inherit / deny, already supported by the `permissioneditor` radio mode) therefore shape the tool list with no new permission system.
5. **One person, one connector.** The editor adds the site's MCP URL as a connector and signs in once through `wristband` OAuth as their frontend user. If that frontend user has a verified link to a `backend_users` row, the grant also carries that backend principal, and backend-guarded tools (Journal) run as it. `tools/list` is the union of what both identities allow; a person without a link sees no backend tools.
6. **Journal stays on `backend_users`.** Authors remain backend users; moving them to frontend users would break a core plugin. The account link bridges the two instead.
## The account link must prove ownership
An admin picking any frontend user in My Profile would let that admin act through MCP as someone else. The link is therefore saved only after the frontend account signs in (My Profile → "Link frontend account" → site sign-in). A superuser can set or remove links on the Users screen behind a new backend permission, for onboarding editors. Revoking the link or the OAuth grant removes the backend tools. Never link by matching email.
## Prerequisites (todos)
- Plugins register their frontend permission codes in Go (`.planning/todos/pending/2026-10-17-plugin-registered-frontend-permissions.md`).
- Verified frontend ↔ backend account link (`.planning/todos/pending/2026-10-17-frontend-backend-account-link.md`).
## Open for Phase 19.7 discussion
- Transport (streamable HTTP) and whether to take the MCP Go SDK as a dependency; it needs a decision note naming it and its version (CLAUDE.md dependency rule). Unresolved: not researched in this session.
- How a tool call reaches its handler: an in-process request through the surf router with the resolved principal, so the route's own middleware (throttles, scope, permission) still runs, rather than calling the handler directly.
- Whether plain personal tokens (user `user_api_tokens`, cabana `g15_…`) may also open an MCP session for single-identity agents, next to OAuth.
- Tool naming across plugins (prefix by plugin code to avoid collisions) and how a tool describes its response.