Conversation
Adds /sites/mcp, a Local-wide MCP endpoint that is not bound to any site,
alongside the existing per-site /sites/{siteId}/mcp routes. It can be
configured once at user scope and used from any directory, which removes
the bootstrap problem: previously the only way to reach the MCP server was
to enable a site in Local's UI and then open that site's folder.
The route is collision-free — /sites/mcp is one path segment after /sites,
so it cannot match the two-segment per-site pattern, and a site whose id is
literally "mcp" still resolves at /sites/mcp/mcp.
Tool surface is now split in two. Site-scoped tools (wp_cli, the log
readers, the wp-config tools, get_site_info, site_health_check) stay on the
per-site endpoints, since they need a bound SiteConfig. Tools that address
Local itself or take an explicit siteId are served on both. Sessions carry
a nullable siteId and handleToolCall takes a nullable config, rejecting a
site-scoped tool called globally with a message pointing at the per-site
endpoint.
Three new tools wrap the setup and teardown paths that were previously
reachable only through the renderer's IPC handlers:
- enable_agent_tools registers a site, writes its MCP config and project
context, and returns the site's own endpoint URL
- disable_agent_tools tears down and is idempotent
- agent_tools_status reports enablement, agents, project dir, registration
and endpoint URL for one site or all of them
Two things surfaced while wiring those up:
setupSite only ever adds files, so it cannot be reused as-is for a site
that is already enabled — moving the project dir would strand config at
the old location, and narrowing the agent list would strand the dropped
agents' files. enable_agent_tools now routes through changeProjectDir and
updateAgents, which clean up after themselves, then regenerateConfig.
projectDir lands in path.join(sitePath, projectDir). That was safe coming
from the UI's folder picker, but over MCP an absolute path or a `..`
segment would write agent config anywhere on disk, so it is now validated.
Adds 33 tests, including a real MCP handshake against the global endpoint
covering the advertised tool list, a global tool call, the site-scoped
refusal, and the /sites/mcp/mcp case.
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Adds /sites/mcp, a Local-wide MCP endpoint that is not bound to any site, alongside the existing per-site /sites/{siteId}/mcp routes. It can be configured once at user scope and used from any directory, which removes the bootstrap problem: previously the only way to reach the MCP server was to enable a site in Local's UI and then open that site's folder.
The route is collision-free — /sites/mcp is one path segment after /sites, so it cannot match the two-segment per-site pattern, and a site whose id is literally "mcp" still resolves at /sites/mcp/mcp.
Tool surface is now split in two. Site-scoped tools (wp_cli, the log readers, the wp-config tools, get_site_info, site_health_check) stay on the per-site endpoints, since they need a bound SiteConfig. Tools that address Local itself or take an explicit siteId are served on both. Sessions carry a nullable siteId and handleToolCall takes a nullable config, rejecting a site-scoped tool called globally with a message pointing at the per-site endpoint.
Three new tools wrap the setup and teardown paths that were previously reachable only through the renderer's IPC handlers:
Two things surfaced while wiring those up:
setupSite only ever adds files, so it cannot be reused as-is for a site that is already enabled — moving the project dir would strand config at the old location, and narrowing the agent list would strand the dropped agents' files. enable_agent_tools now routes through changeProjectDir and updateAgents, which clean up after themselves, then regenerateConfig.
projectDir lands in path.join(sitePath, projectDir). That was safe coming from the UI's folder picker, but over MCP an absolute path or a
..segment would write agent config anywhere on disk, so it is now validated.Adds 33 tests, including a real MCP handshake against the global endpoint covering the advertised tool list, a global tool call, the site-scoped refusal, and the /sites/mcp/mcp case.
Description of the Change
Closes #
How to test the Change
Changelog Entry
Credits
Props @username, ...
Checklist: