Extend mcp tool feature set - #8283
Open
cstns wants to merge 33 commits into
Open
Conversation
Add RFC 8414 authorization-server metadata and RFC 9728 protected-resource metadata under /.well-known so MCP clients can auto-discover the OAuth endpoints and the MCP resource URL. Public, license-tier independent. Ref #7431
…agents Extend the OAuth2 flow so external MCP agents (Claude, Cursor, etc.) can authenticate. Rather than a single hardcoded client id, agents register dynamically per RFC 7591: POST /account/client persists a public AuthClient (type 'mcp', no secret) with its approved redirect URIs and returns a generated client id. The authorize, complete, and token endpoints recognise these clients by looking them up, skip the project/device ownership checks (MCP is user-scoped), and drive an MCP consent step that records the read-only and team selection before issuing a scoped personal access token. Redirect URIs must be loopback http (RFC 8252, port-flexible) or https for hosted clients; token issuance and refresh require no client secret. Also forward the caller scope through the platform automation handler and add a platform_get_active_user tool that reports the calling token's scope. Adds AuthClient.type/name/redirectURIs (migration + model), an AuthClient.createMCPClient controller, and AccessToken.createMCPOAuthToken. Ref #7432
Add the AccessRequestMCP.vue consent page and its /account/request/:id/mcp modal route. The page lets the user pick read-only or full access and scope the grant to specific teams before approving the MCP agent request. Ref #7433
Move the RFC 9728 protected-resource document out of the root .well-known handler into the license-gated EE mcp plugin, so it is only advertised where the /mcp resource exists. Serve it at the path-inserted /.well-known/oauth-protected-resource/mcp (RFC 9728 3.1) with the bare path kept as an alias, and challenge unauthenticated /mcp requests with a WWW-Authenticate header pointing at that metadata.
An AccessToken row holds both the access token and its refresh token, and getOrExpire destroyed the row when the access token expired, taking the refresh token with it so a client could never refresh (RFC 6749 1.5). Add a separate refreshTokenExpiresAt lifetime: reject an expired access token but keep the row while its refresh token is still valid. The MCP refresh token is stable rather than rotating, and concurrent refreshes coalesce through a shared cache so they reuse the most recently minted access token instead of overwriting the row.
Capture the pre-refresh expiry after lowering it, so the assertion compares against the shortened lifetime rather than the original one and no longer ties when the refresh lands in the same millisecond.
Link the MCP OAuth token to the AuthClient it was issued to and name it after the registered client instead of the fixed 'MCP Agent', so the user's token list and later audit attribution can tell agents apart.
Platform tool calls were always stamped source 'mcp:expert', so a third-party agent's actions rendered as the first-party Expert. The MCP door now records the caller's registered client name against its mcpSessionId, and the comms handler reads it to stamp source 'mcp' with that client name. The Expert path never opens that door, so it keeps the 'mcp:expert' default. The audit entry surfaces the client name. Closes #8271.
getOrExpire eager-loaded AuthClient on every token lookup so the client name could be attached to the session, adding a join to every Bearer request when only MCP tokens set AuthClientId. Drop the join and resolve the name with a targeted lookup that runs only when AuthClientId is set.
The refresh_token grant returned access_token, expires_in and refresh_token but omitted token_type, which RFC 6749 section 5.1 requires. A spec-compliant client rejects the response and falls back to re-authorization even though the server rotated the token successfully. Return token_type: bearer to match the authorization_code response.
Run third-party MCP platform actions under the caller's own PAT rather than a freshly minted platform token, and derive the audit source from whether the session has one. The door stashes the PAT against the mcpSessionId; the comms layer injects it and marks the entry 'mcp', or mints a token and marks 'mcp:expert' for the first-party Expert path. Removes the per-client identity plumbing (AuthClientId column, client name lookup and rendering), which is not needed to distinguish expert, third-party and api sources.
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: cstns <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
Co-authored-by: andypalmi <andrea@flowfuse.com> Co-authored-by: Costin Serban <cstn.serban@gmail.com>
…feature-set # Conflicts: # forge/db/controllers/AccessToken.js # forge/db/controllers/AuthClient.js # forge/db/migrations/20260824-01-add-mcp-authclient-fields.js # forge/db/models/AuthClient.js # forge/ee/lib/mcp/tools/applications.js # forge/ee/lib/mcp/tools/instances.js # forge/ee/lib/mcp/tools/platform.js # forge/ee/lib/mcp/tools/snapshots.js # forge/ee/lib/mcp/tools/teams.js # forge/routes/auth/oauth.js # test/unit/forge/routes/auth/oauth_spec.js
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8283 +/- ##
==========================================
+ Coverage 76.17% 76.78% +0.60%
==========================================
Files 451 458 +7
Lines 24175 24395 +220
Branches 6461 6478 +17
==========================================
+ Hits 18416 18732 +316
+ Misses 5759 5663 -96
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Feature branch for extending mcp tools
Related Issue(s)
#7727
#7726
#7724
#7723
#7722
#7721
#7720
#7719
#7718
#7717
#7716
#7715
Checklist
flowforge.yml?FlowFuse/helmto update ConfigMap TemplateFlowFuse/CloudProjectto update values for Staging/ProductionLabels
area:migrationlabel