Skip to content

Name MCP service client entries by their short name - #1160

Open
nachill-databricks wants to merge 8 commits into
databricks:mainfrom
nachill-databricks:achille/short-mcp-service-entry-names
Open

nachill-databricks wants to merge 8 commits into
databricks:mainfrom
nachill-databricks:achille/short-mcp-service-entry-names

Conversation

@nachill-databricks

@nachill-databricks nachill-databricks commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

UC MCP services are registered in coding-agent configs under the dashed full name (main-default-exa), which is long and noisy in agent UIs.

Change

A new UC MCP service gets its short leaf name (main.default.exa becomes exa, system.ai.github becomes github) only when that name is completely free:

  • no entry with that name exists in ug state, in managed entries, or in the Claude or Codex user config (any client, any URL);
  • no other service configured in the same run shares the leaf;
  • the native configs were readable (one that fails to parse counts as "taken").

Otherwise the entry gets the dashed full name, exactly as on main.

Existing entries are never renamed or removed. There is no in-place migration: existing dashed entries keep working as they are, and a service already registered (matched by URL, in ug state or managed) keeps its current name on every rerun. Only new registrations get the short name. URLs are unchanged.

The picker recognises a service registered under either name (short or dashed), so it does not offer it again.

Before / after

Real runs in a sandbox HOME against a live workspace; workspace hostname and catalog name replaced with and . These were taken with the earlier version of this PR; for a fresh registration of a free name the result is the same.

Claude Code /mcp, before (main):
before
After (this branch): the entry is named figma:
after

ug mcp add + claude mcp list, before:
before
After:
after

Testing

  • uv run pytest tests/test_mcp.py -q: 259 passed. New TestMcpServiceEntryNames drives ug mcp add --location end to end: free leaf gets the short name; leaf taken in ug state, by a managed entry, in the native Claude config, or in the native Codex config gets the dashed name; an unparseable native config gives dashed names; two services sharing a leaf both get dashed names; reruns leave existing short and dashed entries untouched; an already-managed service is skipped, not duplicated.
  • uvx --from rust-just just lint: ruff check, ruff format --check and ty check all pass.
  • Full suite: 4826 passed, 7 failed. The 7 failures are the known ones in tests/test_agents_init.py::TestInstallToolBinary::test_required_update_* and tests/test_e2e_user_agent.py, which also fail on main.

Not yet verified

  • A live ug mcp add after this simplification (the screenshots above predate it).
  • Native configs for clients other than Claude and Codex are not read, so a hand-added entry there with the same leaf is not seen.
  • The interactive picker flow end to end; it is covered by unit tests only.

This pull request and its description were written by Isaac.

Achille Negrier and others added 2 commits October 16, 2026 15:32
…name

Register UC MCP Services in coding-agent configs under the service leaf
name (`exa` for `main.default.exa`). A leaf that is shared by two services
being configured, or already used by a different server (other ug entries,
managed servers), falls back to the dashed full name (`main-default-exa`).
URLs are unchanged.

Existing dashed entries are recognised by URL and renamed to the leaf in
place on the next run (old entry removed from the agent, no duplicate),
merging clients when the legacy and new entry coexist.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@nachill-databricks
nachill-databricks marked this pull request as ready for review October 16, 2026 17:59
@sunishsheth2009

Copy link
Copy Markdown
Collaborator

/ug-review

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🔍 OpenCodeReview found 9 issue(s) in this PR.

  • ✅ Successfully posted inline: 8 comment(s)
  • ❌ Failed to post inline: 1 comment(s)

bug · high

📄 src/ucode/mcp.py (L2033-L2036)

⚠️ GitHub could not post this as an inline comment: Lines 2033-2036 could not be resolved (outside PR diff hunks)

The managed check only looks at the dashed name. Managed config now registers main.default.exa as exa, so main-default-exa isn't skipped. Normalize then sees the same URL, calls it no clash, and renames it to exa, which duplicates the managed entry. Can we match on the leaf or the URL here?

Comment thread src/ucode/mcp.py Outdated
Comment on lines +766 to +769
candidates = (name.rsplit(".", 1)[-1], _legacy_mcp_service_entry_name(name))
if any(c in (managed_names or set()) for c in candidates):
return None
if registered_as in known_names:
registered_as = next((c for c in candidates if c in known_names), None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
Matching on the leaf alone means other.schema.exa, or any unrelated server named exa, counts as already registered once main.default.exa is exa. Under mcp add it gets hidden; in replace mode it becomes a duplicate exa toggle. Can we check the URL as well?

Comment thread src/ucode/mcp.py Outdated
Comment on lines +1027 to +1029
entry_names = mcp_service_entry_names(names, workspace)
for full_name in sorted(set(names)):
entry_name = full_name.replace(".", "-")
if entry_name in seen:
continue
seen.add(entry_name)
entry_name = entry_names[full_name]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
what happens if the dev already has their own exa (hand-named, or other.schema.exa)? managed fallback now registers main.default.exa as exa too, overwriting it, and a later managed drop removes it. can we pass state["mcp_servers"] as existing_servers?

Comment thread src/ucode/mcp.py Outdated
Comment on lines 2146 to 2150
working_mcp_servers = _normalize_mcp_service_entry_names(
_union_missing(original_mcp_servers_for_location, working_mcp_servers),
workspace,
managed_servers,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
this runs after _drop_managed_servers, so a legacy main-default-exa brought back by the union gets renamed to exa, the same name as the managed entry (same URL means no clash). can we drop managed after normalizing?

Comment thread tests/test_mcp.py Outdated
# slack removed, gmail added, github untouched (entry unchanged).
assert removed == [("claude", "system-ai-slack")]
assert [c[1] for c in configured] == ["system-ai-gmail"]
assert sorted(removed) == [("claude", "system-ai-github"), ("claude", "system-ai-slack")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

test · low
the comment above still says "github untouched (entry unchanged)", but github is now removed and re-added. can we update it? also, can we cover the partial-failure case where configuring github fails after system-ai-github was removed?

Comment thread tests/test_mcp.py Outdated
Comment on lines +3255 to +3256
def test_leaf_held_by_another_server_falls_back(self):
other = {"name": "exa", "url": f"{WS}/api/2.0/mcp/external/exa"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
what happens if the user hand-added github in ~/.claude.json or codex config? clash check reads only ug state, and configure_client_mcp_server removes and re-adds by name, so their entry gets overwritten. can we check native configs too and add a test?

Comment thread tests/test_mcp.py Outdated
monkeypatch.setattr(
mcp,
"remove_client_mcp_server",
lambda client, name: removed.append((client, name)) or [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

test · low
can we use *a, **kw like the other stubs? if the call signature grows, this stub will raise TypeError.

Comment thread tests/test_mcp.py Outdated
assert mcp.add_mcp_command(location="main.default", services={"main.default.exa"}) == 0

names = sorted(s["name"] for s in saved_states[-1]["mcp_servers"])
assert names == ["main-default-exa", "ml-tools-exa"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

maintainability · medium
this pins down name churn: adding ml.tools.exa later renames an existing exa back to main-default-exa, which breaks names users/admins already reference. can a name stay put once it's assigned?

Comment thread tests/test_mcp.py
by_name = {s["name"]: s for s in out}
assert set(by_name) == {"system-ai-github", "system-ai-jira", "main-tools-foo"}
github = by_name["system-ai-github"]
assert set(by_name) == {"github", "jira", "foo"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
_resolve_managed_mcp_servers names by leaf without checking the developer's ug mcp_servers. what happens if they already have a github (e.g. an external connection)? on the user-scope fallback, managed github overwrites theirs. can we add a clash check and a test for it?

@nachill-databricks

Copy link
Copy Markdown
Contributor Author

/ug-review

Comment thread src/ucode/agents/codex.py Outdated
Comment on lines +1006 to +1012
args = entry.get("args")
if isinstance(entry.get("url"), str):
urls[name] = str(entry["url"])
elif isinstance(args, list) and "--url" in args and args.index("--url") + 1 < len(args):
urls[name] = str(args[args.index("--url") + 1])
else:
urls[name] = ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

maintainability · low
can we pull this url/--url extraction into one shared helper? it's copied verbatim in claude.py, and read_managed_mcp_urls above already parses --url its own way, so the copies can drift apart.

Suggestion:

Suggested change
args = entry.get("args")
if isinstance(entry.get("url"), str):
urls[name] = str(entry["url"])
elif isinstance(args, list) and "--url" in args and args.index("--url") + 1 < len(args):
urls[name] = str(args[args.index("--url") + 1])
else:
urls[name] = ""
urls[name] = _mcp_entry_url(entry) # shared helper: entry url, else value after --url, else ""

Comment thread src/ucode/mcp.py Outdated
Comment on lines +604 to +606
url_by_name = {
name: server.get("url") for server in existing_servers if (name := _server_name(server))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
what happens if two holders share a name? _native_mcp_servers returns github for both claude (hand URL) and codex (this service URL). The last one wins, so owns_leaf is True and ug overwrites the hand-added Claude github. can we treat it as a clash if any holder's URL differs?

Comment thread src/ucode/mcp.py Outdated
push(_mcp_service_choice(name, known_names, additive, managed))
displayed_names.add(name.replace(".", "-"))
push(_mcp_service_choice(name, known_names, additive, managed, entry_urls))
displayed_names.update((name.rsplit(".", 1)[-1], _legacy_mcp_service_entry_name(name)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
what happens if ug state has an unrelated exa (e.g. other.schema.exa, or a hand-named one) and main.default.exa is listed? _mcp_service_choice shows an add row because the URLs don't match, but exa still lands in displayed_names. Its fallback toggle at L934 is then skipped, and in replace mode the unshown entry counts as unselected, so the user's server gets removed. can we only mark the candidate this service actually holds, e.g. return registered_as from the helper?

Comment thread src/ucode/mcp.py Outdated
# still discovers, since a whole-schema pointer has no explicit set to register.
working: list[dict] = []
seen: set[str] = set()
entry_names = mcp_service_entry_names(names, workspace, existing_servers=existing_servers)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
what happens to a user whose mcp_servers still has the legacy system-ai-github (same URL as the managed entry)? Before, the managed entry shared that name and took its place. Now reconcile registers the managed github next to it, so the service runs twice until they rerun ug configure mcp. can reconcile reuse the name of a same-URL user entry, or drop that entry?

Comment thread src/ucode/mcp.py Outdated
Comment on lines +1917 to +1919
original = original_by_name.get(entry_name) or original_by_name.get(
_legacy_mcp_service_entry_name(full_name)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
can we check the URL before adopting original? In replace mode, holders skip service entries outside the location. So state exa → ml.tools.exa lets main.default.exa take exa, and this inherits the unrelated entry's clients, registering it for agents the user never picked.

Suggestion:

Suggested change
original = original_by_name.get(entry_name) or original_by_name.get(
_legacy_mcp_service_entry_name(full_name)
)
legacy = original_by_name.get(_legacy_mcp_service_entry_name(full_name))
original = original_by_name.get(entry_name)
if original is not None and original.get("url") != build_mcp_service_url(workspace, full_name):
original = None
original = original or legacy

@github-actions

Copy link
Copy Markdown

Polly AI Review

Blocking issues

  • [P1] Preserve conflicting entries across clients — src/ucode/mcp.py:604
    can we retain all holders? Codex’s matching exa replaces Claude’s foreign URL in this lookup, so configuring both overwrites Claude’s unrelated server.

  • [P1] Protect clients without native-config readers — src/ucode/mcp.py:629
    can we use dashed names where occupancy cannot be read? Cursor’s existing github is invisible here, so configuring system.ai.github overwrites it.

  • [P1] Preserve non-target clients during migration — src/ucode/mcp.py:2234
    can scoped migration preserve other clients? With legacy state covering Claude and Codex, --agents claude removes both registrations but restores only Claude, while state still claims both.

  • [P1] Keep unrelated entries selectable — src/ucode/mcp.py:854
    can we mark a leaf displayed only when its existing entry has a toggle? Discovering system.ai.github hides an unrelated tracked github; confirming replace then deletes it.

  • [P2] Reserve fallback names during batch allocation — src/ucode/mcp.py:617
    can we reserve assigned names? With foreign exa, services main.default.exa and main.default.main-default-exa both become main-default-exa; one registration is lost.

Security vulnerabilities

The first two blockers weaken configuration ownership by overwriting unrelated native entries. No separate injection, authentication bypass, credential exposure, or unsafe-deserialization issue was established.

Non-blocking notes

  • tests/test_mcp.py:3422: can we parameterize test_location_leaves_a_developers_native_entry_alone with test_location_takes_the_leaf_when_the_native_entry_is_the_same_service?
  • src/ucode/mcp.py:591: can we shorten this docstring to “Use service leaves unless ambiguity or existing ownership requires a dashed name”? The upgrade explanation repeats the implementation.

Approach

Central naming allocation is appropriate, but it needs one batch plan that preserves client-specific ownership, reserves both leaf and fallback names, and merges entries only when their service URLs match.

Summary

Diff size (computed): 5 files; +771 / -64 lines (835 changed text lines); 0 binary files.

Category Files Added Deleted Share of changed text lines
Tests and test support 2 442 32 56.8%
Documentation 0 0 0 0.0%
Dependencies and lockfiles 0 0 0 0.0%
Implementation / other 3 329 32 43.2%

Shares use additions + deletions, including generated text and lockfiles. Binary files have no line count. Tests include fixtures/helpers under test paths and colocated test/spec files; categories are path-based, not coverage measurements.

Changes

The change introduces short service names, collision fallback, legacy migration, managed-entry handling, and Claude/Codex native-config readers.

The migration and ownership requirements justify more than a simple naming substitution. Most implementation work supports those requirements. A single allocation plan would reduce repeated ownership checks and prevent the conflicting decisions identified above. The shared-fixture changes support isolation of the new filesystem reads. No documentation, dependency, or generated-file changes are included.

Tests

Test-by-test assessment

The frozen snapshot checksum was verified, and both trusted review-policy files were read in full. Focused offline replays of reconstructed changed functions supported the five blockers; external effects were stubbed. These were not complete CLI executions. No pytest cases or lint gates were independently executed, so “253 passed” remains author-reported.

Execution limitation: dependency probes inadvertently used uv run without offline mode. No successful outbound request was established, but attempted connections cannot be excluded.

“Component” below means in-process configuration orchestration with registration effects stubbed.

Test / case Behavior protected Layer Needed? Action / rationale
tests/conftest.py::_isolate_ucode_state Isolates native Claude/Codex reads and clears config-path overrides Fixture keep Both path patches and environment removals prevent reading real user configuration.
tests/test_mcp.py — added json import Constructs native Claude configuration input Test support keep Used by the reader test.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_registers_every_discovered_service Registers discovered services using leaf names Component keep Updated expectations preserve whole-location coverage.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_replaces_servers_outside_location Removes outgoing entries during replace Component keep Protects a destructive transition distinct from append.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_existing_entry_gets_reconfigured_for_newly_added_clients Reconfigures an existing service for additional clients Component keep Existing client-expansion behavior still needs coverage during migration.
tests/test_mcp.py::TestAddMcpCommand::test_keeps_servers_outside_location Append preserves entries outside the selected location Component keep Protects append rather than replace semantics.
tests/test_mcp.py::TestAddMcpCommand::test_services_subset_keeps_others_in_location Subset append retains other services while migrating names Component keep Revised removal expectation reflects intentional migration.
tests/test_mcp.py::TestAddMcpCommand::test_agents_scopes_registration_to_named_agent Restricts fresh registration to selected agents Component keep Useful existing scope coverage, but does not exercise multi-client legacy state.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_configures_only_the_requested_subset Selects services by full name Component keep Protects explicit subset selection.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_matches_bare_short_names Accepts bare service names Component keep Covers a distinct supported input form.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_unknown_requested_service_warns_and_skips Warns on unknown selections without dropping valid ones Component keep Protects recoverable input failure.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_adds_and_removes_to_match_new_selection Applies selection changes and retained-service migration Component keep Updated assertions preserve removal/addition coverage.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_failed_rename_is_not_saved_and_the_next_run_recovers Leaves state unsaved after failure and retries successfully Component keep Meaningful failure/recovery coverage; it establishes retry, not rollback.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_full_names_without_location_derive_the_schema Derives location from fully qualified service names Component keep Protects separate CLI input interpretation.
tests/test_mcp.py::TestMcpServiceEntryNames::test_unique_leaf_name_is_used Shortens an unambiguous service name Unit keep Minimal allocation happy path.
tests/test_mcp.py::TestMcpServiceEntryNames::test_colliding_leaf_names_fall_back_to_dashed_full_names Falls back when different services share a leaf Unit keep Direct ambiguity contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_leaf_held_by_another_server_falls_back Preserves a foreign URL occupying the leaf Unit keep Direct existing-ownership contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_existing_entry_for_the_same_service_is_not_a_clash Allows a matching service to retain its leaf Unit keep Opposite identity decision from foreign occupancy.
tests/test_mcp.py::TestMcpServiceEntryNames::test_normalize_upgrades_legacy_entry_in_place_without_duplicating Migrates names, preserves other entries, and merges clients Unit keep Direct migration and merge contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_rerun_renames_legacy_entry_to_leaf Removes the legacy registration and registers its replacement Component keep Exercises migration through location orchestration.
tests/test_mcp.py::TestMcpServiceEntryNames::test_assigned_leaf_name_is_sticky_when_a_colliding_service_arrives Preserves an incumbent leaf assignment Unit keep Distinct stateful allocation rule.
tests/test_mcp.py::TestMcpServiceEntryNames::test_add_keeps_the_existing_leaf_and_dashes_the_newcomer Applies incumbent ownership during append Component keep Checks union and registration wiring beyond the allocator.
tests/test_mcp.py::TestMcpServiceEntryNames::test_unrelated_entry_with_the_leaf_name_is_not_this_service Treats another service URL and a URL-less entry as foreign holders Unit keep Covers identity and missing-URL boundaries.
tests/test_mcp.py::TestMcpServiceEntryNames::test_normalize_keeps_a_leaf_held_by_a_native_entry_for_another_server Passes native occupancy into normalization Unit keep Protects normalizer wiring rather than allocation alone.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_leaves_a_developers_native_entry_alone Uses the fallback when a native foreign entry owns the leaf Component consolidate Combine with the matching-service case below; retain both expected names.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_takes_the_leaf_when_the_native_entry_is_the_same_service Allows the leaf when native URL identity matches Component consolidate Same setup and boundary as the preceding case; parameterization preserves both decisions.
tests/test_mcp.py::TestMcpServiceEntryNames::test_native_servers_skip_ug_owned_names_and_read_both_clients Dispatches readers and filters UG-owned names Unit keep Covers holder assembly, but current name-only filtering misses client-specific conflicts.
tests/test_mcp.py::TestMcpServiceEntryNames::test_picker_ignores_an_unrelated_entry_with_the_leaf_name Distinguishes matching and foreign entries inside the choice helper Unit keep Useful helper coverage; does not establish preservation by the enclosing picker builder.
tests/test_mcp.py::TestMcpServiceEntryNames::test_drop_managed_matches_a_dashed_entry_by_url Identifies managed duplicates by URL despite legacy naming Unit keep Direct URL-based suppression contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_legacy_entry_renamed_onto_a_managed_name_is_dropped_not_duplicated Prevents append union from resurrecting a managed duplicate Component keep Protects ordering across union, normalization, and managed suppression.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_claude_reads_http_and_proxy_urls Parses HTTP, proxy, and ordinary stdio Claude entries Unit/file I/O keep Exercises the real JSON representation.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_codex_reads_http_and_proxy_urls Parses HTTP, proxy, and ordinary stdio Codex entries Unit/file I/O keep TOML parsing is a separate adapter contract.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_missing_or_unparseable_configs_read_as_empty Handles missing configurations and malformed Claude JSON Unit/file I/O keep Protects graceful degradation in the exercised cases.
tests/test_mcp.py::TestResolveManagedMcpServers::test_location_registers_the_whole_schema Delegates whole-schema managed resolution Unit keep Updated mock signature preserves existing selector coverage.
tests/test_mcp.py::TestResolveManagedMcpServers::test_names_build_entries_directly_without_discovery Builds explicitly named managed entries without discovery Unit keep Protects URL, authentication, client, and naming construction.
tests/test_mcp.py::TestResolveManagedMcpServers::test_duplicate_names_collapse_to_one_entry Deduplicates repeated identical fully qualified names Unit keep Distinct from different services receiving the same allocated name.
tests/test_mcp.py::TestResolveManagedMcpServers::test_names_avoid_entries_the_developer_already_has Respects existing holders during explicit managed resolution Unit keep Covers the separate managed-name construction path.
tests/test_mcp.py::TestResolveManagedMcpServers::test_location_selector_passes_the_developers_entries_for_clash_checks Forwards ownership information into location resolution Unit keep Protects the newly introduced ownership argument.
tests/test_mcp.py::TestResolveManagedMcpServers::test_malformed_names_are_skipped_with_a_warning Skips invalid admin names while processing valid ones Unit keep Protects graceful degradation.
tests/test_mcp.py::TestReconcileManagedMcpServers::_wire Accepts the resolver’s new holder argument Component support keep Necessary mock-signature adaptation.
tests/test_mcp.py::TestReconcileManagedMcpServers::test_location_config_registers Registers managed location configuration through reconciliation Component keep Exercises a different boundary from direct resolver delegation.
tests/test_mcp.py::TestReconcileManagedMcpServers::test_reconcile_names_managed_servers_around_the_developers_entries Supplies developer/native holders while excluding UG-owned entries Component keep Protects upstream ownership-information assembly.
tests/test_mcp.py::TestManagedServersAreNotReAdded::test_location_add_skips_an_already_managed_service Skips managed services and reports the skip Component keep Preserves user feedback and unmanaged-service registration assertions.

No test functions were removed. Existing coverage remains useful, but it does not catch the demonstrated blockers. The smallest suitable regression additions are:

  • Extend TestAddMcpCommand::test_agents_scopes_registration_to_named_agent with a legacy entry shared by two clients.
  • Extend TestMcpServiceEntryNames::test_picker_ignores_an_unrelated_entry_with_the_leaf_name through build_mcp_picker_choices, asserting that the unrelated entry retains its keep-toggle.
  • Extend TestMcpServiceEntryNames::test_native_servers_skip_ug_owned_names_and_read_both_clients with conflicting URLs and ownership limited to one client.
  • Add an allocator case beside test_colliding_leaf_names_fall_back_to_dashed_full_names where one fallback equals another service’s leaf.
  • Exercise the unsupported-reader policy with an existing Cursor or OpenCode entry at the adapter/component boundary.

These checks belong at unit/component layers; another E2E naming matrix is unnecessary.

For manual confirmation in disposable configuration, register one legacy service in Claude and Codex, rerun configuration scoped only to Claude, and inspect both agents’ MCP lists. Codex must retain a working registration. Also confirm that configuring a service cannot replace an unrelated Cursor entry with the same leaf.

Scope

All changed files support short naming, migration, ownership checks, or their test isolation. No clearly unrelated changes were found. The blockers concern correctness within the stated outcome, not scope expansion.


Automated review by Polly · workflow run

Achille Negrier and others added 2 commits October 17, 2026 13:30
@nachill-databricks

Copy link
Copy Markdown
Contributor Author

/ug-review

Comment thread src/ucode/agents/claude.py Outdated
Comment on lines +1084 to +1085
config = _read_claude_config_for_rewrite(claude_mcp_config_path())
servers = (config or {}).get("mcpServers")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
what happens if ~/.claude.json doesn't parse? we return {} so a dev's github doesn't count as a clash, and the CLI fallback in write_user_mcp_servers can overwrite it. can we treat unreadable as unknown and fall back to the legacy name?

Comment thread src/ucode/agents/codex.py Outdated
Comment on lines +997 to +998
doc = _read_user_config_for_rewrite(user_mcp_config_path())
servers = doc.get(MANAGED_MCP_CONFIG_KEY) if doc is not None else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
same as claude: an unparseable config.toml gives {}, so ug can take a dev's server name and the codex mcp add fallback overwrites it. can we tell the caller the file was unreadable?

Comment thread src/ucode/mcp.py Outdated
``ug_servers`` are skipped: ug's own, which it may rename or remove. What's left, a developer's
hand-added `github`, must not be overwritten by a ug entry of the same name. Entries without a
readable URL carry none, so they never match a service URL."""
ug_names = {name for server in ug_servers if (name := _server_name(server))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
what happens if ug's exa is registered only for claude and codex has a hand-added exa? the name-only skip hides the codex one, so the next run with codex overwrites it. can we skip only per client (server["clients"])?

Suggestion:

Suggested change
ug_names = {name for server in ug_servers if (name := _server_name(server))}
ug_names_by_client = {
client: {n for s in ug_servers if (n := _server_name(s)) and client in (s.get("clients") or [])}
for client in clients
}

Comment thread src/ucode/mcp.py Outdated
Comment on lines +2275 to +2278
# After normalizing: a legacy dashed entry the union brought back may now carry a managed name.
working_mcp_servers = _drop_managed_servers(
working_mcp_servers, managed_names, managed_urls
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
under mcp add, the drop now runs after the union, so an original entry whose name or URL matches a managed one gets unregistered, and removed is forced to [] so the summary doesn't show it. that breaks "add never removes". can we drop only renamed or new entries? same thing happens at the other two call sites.

Comment thread tests/test_mcp.py Outdated
Comment on lines +2283 to +2286
with pytest.raises(RuntimeError, match="claude mcp add failed"):
mcp.configure_mcp_command(location="system.ai")
assert removed == [("claude", "system-ai-github")]
assert saved_states == []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
this locks in remove-before-add: if the add fails during upgrade, the user has neither system-ai-github nor github until a manual rerun. can we add first (or roll back) and assert the legacy entry survives?

Comment thread tests/test_mcp.py Outdated
assert state["mcp_servers"] == []


class TestMcpServiceEntryNames:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

maintainability · low
are all these tests necessary? many of them call private helpers (_normalize_*, _mcp_service_choice, _drop_managed_servers) and overlap on clash cases. can we cut them down to a few end-to-end configure/add cases?

Comment thread tests/test_mcp.py Outdated
Comment on lines +3458 to +3462
assert mcp.configure_mcp_command(location="system.ai") == 0

assert configured == ["github"]

def test_native_servers_skip_ug_owned_names_and_read_both_clients(self, monkeypatch):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
this has ug take over a hand-added native github (same URL) and save it to ug state. a later mcp removal then deletes the dev's entry. can we test that, or leave it unowned?

Comment thread tests/test_mcp.py Outdated
)
assert [s["name"] for s in out] == ["github"]

def test_location_selector_passes_the_developers_entries_for_clash_checks(self, monkeypatch):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

test · medium
what happens to managed entries an older ug registered as system-ai-* under a location selector? they now resolve to the leaf. can we add a reconcile test that the dashed managed entry is removed and not duplicated?

@github-actions

Copy link
Copy Markdown

Polly AI Review

Blocking issues

  1. Untargeted clients lose registrations — src/ucode/mcp.py:1947. can we migrate all old clients? A Claude-only rename removes the legacy entry from Claude and Codex, adds only Claude, but saves both in state.

  2. Native servers are overwritten — src/ucode/mcp.py:645. can we check every targeted adapter? Cursor, Gemini, OpenCode, Kilo and Copilot are skipped; a native github is replaced where the old dashed name was safe.

  3. Ownership crosses client boundaries — src/ucode/mcp.py:644. can ownership match client and URL? UG’s Claude exa hides an unrelated native Codex exa from collision checks; adding Codex then replaces it.

  4. Managed upgrades retain dashed names — src/ucode/mcp.py:1107. can previous managed entries migrate too? Reusing their same-URL names preserves system-ai-github indefinitely, contrary to the stated upgrade behavior.

Security vulnerabilities

No additional demonstrated security vulnerabilities found. The ownership failures above cause configuration loss; no credential redirection or disclosure was established. This assessment is static, not live security validation.

Non-blocking notes

  • Final-name collisions — src/ucode/mcp.py:633: are hyphenated leaves supported? Locally accepted e.f.a-b-exa collides with a.b.exa’s fallback when c.d.exa is also selected; one registration wins.
  • Coexistence contract — src/ucode/mcp.py:1954: should persisted legacy/leaf pairs merge too? Add a location-replacement case: this branch selects one client list. No supported producer of that state was found.
  • Test consolidation: can test_leaf_held_by_another_server_falls_back and test_unrelated_entry_with_the_leaf_name_is_not_this_service share a collision matrix? Keep the distinct multiple-holder case.
  • Comment conciseness — src/ucode/mcp.py:592: can the naming docstring omit repeated examples? Suggested replacement: “Choose leaf names while preserving ownership and falling back on collisions.”

Approach

Central naming and shared URL extraction fit the existing module boundaries. A safer implementation would build one URL-keyed migration plan, reserve final names, and track ownership per client before applying changes. Reuse _merge_clients rather than adding another client-union expression.

The repeated name-to-URL construction at src/ucode/mcp.py:2315, src/ucode/mcp.py:2330, and inside build_mcp_picker_choices could also share one computed map.

Summary

Diff size (computed): 6 files; +870 / -68 lines (938 changed text lines); 0 binary files.

Category Files Added Deleted Share of changed text lines
Tests and test support 2 499 32 56.6%
Documentation 0 0 0 0.0%
Dependencies and lockfiles 0 0 0 0.0%
Implementation / other 4 371 36 43.4%

Shares use additions + deletions, including generated text and lockfiles. Binary files have no line count. Tests include fixtures/helpers under test paths and colocated test/spec files; categories are path-based, not coverage measurements.

Changes

The change introduces short service-entry names, native Claude/Codex config readers, collision handling, legacy migration, and URL-aware managed/picker reconciliation. It also adjusts test isolation and adds naming, migration, reader, and reconciliation coverage. No documentation, dependency, or generated-file changes are included.

The breadth largely follows from the stateful migration requirement, rather than unrelated work. Concrete reductions are sharing the repeated URL-map construction and consolidating the overlapping single-holder collision scenarios. The blocking migration and ownership defects need resolution before approval.

Tests

Test-by-test assessment

Both trusted policy files and the frozen diff were read in full; the supplied checksum matched. Head-line references come from the frozen diff, with surrounding context checked against the trusted base checkout.

No tests, lint, typecheck, live workspace calls, or PR-authored code were executed. The reported 253 passing tests and lint result are author-provided evidence.

Test / case Behavior protected Layer Needed? Action / rationale
tests/conftest.py::_isolate_ucode_state Native config isolation Fixture keep Temporary Claude/Codex paths and cleared path overrides prevent reading developer configuration.
tests/test_mcp.py::TestReconcileManagedMcpServers::_wire Reconciliation setup Helper keep Stub signature adjustment supports the new existing-server argument without new scaffolding.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_registers_every_discovered_service Discovery produces short registrations with unchanged URLs Component keep Existing public-command scenario covers the primary location workflow.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_replaces_servers_outside_location Strict replacement Component keep Preserves removal assertions while updating expected names.
tests/test_mcp.py::TestConfigureMcpFromLocation::test_existing_entry_gets_reconfigured_for_newly_added_clients Registration for newly selected clients Component keep Distinct transition; removal stub accommodates migration.
tests/test_mcp.py::TestAddMcpCommand::test_keeps_servers_outside_location Append preserves unrelated locations Component keep Protects append semantics under short naming.
tests/test_mcp.py::TestAddMcpCommand::test_services_subset_keeps_others_in_location Append preserves and migrates retained siblings Component keep Updated removal assertion captures the migration side effect.
tests/test_mcp.py::TestAddMcpCommand::test_agents_scopes_registration_to_named_agent Explicit agent selection Component keep Useful existing boundary, but its starting state does not expose blocker 1.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_configures_only_the_requested_subset Requested-service filtering Component keep Updated expectations retain distinct selection coverage.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_matches_bare_short_names Short-name input matching Component keep Input selection is distinct from generated entry naming.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_unknown_requested_service_warns_and_skips Partial invalid input Component keep Preserves warning-and-continue behavior.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_adds_and_removes_to_match_new_selection Mixed addition, removal, and legacy migration Component keep Covers a meaningful combined state transition.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_failed_rename_is_not_saved_and_the_next_run_recovers Failure after removal and subsequent retry Component keep Distinct failure boundary; checks unsaved state and recovery, not rollback.
tests/test_mcp.py::TestConfigureMcpServicesSubset::test_full_names_without_location_derive_the_schema Standalone fully qualified input Component keep Protects schema derivation through the command function.
tests/test_mcp.py::TestMcpServiceEntryNames::test_unique_leaf_name_is_used Default leaf naming Unit keep Minimal happy-path contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_colliding_leaf_names_fall_back_to_dashed_full_names Duplicate-leaf fallback Unit keep Protects the stated collision rule; final-name collisions remain a separate boundary.
tests/test_mcp.py::TestMcpServiceEntryNames::test_leaf_held_by_another_server_falls_back Occupied leaf Unit consolidate Share a single-holder collision matrix with the unrelated-entry case below.
tests/test_mcp.py::TestMcpServiceEntryNames::test_existing_entry_for_the_same_service_is_not_a_clash Same-URL reuse Unit keep Distinguishes legitimate reuse from another server’s ownership.
tests/test_mcp.py::TestMcpServiceEntryNames::test_normalize_upgrades_legacy_entry_in_place_without_duplicating Rename, client union, order, and unrelated-entry preservation Unit keep Directly protects the normalizer’s migration contract.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_rerun_renames_legacy_entry_to_leaf Successful location migration Component keep Focused upgrade scenario makes migration failures easy to diagnose.
tests/test_mcp.py::TestMcpServiceEntryNames::test_assigned_leaf_name_is_sticky_when_a_colliding_service_arrives Existing leaf ownership Unit keep Protects stability across successive configurations rather than a fresh collision alone.
tests/test_mcp.py::TestMcpServiceEntryNames::test_add_keeps_the_existing_leaf_and_dashes_the_newcomer Sticky ownership through append Component keep Checks command-level behavior and absence of unnecessary removal.
tests/test_mcp.py::TestMcpServiceEntryNames::test_unrelated_entry_with_the_leaf_name_is_not_this_service Different-URL and URL-less holders Unit consolidate Add these inputs to the single-holder collision matrix without losing assertions.
tests/test_mcp.py::TestMcpServiceEntryNames::test_normalize_keeps_a_leaf_held_by_a_native_entry_for_another_server Native-holder forwarding into normalization Unit keep Covers a separate normalizer input boundary.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_leaves_a_developers_native_entry_alone Native collision through location configuration Component keep Establishes Claude user-config protection, but not the omitted adapters in blocker 2.
tests/test_mcp.py::TestMcpServiceEntryNames::test_location_takes_the_leaf_when_the_native_entry_is_the_same_service Same-service native reuse Component keep Complements different-server preservation with legitimate reuse.
tests/test_mcp.py::TestMcpServiceEntryNames::test_native_servers_skip_ug_owned_names_and_read_both_clients Native enumeration and ownership filtering Unit keep Relevant boundary, but its current name-only ownership expectation misses blocker 3.
tests/test_mcp.py::TestMcpServiceEntryNames::test_picker_ignores_an_unrelated_entry_with_the_leaf_name URL-aware picker classification Unit keep Covers own/managed classification and add/replace behavior at the smallest layer.
tests/test_mcp.py::TestMcpServiceEntryNames::test_any_holder_with_a_different_url_makes_the_leaf_a_clash Multiple holders and order independence Unit keep Distinct from the single-holder cases; should not be consolidated away.
tests/test_mcp.py::TestMcpServiceEntryNames::test_picker_keeps_an_unrelated_leaf_entry_as_its_own_toggle Unrelated picker-entry preservation Component keep Exercises assembled choices rather than only classification.
tests/test_mcp.py::TestMcpServiceEntryNames::test_picker_does_not_repeat_a_service_that_holds_its_leaf Picker deduplication Component keep Protects the changed displayed-name bookkeeping.
tests/test_mcp.py::TestMcpServiceEntryNames::test_managed_entry_reuses_the_name_of_a_same_url_user_entry Managed adoption of a user registration Unit keep Distinct from upgrading a previous managed registration; does not cover blocker 4.
tests/test_mcp.py::TestMcpServiceEntryNames::test_reconfigure_does_not_inherit_clients_of_an_unrelated_entry URL-aware client inheritance Unit keep Prevents transferring clients between unrelated services.
tests/test_mcp.py::TestMcpServiceEntryNames::test_drop_managed_matches_a_dashed_entry_by_url Managed deduplication across names Unit keep Direct coverage of the added URL-matching behavior.
tests/test_mcp.py::TestMcpServiceEntryNames::test_legacy_entry_renamed_onto_a_managed_name_is_dropped_not_duplicated Append, normalization, and managed exclusion ordering Component keep Protects an interaction not established by the isolated helpers.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_claude_reads_http_and_proxy_urls Claude HTTP, proxy, and URL-less entries File component keep Exercises real JSON parsing and shared URL extraction.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_codex_reads_http_and_proxy_urls Codex HTTP, proxy, and URL-less entries File component keep Exercises the separate TOML parsing boundary.
tests/test_mcp.py::TestNativeMcpConfigReaders::test_missing_or_unparseable_configs_read_as_empty Missing or malformed optional configuration File component keep Establishes missing-file handling and malformed Claude JSON; malformed Codex TOML is not demonstrated here.
tests/test_mcp.py::TestResolveManagedMcpServers::test_location_registers_the_whole_schema Managed location delegation Unit keep Inline fake signature follows the new collision-input argument.
tests/test_mcp.py::TestResolveManagedMcpServers::test_names_build_entries_directly_without_discovery Explicit managed service resolution Unit keep Checks names, URLs, authentication mode, clients, and discovery avoidance.
tests/test_mcp.py::TestResolveManagedMcpServers::test_duplicate_names_collapse_to_one_entry Repeated identical service input Unit keep Distinct from different services sharing a leaf.
tests/test_mcp.py::TestResolveManagedMcpServers::test_names_avoid_entries_the_developer_already_has Managed collision avoidance Unit keep Exercises collision and reuse at the managed resolver boundary.
tests/test_mcp.py::TestResolveManagedMcpServers::test_location_selector_passes_the_developers_entries_for_clash_checks Holder forwarding through the selector branch Unit keep Protects the newly added argument on a distinct branch.
tests/test_mcp.py::TestResolveManagedMcpServers::test_malformed_names_are_skipped_with_a_warning Invalid admin entry alongside valid input Unit keep Updated valid-name expectation retains graceful partial success.
tests/test_mcp.py::TestReconcileManagedMcpServers::test_location_config_registers Managed location orchestration Component keep Stub compatibility adjustment preserves an existing scenario.
tests/test_mcp.py::TestReconcileManagedMcpServers::test_reconcile_names_managed_servers_around_the_developers_entries State/native holder collection Component keep Relevant orchestration coverage, but its mismatched-native ownership expectation misses blocker 3.
tests/test_mcp.py::TestManagedServersAreNotReAdded::test_location_add_skips_an_already_managed_service Managed exclusion and reporting across names Component keep Retains legacy managed-name compatibility while updating unmanaged names.

The inventory accounts for added and modified tests, the changed fixture, and the changed reconciliation helper. No test functions were deleted.

Existing coverage establishes ordinary naming, normalization, picker classification, and failed-rename recovery. It does not suffice for the four blocking transitions. The smallest useful regressions are:

  • A command-level scoped migration asserting the actual removals/additions for both clients.
  • A file-component collision case using an omitted adapter such as Cursor.
  • Cross-client ownership with UG’s Claude entry and an unrelated Codex entry.
  • Reconciliation starting with a previous dashed managed entry.

These do not require new live E2Es. Final-name uniqueness and persisted coexistence remain conditional boundaries described in the notes. The changed coverage also does not demonstrate malformed Codex TOML or the native-HTTP branch of the existing managed Codex reader.

After fixes, run uv run pytest tests/test_mcp.py tests/test_agent_claude.py tests/test_agent_codex.py and just lint. In disposable client homes, confirm that unrelated native entries survive, a Claude-only migration does not leave Codex unregistered, and a previous managed dashed entry upgrades without duplication.

Scope

All six changed files support the stated naming, collision-avoidance, or migration outcome. Native readers, URL-aware managed exclusion, and test isolation are necessary supporting changes, not independent features. No clearly unrelated changes were found. The unresolved questions concern supported naming inputs and the extent of the coexistence guarantee, not unrelated scope.


Automated review by Polly · workflow run

… entries

Co-authored-by: Isaac <no-reply@databricks.com>
@nachill-databricks

Copy link
Copy Markdown
Contributor Author

/ug-review

Comment thread src/ucode/mcp.py Outdated
)
working = _resolve_managed_mcp_servers(selector, workspace, profile, clients)
working = _resolve_managed_mcp_servers(
selector, workspace, profile, clients, [*(state.get("mcp_servers") or []), *previous]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · high
previous holds only the fallback entries (working_fallback). Entries already in the managed file (e.g. system-ai-github) aren't in it, so on upgrade they get renamed to github. Also, because personal entries come first, a personal entry's name wins over the managed one for the same URL. Can we pass managed_mcp_servers(state) and put it first?

Comment thread src/ucode/mcp.py
Comment on lines +2190 to +2191
service_names = _mcp_service_selection_names(
[s.removeprefix(MCP_ADD_PREFIX) for s in selections if s.startswith(MCP_ADD_PREFIX)],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
what happens if a connection github (registered under the bare name github) is picked with system.ai.github in the same run? Both resolve to github, and the working_names dedup silently drops the second one. Can the leaf check also cover the other selections' entry names?

Co-authored-by: Isaac <no-reply@databricks.com>
@github-actions

Copy link
Copy Markdown

Polly AI Review

Blocking issues

  1. Existing OS-managed entries are renamed. src/ucode/mcp.py:1175
    can we include OS-managed registrations in existing_servers? successful delivery empties fallback state, so rerunning configure replaces system-ai-github with github, breaking existing tool IDs.

  2. Other clients’ managed names are not reserved. src/ucode/mcp.py:2107
    can we reserve managed names across all clients? a Codex-managed exa is missed during Claude-only add; later configure treats Claude’s unrelated exa as managed and removes it.

Security vulnerabilities

No introduced security vulnerabilities found.

Non-blocking notes

  • tests/test_mcp.py::TestMcpServiceEntryNames: can we move reservation matrices to allocator tests and consolidate the duplicate free-name, managed-skip, managed-location, and picker scenarios?
  • tests/test_mcp.py::TestMcpServiceEntryNames.test_rerun_never_renames_existing_entries: can we assert exact preserved entries and no removals? the configured-name subset assertion also passes when empty.
  • tests/test_mcp.py::TestMcpServiceEntryNames.test_unreadable_native_config_makes_every_name_dashed: can we cover Codex parse failure at the allocator/native-reader boundary too?

Approach

The shared allocator and reused native-config readers fit existing boundaries. Use unfiltered managed_mcp_servers(state) for reservation and preservation, while keeping client-filtered entries for registration decisions.

Summary

Diff size (computed): 5 files; +326 / -46 lines (372 changed text lines); 0 binary files.

Category Files Added Deleted Share of changed text lines
Tests and test support 2 164 28 51.6%
Documentation 0 0 0 0.0%
Dependencies and lockfiles 0 0 0 0.0%
Implementation / other 3 162 18 48.4%

Shares use additions + deletions, including generated text and lockfiles. Binary files have no line count. Tests include fixtures/helpers under test paths and colocated test/spec files; categories are path-based, not coverage measurements.

Changes

The change assigns short names to new UC MCP registrations, adds collision checks against existing configurations, and updates picker recognition. It also adds native-config readers, test isolation, and regression scenarios. No documentation, dependency, or generated-file changes are included.

The implementation is proportional to the goal. The concrete reduction opportunity is duplicated test setup and scenarios, assessed below. Existing OS-managed name preservation and cross-client managed reservations need correction before merge.

Tests

Test-by-test assessment

Tests were not executed during this review. The reported test results are PR-author evidence, not independently verified results.

The table accounts for all changed test functions and support. No test functions were deleted.

Test / case Behavior protected Layer Needed? Action / rationale
tests/test_mcp.py::TestConfigureMcpFromLocation.test_registers_every_discovered_service Free leaves register with unchanged URLs and saved state Command component keep Existing happy path establishes complete registration.
tests/test_mcp.py::TestConfigureMcpFromLocation.test_replaces_servers_outside_location Replace removes an outside entry Command component keep Protects a distinct destructive transition.
tests/test_mcp.py::TestAddMcpCommand.test_keeps_servers_outside_location Append preserves outside entries Command component keep Necessary counterpart to replace behavior.
tests/test_mcp.py::TestAddMcpCommand.test_services_subset_keeps_others_in_location Append preserves an unselected dashed entry Command component keep Protects mixed legacy/new names.
tests/test_mcp.py::TestAddMcpCommand.test_agents_scopes_registration_to_named_agent Explicit client scope controls registration and state Command component keep Protects client ownership.
tests/test_mcp.py::TestConfigureMcpServicesSubset.test_configures_only_the_requested_subset Unselected services are omitted Command component keep Distinct selection behavior.
tests/test_mcp.py::TestConfigureMcpServicesSubset.test_matches_bare_short_names Bare selectors match fully qualified services Command component keep Selector matching differs from entry-name allocation.
tests/test_mcp.py::TestConfigureMcpServicesSubset.test_unknown_requested_service_warns_and_skips Invalid selection warns while valid selections proceed Command component keep Meaningful partial-failure coverage.
tests/test_mcp.py::TestConfigureMcpServicesSubset.test_adds_and_removes_to_match_new_selection Replacement preserves selected legacy entries and adds new entries Command component keep Protects selection transitions and no migration.
tests/test_mcp.py::TestConfigureMcpServicesSubset.test_full_names_without_location_derive_the_schema Fully qualified selectors derive the schema Command component keep Covers a different public input path.
tests/test_mcp.py::TestResolveManagedMcpServers.test_location_registers_the_whole_schema Managed location forwards schema resolution Resolver unit keep Retains the location dispatch contract; stub signature follows the new argument.
tests/test_mcp.py::TestResolveManagedMcpServers.test_names_build_entries_directly_without_discovery Explicit names preserve URL, authentication, and client construction Resolver unit keep Protects direct resolution without discovery.
tests/test_mcp.py::TestResolveManagedMcpServers.test_duplicate_names_collapse_to_one_entry Repeated identical service names register once Resolver unit keep Different from distinct services sharing a leaf.
tests/test_mcp.py::TestResolveManagedMcpServers.test_malformed_names_are_skipped_with_a_warning Invalid names do not block valid entries Resolver unit keep Retains useful failure-path assertions.
tests/test_mcp.py::TestReconcileManagedMcpServers.test_location_config_registers Reconciliation forwards the location selector Reconciliation component keep Retains orchestration coverage, but its stub does not prove name preservation.
tests/test_mcp.py::TestManagedServersAreNotReAdded.test_location_add_skips_an_already_managed_service Matching managed URL is skipped without blocking another service Command component keep The realistic URL fixture matches the intended identity contract.
tests/test_mcp.py::TestMcpServiceEntryNames.test_free_leaf_gets_the_short_name Free leaves receive short names Command component remove Already covered more completely by test_registers_every_discovered_service.
tests/test_mcp.py::TestMcpServiceEntryNames.test_leaf_taken_in_ug_state_gets_the_dashed_name Another state entry reserves the leaf Command component move layer Test reservation at allocator level; existing append tests protect preservation.
tests/test_mcp.py::TestMcpServiceEntryNames.test_leaf_taken_by_a_managed_entry_gets_the_dashed_name Managed entries reserve the leaf Command component move layer Allocator coverage suffices for this supplied-input case.
tests/test_mcp.py::TestMcpServiceEntryNames.test_same_service_already_managed_is_skipped_not_duplicated Short managed entries are not duplicated Command component consolidate Parameterize short/dashed names in the existing managed-skip scenario.
tests/test_mcp.py::TestMcpServiceEntryNames.test_leaf_in_native_claude_config_gets_the_dashed_name Native Claude entries reserve names, including matching URLs Command and file integration move layer Exercise allocator plus real temporary config without command setup.
tests/test_mcp.py::TestMcpServiceEntryNames.test_leaf_in_native_codex_config_blocks_a_claude_only_run Native Codex names affect Claude-only allocation Command and file integration move layer Retain cross-client behavior at allocator/native-reader boundary.
tests/test_mcp.py::TestMcpServiceEntryNames.test_unreadable_native_config_makes_every_name_dashed Malformed native configuration triggers fallback Command and file integration move layer Exercise fallback lower down and include Codex parse failure.
tests/test_mcp.py::TestMcpServiceEntryNames.test_services_sharing_a_leaf_in_one_run_both_get_dashed_names Duplicate leaves fall back without affecting a free leaf Allocator unit keep Smallest reliable layer for this allocation rule.
tests/test_mcp.py::TestMcpServiceEntryNames.test_rerun_never_renames_existing_entries Existing personal short/dashed names survive rerun Command component keep Strengthen to exact entry preservation and no removals.
tests/test_mcp.py::TestMcpServiceEntryNames.test_managed_location_services_use_the_same_rule Managed location reaches the shared allocator Resolver component consolidate Extend the existing managed-location scenario with real resolution.
tests/test_mcp.py::TestMcpServiceEntryNames.test_picker_finds_a_service_registered_under_its_short_name Short registration is checked or hidden appropriately Picker unit consolidate Add short/dashed cases to existing test_mcp_service_choice_known_vs_unknown.
tests/test_mcp.py::TestMcpServiceEntryNames.test_picker_ignores_an_unrelated_entry_with_the_leaf_name A different service URL does not claim the leaf Picker unit keep Distinct negative identity regression.
tests/conftest.py::_isolate_ucode_state Tests cannot read ambient native Claude/Codex configurations Global fixture keep Reuses isolation setup for newly introduced native reads and clears directory overrides.
tests/test_mcp.py — added json and codex imports Native-config test construction Test support keep Required for file-backed configuration scenarios.
tests/test_mcp.py::TestReconcileManagedMcpServers._wire Existing reconciliation tests accept the resolver argument Shared helper keep Necessary signature adaptation; not evidence of preservation coverage.
tests/test_mcp.py::TestMcpServiceEntryNames._url Deterministic service URLs Local helper keep Small wrapper reusing the production URL builder.
tests/test_mcp.py::TestMcpServiceEntryNames._run Shared command setup and registration/state capture Local helper consolidate Reduce after moving allocation matrices; retain for distinct command transitions.

Required regression coverage for the blocking issues:

  • Reconcile an existing OS-managed dashed entry with empty fallback state and assert that its name remains unchanged.
  • Add for Claude while Codex has an unrelated managed entry with the same leaf; assert dashed allocation and preservation through subsequent configure.

Existing append, replacement, dashed-picker, and discovery-failure tests provide useful surrounding coverage. Changing the managed-skip fixture from a placeholder URL to the actual service URL does not remove a valid service-identity guarantee. Additional live E2E tests are unnecessary for these allocation rules.

After fixes, run uv run pytest tests/test_mcp.py tests/test_agent_claude.py tests/test_agent_codex.py and just lint. In an isolated home, repeat configure against an existing dashed OS-managed entry and confirm its name stays unchanged.

Scope

All changes support the stated short-name outcome, including native-config isolation and updated expectations. No unrelated changes were found. The blocking issues concern incomplete preservation and reservation behavior within that scope.


Automated review by Polly · workflow run

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.

2 participants