GoPress MCP (Agent Access)
gopress-mcp is the optional first-party MCP protocol adapter for GoPress. It
exposes the protocol-neutral Core Agent Tool Registry and Executor as a remote,
site-scoped MCP server, allowing compatible clients to work inside the same
content, permission, and audit boundaries as the CMS.
This page focuses on plugin activation, client connection, and troubleshooting. For Core Agent design, execution, security, and extension contracts, start with the independent Agent and MCP module.
The current implementation is Safe Write Beta (Phase 3). Phases 0β3 are
implemented; Phase 4 OAuth 2.1 and Phase 5 Resources, Prompts, Tasks, and MCP
Apps are not. The plugin is disabled by default and its Tool Profile remains
read_only after activation until an operator explicitly changes it.
Current Capability
| Area | Implementation |
|---|---|
| Endpoint | POST /mcp below the configured public site URL |
| Transport | Stateless Streamable HTTP |
| Protocols | 2026-07-28, with 2025-11-25 compatibility |
| SDK | Official modelcontextprotocol/go-sdk v1.7.0-pre.3 |
| Authentication | Short-lived administrator-issued Bearer tokens |
| Tools | 6 read tools and 6 controlled write tools |
| Default | Plugin inactive; active profile is read_only |
| Discovery | Filtered by credential scopes, current Core RBAC, and Tool Policy; private 30-second cache |
| Audit | Principal, tool, status, duration, argument digest, and result digest; no token or argument values |
This is not automatic REST-to-Tool conversion. The plugin owns MCP transport, protocol negotiation, and authentication mapping. Core owns authorization, ownership checks, validation, idempotency, audit, domain commands, hooks, and cache invalidation.
flowchart LR
C["MCP client"] -->|"Bearer + POST /mcp"| P["gopress-mcp plugin"]
P --> E["Core Agent Executor"]
E --> A["Scope + RBAC + ownership + Tool Policy"]
E --> R["Agent Tool Registry"]
R --> D["Content / Taxonomy / Media services"]
D --> H["Hooks + cache invalidation"]
E --> U["Agent audit + idempotency"]
Enable and Connect
- Configure the canonical public site URL. Production endpoints should use
HTTPS; plain HTTP is accepted only for
localhost,127.0.0.1, and::1. Credentials are audience-bound to the resulting URL, such ashttps://example.com/mcp. - Activate GoPress MCP from the admin plugin page. The
/mcpand plugin admin routes exist only while the plugin is active. - Open
/admin/plugins/gopress-mcp/settings. - In Access credentials, issue a token with the smallest required scopes. The default expiry is 30 days and the valid range is 1β90 days. The secret is displayed once; only its one-way digest is persisted.
- Copy the generic client configuration from Connection overview:
{
"mcpServers": {
"gopress": {
"type": "http",
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer gp_agent_REPLACE_WITH_YOUR_TOKEN"
}
}
}
}
Client-specific outer configuration can vary. The HTTP URL and Authorization header are the stable parts. Never commit a real token to source control.
The settings page has four tabs: connection and diagnostics, write policy,
credentials, and audit. Dedicated mcp.*, agent_credential.*, and
agent_audit.* permissions protect the page and its custom handlers. Default
roles leave these controls to the super administrator.
Verify with curl
A real MCP client or Inspector is preferred. For a minimal compatibility-path
check, initialize with protocol 2025-11-25:
export GOPRESS_MCP_URL='https://example.com/mcp'
export GOPRESS_MCP_TOKEN='gp_agent_REPLACE_WITH_YOUR_TOKEN'
curl --fail-with-body -sS \
-X POST "$GOPRESS_MCP_URL" \
-H "Authorization: Bearer $GOPRESS_MCP_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data-binary '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {"name": "curl-test", "version": "1.0.0"}
}
}'
List the tools visible to this credential:
curl --fail-with-body -sS \
-X POST "$GOPRESS_MCP_URL" \
-H "Authorization: Bearer $GOPRESS_MCP_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Mcp-Protocol-Version: 2025-11-25' \
--data-binary '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'
A partial list is expected when scopes, current RBAC, or Tool Policy do not
permit every tool. List results use cacheScope: private, ttlMs: 30000, and
an Agent capability revision.
Scopes and Tools
Read tools
| Tool | Scope | Core RBAC | Main arguments |
|---|---|---|---|
gopress.site.get |
gopress:site:read |
dashboard.read |
none |
gopress.content_types.list |
gopress:content:read |
content.read |
none |
gopress.content.list |
gopress:content:read |
content.read |
content_type; optional status, search, taxonomy filters, and pagination |
gopress.content.get |
gopress:content:read |
content.read |
content_type, id |
gopress.taxonomy.list |
gopress:taxonomy:read |
taxonomy.read |
content_type; optional taxonomy |
gopress.media.list |
gopress:media:read |
media.read |
optional mime_type, page, per_page |
Content status is limited to published, pending, draft, archived, and
trash; the default is published. Lists default to 20 rows and allow at most
100. Content details include only registered metadata and sanitized HTML. Media
results do not expose server filesystem paths.
Write tools
| Tool | Scope | Core RBAC | Additional controls |
|---|---|---|---|
gopress.content.create_draft |
gopress:content:write |
content.create |
Draft only; idempotency_key required |
gopress.content.update |
gopress:content:write |
content.update or owned content.update_own |
Optimistic timestamp and idempotency key |
gopress.content.publish |
gopress:content:publish |
content.publish |
Optimistic timestamp, idempotency key, and confirm: true |
gopress.content.move_to_trash |
gopress:content:write |
content.delete or owned content.delete_own |
Soft delete, optimistic timestamp, key, and confirmation |
gopress.content.restore |
gopress:content:write |
content.update |
Restores trash to draft; optimistic timestamp and key |
gopress.media.update_metadata |
gopress:media:write |
media.update or owned media.update_own |
Only alt_text, title, and caption; optimistic timestamp and key |
Write tools accept only registered editable content types. Draft creation validates declared fields, parent type, metadata, unique slugs, and sanitized content. A generic update cannot smuggle in a status transition; publish, trash, and restore use separate tools.
Safe Write Policy
A write call succeeds only when all four controls agree:
site profile == safe_write
AND the individual write tool is enabled
AND the credential contains its gopress:* scope
AND the current principal has the Core resource.action permission
Start with draft creation and content update. Publish and trash are never
enabled merely by choosing safe_write; each requires its own switch. The
default content.publish capability is assigned only to editors and the super
administrator, so an author cannot gain publication rights from a token scope.
Every write argument object needs an 8β200 character idempotency_key.
Retrying the same tool with the same credential, key, and arguments replays the
stored result. Reusing a key with different arguments conflicts. Updates and
transitions also require the most recently read RFC 3339 updated_at as
expected_updated_at; stale writes are rejected.
Draft call arguments:
{
"name": "gopress.content.create_draft",
"arguments": {
"content_type": "post",
"title": "Agent-created draft",
"content": "<p>Body</p>",
"idempotency_key": "draft-20260809-0001"
}
}
Publish call arguments:
{
"name": "gopress.content.publish",
"arguments": {
"content_type": "post",
"id": 42,
"expected_updated_at": "2026-08-09T08:30:00.123456Z",
"idempotency_key": "publish-post-42-0001",
"confirm": true
}
}
Security and Operations
/mcpaccepts only dedicated Agent Bearer tokens, not admin cookies, admin JWTs, or REST API keys.- Tokens bind a subject, scopes, audience, expiry, and revocation state. Current account activation and role are reloaded for every execution.
- Arguments use restricted JSON Schemas, a 64 KiB Tool-argument limit, and a depth limit of 16. HTTP bodies are capped at 256 KiB and outputs at 1 MiB.
- Cross-origin protection,
private, no-store, request cancellation, and a baseline limit of 120 requests per source per minute are enabled. - Tool-specific timeouts and concurrency limits protect domain services.
- Type and ownership checks prevent cross-type or cross-owner ID access.
- Audit states include
started,succeeded,denied,failed, andreplayed. Argument values and Bearer tokens are never persisted. Execution fails closed if audit storage is unavailable.
At a reverse proxy, preserve Authorization, Content-Type, Accept, and all
Mcp-* headers; do not cache /mcp; use HTTPS; and keep the externally visible
URL identical to the configured public site URL. Store tokens only in a client
secret store or environment variable and rotate them regularly.
Protocol transport is stateless, while credentials, idempotency records, and audits use the site database. There is no browser OAuth flow, refresh token, or automatic scope elevation before Phase 4.
Troubleshooting
| Symptom | Check |
|---|---|
/mcp returns 404 |
Plugin activation and router rebuild |
| 401 | Missing, expired, revoked, or audience-mismatched token |
insufficient_scope |
Required credential scope |
permission_denied |
Current Core role and resource ownership |
risk_denied |
safe_write profile and individual Tool switch |
confirmation_required |
confirm: true for publish or trash |
conflict |
Stale expected_updated_at or reused key with changed arguments |
| Fewer than 12 tools | Expected permission-filtered discovery; inspect policy, scopes, and RBAC |
Connection diagnostics report endpoint, transport, authentication, supported protocols, SDK/plugin versions, registry and policy revisions, registered tool count, and secure transport state. The audit tab identifies whether a request was rejected by authorization, policy, or the domain command.
Not Implemented Yet
- OAuth 2.1 resource metadata, browser authorization, PKCE, refresh-token rotation, and step-up scopes.
- MCP Resources, Prompts, Tasks, MCP Apps, subscriptions, or registry publication.
- User and role management, plugin or theme changes, arbitrary site settings, file deletion, database maintenance, refunds, or payment operations.
- A claim of compatibility with every MCP client or access to every admin operation.
The implementation-oriented design is documented in the Agent and MCP module. Full phase decisions, threat model, and later phases are currently documented in the Roadmap and Contributions.
