Skip to content

Fix #286: plugin MCP servers join the config lookup/merge#318

Open
ericleepi314 wants to merge 1 commit into
fix/issue-285-cache-state-latchesfrom
fix/issue-286-plugin-mcp-server-config
Open

Fix #286: plugin MCP servers join the config lookup/merge#318
ericleepi314 wants to merge 1 commit into
fix/issue-285-cache-state-latchesfrom
fix/issue-286-plugin-mcp-server-config

Conversation

@ericleepi314

Copy link
Copy Markdown
Collaborator

Closes #286

Stacked on #317 (deep stack down to #304). Merge in order; GitHub retargets automatically.

Summary

The plugin-scope loader (get_managed_mcp_configs) returned {} unconditionally — any MCP server shipped by a plugin was invisible to the config aggregation layer: no per-name lookup, no merge-driven /mcp listings, no scope-policy filtering.

  • McpPluginWrapper.server_config (the issue's fix sketch): set at registration via the new wrap_mcp_server_as_plugin(..., server_config=...) kwarg. None (all existing callers) keeps legacy tools-only registrations invisible — bit-identical behavior. LoadedPlugin.mcp_servers now reflects the real transport type instead of hardcoded stdio.
  • The loader reads the registry: wrappers with a config surface as ScopedMcpServerConfig(scope="managed", plugin_source=<plugin name>). Scope "managed" (not the sketch's "plugin") matches the existing ConfigScope and the allow_managed_only_mcp policy tuple — the consumption side (aggregator merge at lowest precedence, by-name lookup, policy filter) was already wired and engages immediately.
  • dedup_plugin_mcp_servers wired in (existed with unit tests, zero callers): a plugin server whose launch signature duplicates an enabled manual entry under a different name is suppressed with a warning notice. Disabled manual twins don't suppress (the claudeai-dedup carve-out — otherwise disabling the manual copy would leave zero working servers; critic-found).
  • By-name enterprise lockdown (critic-found, fixed for all six scopes at once): with a managed-mcp.json present, get_mcp_config_by_name now restricts to enterprise scope, matching the aggregate — previously it was a side door handing reconnect/OAuth flows configs the merge excluded (newly reachable for managed; latent for dynamic since before).

Known follow-ups (out of scope, noted per review)

  • Manifest-declared mcp_servers parsed by src/plugins/loader.py never register McpPluginWrappers — those stay invisible until a loader→registry bridge lands.
  • By-name still doesn't run filter_mcp_servers_by_policy (pre-existing class, strictly narrower after this PR).
  • Plugin-vs-claudeai signature overlap is not deduped (manual-wins is the only implemented policy).

Test plan

  • 9 tests: registration surfaces in managed scope with attribution; legacy invisible; transport type reflected; aggregate + by-name resolution; manual same-name precedence; signature-dup suppressed with notice; disabled-twin carve-out; allow_managed_only_mcp keeps plugin servers; enterprise lockdown by-name. Hermetic fixture isolates CLAUDE_CONFIG_DIR/CLAUDE_MANAGED_CONFIG_DIR and clears the process-wide enterprise-exists latch (these are the suite's first end-to-end get_all_mcp_configs tests).
  • All 654 mcp/plugin tests pass
  • Full suite on the stack: 7897 passed, 0 failed, 5 skipped
  • Critic review loop: APPROVE after 1 revision round (the disabled-twin suppression bug, the by-name lockdown side door, and the test machine-dependence all came from it)

🤖 Generated with Claude Code

The plugin-scope loader returned {} unconditionally — plugin-provided
MCP servers were invisible to the config aggregation (no per-name
lookup, no merge-driven listings, no scope-policy filtering).

- McpPluginWrapper gains server_config (set at registration via the
  new wrap_mcp_server_as_plugin kwarg; None keeps legacy tools-only
  registrations invisible, unchanged)
- get_managed_mcp_configs reads the wrapper registry and returns
  managed-scope entries with plugin_source attribution; the
  consumption side (aggregator merge, by-name lookup,
  allow_managed_only_mcp policy) was already wired
- dedup_plugin_mcp_servers (previously unit-tested but never called)
  wired into the aggregator: a plugin server whose launch signature
  duplicates an ENABLED manual entry is suppressed with a notice
  (disabled manual twins don't suppress — the claudeai carve-out)
- get_mcp_config_by_name honors the enterprise-lockdown short-circuit
  (all scopes): with a managed-mcp.json present, by-name no longer
  hands out configs the merge excluded

Known follow-up: manifest-declared mcp_servers parsed by
src/plugins/loader.py never register wrappers, so those remain
invisible until a loader-to-registry bridge lands.

Closes #286

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant