8.7 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 19 | Drop legacy ~/.pi/agent/settings.json fallback for MCP server names |
Drop legacy ~/.pi/agent/settings.json fallback for MCP server names
Problem Statement
PermissionManager.getConfiguredMcpServerNames() reads MCP server names from two paths: mcp.json and the legacy settings.json (Pi's own settings file).
The settings.json fallback reaches into another package's config file whose structure Pi can change at any time.
The supported MCP server config source is mcp.json, which the manager already reads.
Keeping the fallback creates a fragile coupling and a confusing second source of truth for MCP server name derivation.
Goals
- Remove
defaultLegacyGlobalSettingsPath()and all references tolegacyGlobalSettingsPathfromsrc/permission-manager.ts. - Remove
legacyGlobalSettingsPathfrom thePermissionManagerconstructor options. - Keep
mcp.jsonas the sole file-based source for derived MCP server names (themcpServerNamesoverride remains). - Add a test confirming that server names in a
settings.json-style file are not picked up. - Verify no README or docs reference
settings.jsonas a source for MCP server names (none found).
Non-Goals
- Changing the MCP target derivation logic (
pushMcpToolPermissionTargets,addDerivedMcpServerTargets,createMcpPermissionTargets). - Changing how users configure MCP servers in Pi itself.
- Adding any new MCP config sources.
Background
Relevant modules
| File | Role |
|---|---|
src/permission-manager.ts |
Contains defaultLegacyGlobalSettingsPath(), the legacyGlobalSettingsPath field, and uses it in getConfiguredMcpServerNames(). This is the only file that references the legacy path. |
src/index.ts |
Constructs PermissionManager — does not pass legacyGlobalSettingsPath, so it gets the default. No changes needed. |
tests/permission-system.test.ts |
Tests use the mcpServerNames override, not the file-based lookup. No existing tests exercise the legacy path. |
Permission surface
MCP — specifically the server-name derivation used to expand bare tool names into server:tool permission targets.
The change does not affect any permission decision logic; it only narrows the set of files consulted for server name discovery.
Existing code path
// In getConfiguredMcpServerNames():
const paths = [this.globalMcpConfigPath, this.legacyGlobalSettingsPath];
After this change, the array becomes [this.globalMcpConfigPath] — or the method simplifies to read only mcp.json.
Design Overview
This is a pure removal — no new types, no new config fields, no merge-precedence changes.
What's removed
defaultLegacyGlobalSettingsPath()— the free function returningjoin(getAgentDir(), "settings.json").legacyGlobalSettingsPath— the private field onPermissionManager.legacyGlobalSettingsPath— the optional constructor parameter.- The second element in the
pathsarray insidegetConfiguredMcpServerNames().
What stays
globalMcpConfigPathanddefaultGlobalMcpConfigPath()— unchanged.mcpServerNamesconstructor override — unchanged.getConfiguredMcpServerNamesFromPaths()andreadConfiguredMcpServerNamesFromConfigPath()— unchanged (still used formcp.json).
Edge cases
- A user who only had MCP servers defined in
settings.json(notmcp.json) would silently lose server-name derivation. This is intentional:settings.jsonwas never documented as a permission-system config source, and any servers there are still usable in Pi — they just won't influence permission target expansion. The worst case is that a bare tool namefoo_myserverstops matching themyserver:foo_myserverexpansion, falling through to the default MCP policy (which defaults toask, notallow). This cannot silently weaken a permission — it can only make a permission stricter.
Module-Level Changes
src/permission-manager.ts — changed
- Delete
defaultLegacyGlobalSettingsPath(). - Remove
legacyGlobalSettingsPathfrom the private fields. - Remove
legacyGlobalSettingsPathfrom the constructor options interface and the constructor body. - In
getConfiguredMcpServerNames(), change thepathsarray to[this.globalMcpConfigPath].
tests/permission-system.test.ts — changed
- Add a test constructing a
PermissionManagerwith a tempsettings.jsoncontainingmcpServersand confirmgetConfiguredMcpServerNames()(viacheckPermissionon an MCP tool) does not derive targets from those names. SincegetConfiguredMcpServerNames()is private, the test will usecheckPermission("mcp", ...)with a bare tool name and assert the server-derived targets are absent. - Alternatively, add a focused unit test for
getConfiguredMcpServerNamesFromPaths()(the module-level function) to confirm only themcp.jsonpath is consulted.
No schema, config, or README changes required
settings.json is not referenced in schemas/permissions.schema.json, config/config.example.json, or README.md.
TDD Order
-
Red: test that
settings.jsonserver names are not used. Write a test that creates a tempsettings.jsonwith{ "mcpServers": { "legacy-server": {} } }and amcp.jsonwithout that server. Construct aPermissionManagerwith those paths. CallcheckPermission("mcp", { tool: "some_tool_legacy-server" })and assert the result does not produce alegacy-server:some_tool_legacy-servertarget match. This test should pass even before the removal (since the derivation path exists but only affects ordering), so frame the assertion as: the manager must produce identical results whether or notsettings.jsonexists. Commit:test: verify MCP server names come only from mcp.json (#19) -
Green: remove legacy settings.json fallback. Delete
defaultLegacyGlobalSettingsPath(), thelegacyGlobalSettingsPathfield, the constructor option, and the array entry ingetConfiguredMcpServerNames(). All existing tests must still pass. Commit:feat: drop legacy settings.json fallback for MCP server names (#19) -
Verify: run full test suite. Confirm
npm testandnpm run buildpass cleanly. Commit (if any fixups needed):fix: adjust tests after legacy path removal (#19)
Risks and Mitigations
| Risk | Mitigation |
|---|---|
| Could this silently weaken a permission? | No. Removing a server-name source can only make derivation less permissive — a bare tool name that previously matched a server-qualified allow rule would now fall through to the default MCP policy (ask). This is stricter, not weaker. |
Users relying on settings.json for MCP server name derivation. |
This was never documented. Users who configure MCP servers in settings.json can add the same entries to mcp.json or use explicit server:tool patterns in their permission policy. |
| On-disk identity change. | None. No config directory, log filename, slash command, or event channel name is affected. |
| Breaking change? | Non-breaking. The constructor option legacyGlobalSettingsPath was internal and not part of any public API contract. No policy file format changes. |
Open Questions
None — the scope is narrow and unambiguous.