salesforce-mcp v7.1.0 — P1/P6 zip-prefix bug blocks operator-intent surface for Profiles/PermissionSets
Summary
salesforce-mcp v7.1.0 ships the four R48 zero-limitation operator-intent tools (P1 sf_get_profile_full_metadata, P2 sf_validate_user_permissions, P3 sf_resolve_permission_dependencies, P5 sf_disable_profile_permissions / sf_enable_profile_permissions, P6 sf_get_permission_set_full_metadata / sf_get_muting_permission_set_full_metadata) — but two defects make the Profile/PermissionSet write surface non-functional in the cutover build.
Defect 1 — P5 fail-closed on retired permission names
P5 (sf_disable_profile_permissions) wraps P2 pre-flight, then rejects the entire batch if any name is bucketed retired instead of auto-skipping retired names and proceeding with the valid subset. Operator must read the rejection, drop retired names, re-call. Two round-trips minimum — breaks the documented R48 “MCP absorbs all iteration / single round-trip” promise.
Reproduction (06-May-2026, fullcopy):
sf_disable_profile_permissions(profile="System Administrator Clone",
names=["ManageUsers", ..., "ManageMobileConfigurations", ...18 others],
org_alias="fullcopy")
→ status=error, skipped_retired=[{name: ManageMobileConfigurations, retired_in_api: v58, ...}],
would_have_acted_on=[18 valid names] # but did NOT act
Defect 2 — P1 zip-prefix bug (HARD, blocks all writes)
P1 (sf_get_profile_full_metadata) — also called internally by P5 to read current state — looks for the retrieved Profile at zip path unpackaged/profiles/{name}.profile but the actual zip returned by Salesforce contains it at profiles/{name}.profile (no unpackaged/ prefix). Returns status=error with misleading “Check fullname spelling” message while the same response’s zip_namelist proves the profile WAS retrieved successfully.
Reproduction:
sf_get_profile_full_metadata(profile_name="System Administrator Clone", org_alias="fullcopy")
→ status=error, zip_namelist=["profiles/System Administrator Clone.profile", "package.xml"],
insights="Expected entry 'unpackaged/profiles/...' not found in zip"
Suspected root cause: Modern simple-salesforce mdapi.retrieve() (Tooling/REST path) returns zip entries WITHOUT unpackaged/ prefix; legacy SOAP MDAPI returned WITH prefix. P1/P6 readers likely tested against SOAP-shape fixtures or coded against SOAP-era documentation.
Single-line candidate fix (tools/profile_complete/operations.py):
candidates = [f"profiles/{name}.profile", f"unpackaged/profiles/{name}.profile"]
matched = next((c for c in candidates if c in zip_namelist), None)P6 readers (sf_get_permission_set_full_metadata, sf_get_muting_permission_set_full_metadata) almost certainly carry the same defect — added together in Bible v19.1.10 Check 15.
Why Phase 9 census missed it
Cutover census validated symmetric-reader presence (method exists + returns a structured response) but NOT correctness against a real retrieved zip. Add a real-zip-roundtrip check to Phase 9: assert P1 returns parsed Profile shape (not error) for at least one known-existing profile per env.
Behavior to expect / workaround until v7.1.1
- ❌ Do NOT fall back to raw
sf_update_profile_metadatafor operator-intent flows — defeats the entire R48 validation purpose. - ❌ Do NOT script around with live-cred Python — violates tool-ambiguity protocol.
- ✅ Halt. Document. Wait for v7.1.1.
- ✅ For the rare unavoidable case, use Setup UI manually (~3 min for a 19-perm tightening).
What IS confirmed working in v7.1.0
health_checkreports 7.1.0 / Bible-v19.1.10 / 684 tools / both orgs healthy- All four P1-P6 tools resolved via ToolSearch
sf_validate_user_permissions(P2) correctly buckets retired names withretired_in_api+retirement_reasonmetadatamdapi.retrieve()itself succeeds — zip is fetched, contains the requested profile
Recommended fix for v7.1.1
- Patch P1 + both P6 readers to try both zip-path candidates (no-prefix first).
- Add Defect 1 policy decision — recommended: opt-in
skip_retired_names: bool = Falseparam (preserves strict default, honors R48 when caller opts in). - Add real-zip-roundtrip regression test + Phase 9 census check.
- Cutover v7.1.1 → resume profile tightening with same call shape.
Time-saving for next operator
This bug post-mortem at /root/aj-ea/outputs/documents/mcp-tool-limitation-salesforce-v7.1.0-p1-zip-prefix-bug-06-may-2026.md saves 30-45 min of debugging on next encounter — operator goes straight to “this is the v7.1.0 P1 prefix bug, halt and wait for v7.1.1” instead of re-discovering it.