Skip to content

chore(cli): correct MCP server layering precedence in docs - #70

Closed
lukealvoeiro wants to merge 1 commit into
mainfrom
cli-args-audit/mcp-layering
Closed

lukealvoeiro wants to merge 1 commit into
mainfrom
cli-args-audit/mcp-layering

Conversation

@lukealvoeiro

Copy link
Copy Markdown
Contributor

Gap

docs/cli/configuration/mcp.mdx and its JP mirror docs/jp/cli/configuration/mcp.mdx describe MCP config layering incorrectly. They say a user-level server wins over folder-level and project-level servers with the same name, and that folder-level mcp.json is read from any ancestor directory of the project. The CLI does neither of these things.

Evidence

  • Docs (before this change), docs/cli/configuration/mcp.mdx:264: ".factory/mcp.json in any ancestor directory of the project". docs/cli/configuration/mcp.mdx:271: "When the same server is defined at multiple levels, user config takes priority, followed by folder-level config, then project config." The JP page has the same statements at lines 264 and 271.
  • Code (factory-mono dev):
    • packages/runtime/settings/src/SettingsManager.ts:3101-3189 (buildSettingsHierarchyLevels) orders levels as org, runtime overlay, folder (nearest first), project, user.
    • packages/runtime/settings/src/mergeMcp.ts:7-27: a server defined at a higher level replaces a lower-level server with the same name.
    • packages/runtime/settings/src/McpSettingsManager.ts:212-223 attributes each server to the first (highest) level that defines it.
    • packages/runtime/shell/src/dot-folder-paths.ts:97-112: folder levels are only the .factory directories from the cwd up to the git root, not including the root. Directories above the project root are never read.
    • packages/runtime/settings/src/SettingsManager.test.ts:667-708 asserts that a project server shadows a user server with the same name ('shadowed-server': { source: SettingsLevel.Project }).
  • History: the general hierarchy was changed from org > user > folder > project to org > folder > project > user on purpose in factory-mono #10094 (ea212240f01, "swap order"). That PR added regression tests named "Precedence Order: Org > Folder > Project > User". The docs sentence came in with docs #1171 and matches the order the code used before #10094.
  • Live check through a hermetic droid built from source (no network, scratch HOME). Fixture: folderlvl/.factory/mcp.json {foldersrv, dup}; folderlvl/repo (git root) /.factory/mcp.json {projsrv, dup, dup2}; repo/sub/.factory/mcp.json {subsrv, dup2}; user ~/.factory-dev/mcp.json {dup2}.
$ (cd folderlvl/repo) droid mcp list            # exit 0
  dup  http  failed: Failed to connect to MCP server  [project]
  dup2  http  failed: Failed to connect to MCP server  [project]
  projsrv  http  failed: Failed to connect to MCP server  [project]
# foldersrv from the ancestor folder is not loaded; the user's dup2 loses to the project's dup2

$ (cd folderlvl/repo) droid mcp add dup2 node   # exit 1
Error: Server "dup2" already exists with configuration:
  Type: http
  URL: http://127.0.0.1:9/proj-dup2
  ...

$ (cd folderlvl/repo/sub/deeper) droid mcp list # exit 0
  dup  http  failed: Failed to connect to MCP server  [project]
  dup2  http  failed: Failed to connect to MCP server  [folder]
  projsrv  http  failed: Failed to connect to MCP server  [project]
  subsrv  http  failed: Failed to connect to MCP server  [folder]
# the folder between cwd and the git root wins over the project, and the project wins over the user

Why it matters

Users who follow the docs expect a server in ~/.factory/mcp.json to override a team server with the same name. In practice the project config wins silently. Users may also put a shared mcp.json in a parent folder above several repos, and that file is never read.

Fix

Docs only, in EN and JP:

  • The Folder row now says .factory/mcp.json in a subdirectory between the working directory and the project root. Its purpose is now described as scoping servers to part of a repository, such as one package in a monorepo.
  • "How Layering Works" now gives the order folder (nearest to the working directory first), then project, then user. It also says servers from organization-managed settings take priority over all local levels. The page already mentions org-provided servers in the paragraph above.

I checked the other EN/JP pages that mention mcp.json: settings, custom-droids, plugins, the video walkthrough and hierarchical-settings-and-org-control. None of them state an MCP-specific precedence, so they are unchanged.

Not changed here: the existing bullet saying that enabling or disabling a project-defined server saves a user-level copy. The CLI does write that copy (McpSettingsManager.updateMcpServer). However, because the project level now outranks the user level, the copy does not take effect. A user config of {"dup":{...,"disabled":true}} still leaves the project's dup active in droid mcp list. This is a code issue and should be fixed separately from this docs change.

Validation

  • Re-ran the three commands above with the hermetic wrapper against the fixture; the outputs and exit codes are shown above.
  • The docs repo has no local lint or format tooling (no package.json scripts, and no lint step in CI). Checked that every row of the edited tables has the same 4 column separators in both files. git diff --check is clean.

Found by the scheduled CLI arguments audit (gap EXT-T1).

The MCP configuration page said user config wins over folder and project
config, and that folder-level mcp.json is read from ancestor directories of
the project. The CLI resolves MCP servers through the general settings
hierarchy (org > folder > project > user, reordered deliberately in
factory-mono #10094), and folder levels are the .factory directories between
the working directory and the git root. Update the EN page and the JP mirror
to describe the actual behavior.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@lukealvoeiro lukealvoeiro added the cli-args-audit CLI arguments documentation and behavior audits label Oct 13, 2026
@factory-droid

factory-droid Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Droid finished @lukealvoeiro's task —— View job


LGTM. No actionable issues found in the MCP layering documentation changes.

0 inline comments posted

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

Labels

cli-args-audit CLI arguments documentation and behavior audits

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant