Skip to content

feat: Expose the assistant tool set over an MCP server#186

Merged
nfebe merged 2 commits into
mainfrom
feat/mcp-server
Jul 21, 2026
Merged

feat: Expose the assistant tool set over an MCP server#186
nfebe merged 2 commits into
mainfrom
feat/mcp-server

Conversation

@nfebe

@nfebe nfebe commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Closes #184.

FlatRun's assistant and an external MCP client are now two front doors onto one
tool set: whatever the assistant can do, an MCP client can do too, subject to the
same authorization.

Every MCP call runs as the calling actor and passes the same per-tool permission
and protected-mode checks the assistant enforces, so access is bounded by the
caller's own permissions. The transport is stateless, so authorization is
re-evaluated on each request rather than fixed when a session opens. The endpoint
is off unless enabled in config.

The MCP protocol layer is the whilesmartgo/mcp bridge over the official Go SDK;
FlatRun supplies only the tool registry and its auth.

FlatRun can now serve its assistant's full tool set to external MCP
clients, so an IDE, desktop app, or another agent can drive the server
through the same capabilities the built-in assistant has. The same tools
back both front doors.

Every MCP call is authenticated and runs as the calling actor, gated by
the same per-tool permission and protected-mode checks the assistant
enforces, so a caller can do exactly what their access allows and no
more. The endpoint is off unless enabled in config.
@sourceant

sourceant Bot commented Jul 20, 2026

Copy link
Copy Markdown

Code Review Summary

This PR implements an MCP (Model Context Protocol) server, allowing external agents to access FlatRun's assistant tools with full authentication and authorization parity. It is a well-integrated feature that leverages existing logic for tool registration and permission gating.

🚀 Key Improvements

  • Exposes assistant tools to external MCP clients via /api/mcp.
  • Maintains strict security parity by re-evaluating actor permissions on every call.
  • Provides a runtime toggle for the MCP server in the configuration.
  • Upgrades golang-jwt/jwt/v5 to 5.3.1 addressing security vulnerabilities.

💡 Minor Suggestions

  • Harden the internal gin.Context mock used for tool dispatch to prevent potential nil pointer panics.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. No specific code suggestions were generated. See the overview comment for a summary.

Enabling or disabling the MCP server now takes effect immediately when
the setting is saved, without restarting the agent, so it can be flipped
from the settings UI. While disabled, the endpoint refuses calls rather
than being absent.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

// toolGinContext builds the minimal gin.Context the tool dispatch expects: a
// request carrying ctx (for per-tool timeouts) and the actor for permission
// checks. A nil actor means auth is disabled, as elsewhere in the API.
func toolGinContext(ctx context.Context, actor *auth.ActorContext) *gin.Context {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manual construction of gin.Context here leaves the Writer field as nil. While current tool handlers might only read from the context, any future logic or middleware (such as a permission check that calls c.AbortWithStatusJSON) will trigger a nil pointer dereference panic when attempting to write to the response. Using gin.CreateTestContext with an httptest.NewRecorder is a safer approach for internal dispatching. Note: This requires importing net/http/httptest.

Suggested change
func toolGinContext(ctx context.Context, actor *auth.ActorContext) *gin.Context {
func toolGinContext(ctx context.Context, actor *auth.ActorContext) *gin.Context {
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "/", nil)
w := httptest.NewRecorder()
gc, _ := gin.CreateTestContext(w)
gc.Request = req
if actor != nil {
gc.Set(contextkeys.Actor, actor)
}
return gc
}

@nfebe
nfebe merged commit f582b3b into main Jul 21, 2026
6 checks passed
@nfebe
nfebe deleted the feat/mcp-server branch July 21, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ai): Expose FlatRun's tools as an MCP server

1 participant