Skip to content

feat(plugin-forms): offer forms and submissions as MCP tools - #4027

Draft
eisenbruch wants to merge 2 commits into
emdash-cms:mainfrom
eisenbruch:feat/forms-mcp-tools
Draft

eisenbruch wants to merge 2 commits into
emdash-cms:mainfrom
eisenbruch:feat/forms-mcp-tools

Conversation

@eisenbruch

Copy link
Copy Markdown
Contributor

What does this PR do?

Offers four forms-plugin routes as MCP tools, so an API token can read form submissions without the admin scope:

  • forms_list
  • submissions_list
  • submissions_get
  • submissions_update (status, starred, notes; it does not touch what the visitor sent)

The problem. Every private plugin route requires the admin token scope (authorizePrivatePluginRouteRequest), whatever permission the route declares. So an agent that works a site's content through the MCP server, with a token that deliberately has no admin, cannot see what arrived through the site's forms. On our site that means it cannot read listing claims or business submissions, and an editor has to copy them across by hand. Plugin MCP tools (#2002, Discussion #1587) are the designed way round this, and the forms plugin declares none.

Relation to #2229. #2229 exposes all ten management routes and has had changes requested since August 12. This PR is narrower on purpose and settles both review points:

  • Unbounded export over MCP. Export is not offered here at all, nor is delete, nor any form-editing route. An agent reading submissions has no need of them, and each one is a larger thing to hand to a token.
  • The forms list could not be paged. forms/list now takes limit (1 to 100, default 100) and cursor and forwards them to the query. The route's input is formsListSchema.prefault({}), so the admin's existing calls, which send {}, and a caller that sends no body still get the first 100.

I am happy to close this in favor of #2229 if its author picks it up, or to add tools here if you would rather have the wider set in one PR.

REST behavior is unchanged. A tool can only bind to a route that names its permission, so the four routes now state permission: "plugins:manage", which is what they defaulted to. Tools stay off until a site admin enables them for the plugin, and a token needs mcp:tools or mcp:tools:emdash-forms.

Discussion: #1587

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to an approved Discussion: RFC: Let plugins explicitly declare MCP tools #1587
  • I have included screenshots below if this PR changes the UI

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 5.5 (Claude Code)

Screenshots / test output

Not applicable (no UI change).

tests/mcp-tools.test.ts checks what the runtime checks before it will register a tool (the route exists, is private, names a permission, and is a JSON POST route), because a tool that fails any of those is dropped silently. It also checks that delete, export and the form-editing routes are not bound, that the REST permission is where it was, and that the forms list pages and still accepts an empty or missing body.

packages/plugins/forms
 Test Files  10 passed (10)
      Tests  59 passed (59)

Run against a deployed site (EmDash 1.2.0 on Cloudflare Workers, this change carried as a patch) with a token holding only content:read and mcp:tools: the four tools are listed, forms_list returns the site's nine forms, and submissions_list and submissions_get return a form's submissions. A token without mcp:tools gets INSUFFICIENT_SCOPE: requires mcp:tools:emdash-forms from the same tools. submissions_update was not run against that site.

@changeset-bot

changeset-bot Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 20e6bfe

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@emdash-cms/plugin-forms Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/admin@4027

@emdash-cms/auth

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth@4027

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth-atproto@4027

@emdash-cms/blocks

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/blocks@4027

@emdash-cms/cloudflare

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/cloudflare@4027

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/contentful-to-portable-text@4027

emdash

npm i https://pkg.pr.new/emdash-cms/emdash@4027

create-emdash

npm i https://pkg.pr.new/emdash-cms/emdash/create-emdash@4027

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/gutenberg-to-portable-text@4027

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-cli@4027

@emdash-cms/plugin-test

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-test@4027

@emdash-cms/plugin-types

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-types@4027

@emdash-cms/registry-client

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-client@4027

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-lexicons@4027

@emdash-cms/registry-loader

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-loader@4027

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-moderation@4027

@emdash-cms/registry-verification

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-verification@4027

upgrade-emdash

npm i https://pkg.pr.new/emdash-cms/emdash/upgrade-emdash@4027

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/sandbox-workerd@4027

@emdash-cms/x402

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/x402@4027

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-ai-moderation@4027

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-ai-search@4027

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-atproto@4027

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-audit-log@4027

@emdash-cms/plugin-color

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-color@4027

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-embeds@4027

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-field-kit@4027

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-forms@4027

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-webhook-notifier@4027

commit: 20e6bfe

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant