Skip to content

fix(extensions): resolve custom MCP auth during registration - #7024

Merged
henrypark133 merged 23 commits into
mainfrom
mcp-registration-followups
Aug 3, 2026
Merged

henrypark133 merged 23 commits into
mainfrom
mcp-registration-followups

Conversation

@henrypark133

@henrypark133 henrypark133 commented Aug 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Resolve hosted-MCP authentication during registration, before admitting a package definition.
  • Auto now performs only a credential-free MCP initialization handshake: success resolves to NoAuth; validated RFC 9728/RFC 8414 metadata resolves to OAuth only when a usable client path exists; metadata without dynamic client registration (and no selected client profile), or an otherwise unexplained 401, returns typed auth_selection_required and persists nothing.
  • Keep the registration modal open for the ambiguous case and offer exactly OAuth or bearer. Explicit OAuth is metadata-validated during the retry; explicit bearer records the credential requirement for the ordinary setup continuation.
  • Preserve exact-retry idempotency: once the same definition is durable, a lost-response retry does not depend on the remote MCP still being reachable.
  • Retain the prior follow-up fixes for sanitized challenge handling, metadata fallback, tenant authority, credential/setup recovery, bounded manifest CAS, and failed-preparation lease cleanup.

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

Follow-up to #6930 and its merged review comments.

Validation

  • cargo fmt --all -- --check
  • Scoped all-target clippy with -D warnings for MCP, extension host/manager, product/contracts, and WebUI.
  • cargo test -p ironclaw_mcp (32 unit, 36 adapter-contract, 5 dispatch tests passed).
  • Full hosted-MCP registration integration suite (20 passed, 2 intentional live-test skips).
  • WebUI registration handler contracts (3 passed) and focused frontend suites (19 passed) plus TypeScript typecheck.
  • cargo test -p ironclaw_architecture passed.
  • Exact pinned-nightly changed-line coverage replay passed at 90.45% (758/838), with the 90% floor unchanged.
  • Merged origin/main at 80a433a38.
  • $code-review-local Runtime, Structure, and Verification lanes plus strict quality review converged on the final delta.

Test Strategy

User behavior: registration either completes with a proven no-auth/OAuth mode or remains in the registration wizard asking for OAuth versus bearer. An unresolved Auto attempt creates no extension, so installation never becomes the first place users must classify server authentication.

Risk areas:

  • Model behavior
  • Browser
  • Side effect
  • Persistence
  • Security or permissions
  • External provider
  • Cross-component behavior

Tests added or strengthened:

  • MCP adapter: the auth probe performs only initialize + notifications/initialized, discards its session, never lists tools, and fails closed for unsupported transport/missing URL.
  • Reborn integration: Auto no-auth persists NoAuth even with an empty catalog; Auto OAuth and explicit OAuth validate metadata; GitHub-shaped OAuth metadata without DCR returns the registration blocker and persists nothing; a bare 401 does the same; a wrong OAuth retry still persists nothing; explicit bearer proceeds to credential setup and wrong/correct-token recovery; exact retries perform no additional network request.
  • WebUI/backend contract: ambiguity surfaces as validation_code=auth_selection_required on field=auth_selection, not generic invalid_value; mutation messages survive authoritative lifecycle reprojection, with caller-level setup coverage.
  • Frontend: the modal converts that typed result into OAuth/Bearer choices, retries with the selected mode, excludes no-auth after a 401, and resets to Auto when the user goes back to edit the endpoint.
  • Persistence/concurrency coverage from the preceding commits remains intact for bounded manifest CAS, membership/credential preservation, tenant authority, and lease cleanup.

Test tiers:

  • Unit/contract: passed for affected MCP, product-surface, WebUI, and frontend contracts.
  • Reborn integration: passed, 20 tests; 2 live-provider tests intentionally ignored by their opt-in contract.
  • Architecture: passed full ironclaw_architecture suite.
  • Browser E2E: Not run locally; focused component/hook tests and handler contracts cover the changed wizard state machine, while CI owns browser E2E.
  • Model/recorded fixture: Not applicable: no prompt, model selection, or model-output behavior changed.
  • Database: no schema change; existing libSQL/PostgreSQL CAS coverage from this PR remains unchanged.

Commands run include:

  • cargo test -p ironclaw_mcp -- --nocapture
  • cargo test -p ironclaw_reborn_integration_tests --test reborn_integration_hosted_mcp_registration -- --nocapture
  • cargo test -p ironclaw_webui --test webui_v2_handlers_contract register_hosted_mcp -- --nocapture
  • cargo test -p ironclaw_architecture -- --nocapture
  • scoped cargo clippy ... --all-targets -- -D warnings
  • focused Vitest suites and tsc --noEmit
  • cargo fmt --all -- --check and git diff --check

Public MCP evidence:

  • The earlier credential-free matrix covered 20 representative endpoints across OAuth, bearer, no-auth, and ambiguous outcomes.
  • Reverified on 2026-08-03: DeepWiki accepts anonymous initialization; Linear and Stripe advertise usable OAuth metadata; GitHub Copilot MCP advertises OAuth metadata but no DCR endpoint, so Auto now asks for an explicit choice and Bearer supports the documented PAT flow.
  • The bare-401 fixture remains necessary for servers that expose no usable metadata; it deterministically exercises the OAuth-versus-bearer ambiguity without provider-specific branching.

Security Impact

Authentication discovery changes, but the probe uses the existing host-mediated runtime egress path. Raw challenge values, response bodies, and credentials remain outside the product wire. Metadata fetches remain HTTPS-only, bounded, redirect/private-range mediated, and credential-free. No package definition is persisted from an ambiguous or invalid OAuth registration. Tenant members still cannot mutate tenant-owned extension authentication.

Reborn Trust-Boundary Checklist

  • Typed auth selection remains owned by extension contracts; the generic product surface adds one sanitized validation code.
  • Untrusted endpoints are canonicalized before mediated egress and cannot carry credentials, fragments, localhost, or IP literals.
  • New status/action/response branches are exhaustively covered at lifecycle, product-surface, HTTP, and frontend boundaries.
  • Metadata bodies retain the 64 KiB bound; registration preflight is capped at 8 concurrent requests per process with fail-fast retryable overload behavior.
  • Registration discovery is side-effect-free: protocol handshake and metadata GET only; tool catalog admission remains installation preparation.
  • Driver/operator failures remain stable and sanitized; ambiguity is actionable without exposing provider text.

Database Impact

No schema or migration change. Unresolved registration writes nothing. Existing bounded CAS behavior preserves normalized membership and credential rows for prepared installations.

Blast Radius

Hosted-MCP registration/preparation, the concrete host-mediated MCP HTTP client, product-surface error projection, and the custom-MCP registration modal. No provider-name heuristic or new generic discovery trait was added.

Rollback Plan

Revert this PR. No migration or external state rewrite is required; already registered extensions remain removable and re-registerable.

Review Follow-Through

Historical #6930 comments and all eight review threads on this PR were checked against commit history and live code. Six prompted focused fixes; two suggestions were declined after strict quality review because they added state or coordination without changing the contract. Valid code/contract issues were fixed without weakening valid tests. The one intentionally changed integration scenario asserted the obsolete install-time auth-choice flow; it now proves the stronger registration-time no-persistence contract while preserving bearer credential recovery. HANDOFF.md and .preserved/ remain local and uncommitted.


Review track: C (network authentication, authority, lifecycle, and persistence concurrency)

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@railway-app

railway-app Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-7024 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Aug 3, 2026 at 9:42 pm

@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added hosted and custom MCP authentication setup with Bearer, OAuth, and no-auth options.
    • Added recovery flows for registrations requiring an authentication choice.
    • Setup screens now display blockers, status messages, validation errors, and refreshed requirements.
    • Added initialization-only authentication checks before completing registration.
  • Bug Fixes

    • Improved OAuth metadata discovery across path-specific, root, query, and fragment URLs.
    • Prevented invalid configurations from publishing tools.
    • Improved handling of missing challenges, rejected credentials, unavailable metadata, and failed setup retries.

Walkthrough

The PR adds RFC 9728 protected-resource metadata fallbacks and hosted MCP authentication selection. The flow spans authentication probing, OAuth preparation, installation persistence, lifecycle contracts, setup responses, WebUI recovery, and integration coverage.

Changes

Hosted MCP authentication and OAuth discovery

Layer / File(s) Summary
OAuth metadata URL construction and candidate discovery
crates/ironclaw_auth/src/engine/*, crates/ironclaw_auth/tests/*
Metadata URLs use the RFC 9728 layout. Discovery evaluates advertised, path-specific, and origin-root candidates.
Authentication preparation and installation state
crates/ironclaw_extension_host/src/hosted_mcp_*, crates/ironclaw_extension_host/src/mcp_discovery.rs, crates/ironclaw_mcp/src/lib.rs, crates/ironclaw_extensions/src/installations.rs
Registration probes authentication, supports explicit selection and OAuth recovery, refreshes manifests conditionally, and cleans matching preparation leases.
Lifecycle action and setup response wiring
crates/ironclaw_product_contracts/src/*, crates/ironclaw_product/src/reborn_services/*, crates/ironclaw_extension_host/src/product_lifecycle.rs, crates/ironclaw_extension_manager/src/*
A typed selection action, setup parsing, blocker reporting, activation handling, and lifecycle messages were added.
WebUI authentication recovery
crates/ironclaw_webui/frontend/src/pages/extensions/*
The configuration and registration modals offer explicit authentication choices and submit selections through new API and hook support.
Validation and test support
tests/integration/*, tests/support/*, crates/ironclaw_extensions/tests/*, crates/ironclaw_mcp/tests/*
Tests cover no-challenge responses, retries, credential rejection, metadata fallback ordering, persistence validation, and lease cleanup.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: copilot, benkurrek

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title follows Conventional Commits style and accurately summarizes the hosted MCP authentication registration change.
Description check ✅ Passed The description covers the required sections, change scope, validation, security, database, rollback, and review details.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7024 August 2, 2026 23:17 Destroyed
@github-actions github-actions Bot added size: M 50-199 changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Aug 2, 2026
@ironloopai

ironloopai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Review · PR #7024

🟢 Completed · Review submitted

Submitted review →

Reviewed the complete trusted base-to-head comparison. The hosted-MCP authentication handling correctly preserves credential setup for metadata-less 401 responses, and the manifest-only bounded CAS update preserves concurrent normalized membership and credential state. No concrete actionable defects were found.

Automatic · PR opened · attempt 1 of 3 · completed in 1m 49s

Run details
  • Repository: nearai/ironclaw
  • Base: main at 0399cef
  • Head: mcp-registration-followups at 543d8f8
  • Created: Aug 2, 2026, 11:22 PM UTC
  • Updated: Aug 2, 2026, 11:23 PM UTC
  • Run: 6c072343-ddb1-44f9-8db5-1c7d6d6f4739
  • Latest attempt: 1 · Completed · b5f5e391-fea5-4dc3-89e7-0657458da593

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 Review complete · PR #7024

✅ No actionable findings

Reviewed the complete trusted base-to-head comparison. The hosted-MCP authentication handling correctly preserves credential setup for metadata-less 401 responses, and the manifest-only bounded CAS update preserves concurrent normalized membership and credential state. No concrete actionable defects were found.

Validation and technical details
  • Inspected all 7 changed files and surrounding hosted-MCP discovery, lifecycle preparation, persistence, compatibility projection, and fixture code.
  • Traced ExtensionInstallationStorePort::upsert_manifest_only through its default contract, Arc delegation, production implementation, and libSQL/PostgreSQL parity tests.
  • Verified the new CAS transform checks installation identity, manifest identity/reference, timestamp freshness, removal state, and active mutation leases while retaining child membership and credential rows.
  • Traced bare MCP AuthRequired classification through sanitized runtime responses into CredentialsRejected and the structured lifecycle credential blocker.
  • Reviewed wrong-token, successful continuation, malformed endpoint, unquoted metadata, stale refresh, identity mismatch, concurrent membership, persistence reopen, and tool-publication assertions.
  • git diff --check passed for refs/ironloop/base..refs/ironloop/head.
  • Targeted cargo tests could not be executed because cargo is not installed in the review environment (cargo: command not found).
  • Base: main
  • Head: mcp-registration-followups at 543d8f8
  • Run: 6c072343-ddb1-44f9-8db5-1c7d6d6f4739

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 85.96% (324666 / 377676 lines)
  floor:    85.11% (tolerance 0.5pp -> effective floor 84.61%)
  denominator: 377676 lines now vs 375097 at floor capture (+2579 lines, +0.69%) — not a material change

RATCHET PASS: ironclaw_runner
  observed: 85.93% (14917 / 17359 lines)
  floor:    85.55% (tolerance 0.5pp -> effective floor 85.05%)
  floor_covered_lines: 14658 (tolerance 20 lines -> effective floor 14638)
  denominator: 17359 lines now vs 17133 at floor capture (+226 lines, +1.32%) — not a material change

RATCHET PASS: ironclaw_processes
  observed: 88.76% (5889 / 6635 lines)
  floor:    88.07% (tolerance 0.5pp -> effective floor 87.57%)
  floor_covered_lines: 5839 (tolerance 20 lines -> effective floor 5819)
  denominator: 6635 lines now vs 6630 at floor capture (+5 lines, +0.08%) — not a material change

RATCHET PASS: ironclaw_turns
  observed: 88.46% (3709 / 4193 lines)
  floor:    85.11% (tolerance 0.5pp -> effective floor 84.61%)

RATCHET PASS: ironclaw_authorization
  observed: 86.59% (723 / 835 lines)
  floor:    62.51% (tolerance 0.5pp -> effective floor 62.01%)
  floor_covered_lines: 612 (tolerance 20 lines -> effective floor 592)
  denominator: 835 lines now vs 979 at floor capture (-144 lines, -14.71%) — material change (>5%)

RATCHET PASS: ironclaw_approvals
  observed: 91.05% (1820 / 1999 lines)
  floor:    85.86% (tolerance 0.5pp -> effective floor 85.36%)
  floor_covered_lines: 1822 (tolerance 20 lines -> effective floor 1802)
  denominator: 1999 lines now vs 2122 at floor capture (-123 lines, -5.8%) — material change (>5%)

RATCHET PASS: ironclaw_secrets
  observed: 85.81% (2896 / 3375 lines)
  floor:    84.01% (tolerance 0.5pp -> effective floor 83.51%)
  floor_covered_lines: 2795 (tolerance 20 lines -> effective floor 2775)
  denominator: 3375 lines now vs 3327 at floor capture (+48 lines, +1.44%) — not a material change

RATCHET PASS: ironclaw_filesystem
  observed: 77.04% (5911 / 7673 lines)
  floor:    75.93% (tolerance 0.5pp -> effective floor 75.43%)
  floor_covered_lines: 5826 (tolerance 20 lines -> effective floor 5806)
  denominator: 7673 lines now vs 7673 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_llm
  observed: 79.22% (20885 / 26364 lines)
  floor:    79.22% (tolerance 0.5pp -> effective floor 78.72%)
  floor_covered_lines: 20885 (tolerance 20 lines -> effective floor 20865)
  denominator: 26364 lines now vs 26364 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_triggers
  observed: 94.68% (3134 / 3310 lines)
  floor:    86.04% (tolerance 0.5pp -> effective floor 85.54%)
  floor_covered_lines: 2804 (tolerance 20 lines -> effective floor 2784)
  denominator: 3310 lines now vs 3259 at floor capture (+51 lines, +1.56%) — not a material change

RATCHET PASS: ironclaw_product
  observed: 87.75% (22428 / 25558 lines)
  floor:    86.94% (tolerance 0.5pp -> effective floor 86.44%)
  floor_covered_lines: 21367 (tolerance 20 lines -> effective floor 21347)
  denominator: 25558 lines now vs 24576 at floor capture (+982 lines, +4%) — not a material change

RATCHET PASS: ironclaw_outbound
  observed: 94.68% (4271 / 4511 lines)
  floor:    93.49% (tolerance 0.5pp -> effective floor 92.99%)
  floor_covered_lines: 4105 (tolerance 20 lines -> effective floor 4085)
  denominator: 4511 lines now vs 4391 at floor capture (+120 lines, +2.73%) — not a material change

RATCHET PASS: ironclaw_extension_host
  observed: 85.08% (24472 / 28765 lines)
  floor:    83.82% (tolerance 0.5pp -> effective floor 83.32%)
  floor_covered_lines: 22271 (tolerance 20 lines -> effective floor 22251)
  denominator: 28765 lines now vs 26569 at floor capture (+2196 lines, +8.27%) — material change (>5%)

RATCHET PASS: ironclaw_events
  observed: 80.55% (1197 / 1486 lines)
  floor:    80.55% (tolerance 0.5pp -> effective floor 80.05%)
  floor_covered_lines: 1197 (tolerance 20 lines -> effective floor 1177)
  denominator: 1486 lines now vs 1486 at floor capture (+0 lines, +0%) — not a material change

RATCHET PASS: ironclaw_safety
  observed: 92.75% (4468 / 4817 lines)
  floor:    92.44% (tolerance 0.5pp -> effective floor 91.94%)
  floor_covered_lines: 3973 (tolerance 20 lines -> effective floor 3953)
  denominator: 4817 lines now vs 4298 at floor capture (+519 lines, +12.08%) — material change (>5%)

RATCHET PASS: ironclaw_host_runtime
  observed: 88.41% (21338 / 24135 lines)
  floor:    88.23% (tolerance 0.5pp -> effective floor 87.73%)
  floor_covered_lines: 20538 (tolerance 20 lines -> effective floor 20518)
  denominator: 24135 lines now vs 23277 at floor capture (+858 lines, +3.69%) — not a material change

Reborn integration-tier coverage

Line coverage (Reborn crates): 85.96% — 324666 / 377676 lines

Per-crate breakdown (63 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_host_ingress 42.5% 17 / 40
ironclaw_memory 53.48% 630 / 1178
ironclaw_projects 72.36% 233 / 322
ironclaw_capabilities 74.59% 2876 / 3856
ironclaw_trust 75.79% 748 / 987
ironclaw_extractors 75.88% 538 / 709
ironclaw_reborn_cli 76.1% 11084 / 14566
ironclaw_observability 76.19% 32 / 42
ironclaw_filesystem 77.04% 5911 / 7673
ironclaw_wasm 78.84% 704 / 893
ironclaw_llm 79.22% 20885 / 26364
ironclaw_events 80.55% 1197 / 1486
ironclaw_loop_contracts 82.4% 5637 / 6841
ironclaw_first_party_extensions 82.57% 6784 / 8216
ironclaw_product_contracts 82.75% 3522 / 4256
ironclaw_memory_native 82.85% 2850 / 3440
ironclaw_libsql_runtime 83.3% 384 / 461
ironclaw_auth 83.95% 6699 / 7980
ironclaw_operator 84.47% 5309 / 6285
ironclaw_hooks 84.57% 9896 / 11702
ironclaw_event_projections 84.81% 854 / 1007
ironclaw_reborn_event_store 84.93% 1206 / 1420
ironclaw_extension_host 85.08% 24472 / 28765
ironclaw_reborn_config 85.29% 2110 / 2474
ironclaw_network 85.31% 894 / 1048
ironclaw_reborn_composition 85.51% 21794 / 25487
ironclaw_secrets 85.81% 2896 / 3375
ironclaw_runner 85.93% 14917 / 17359
ironclaw_host_api 86.05% 6367 / 7399
ironclaw_extension_contracts 86.42% 2584 / 2990
ironclaw_authorization 86.59% 723 / 835
ironclaw_webui 86.95% 11937 / 13729
ironclaw_wasm_limiter 87.06% 74 / 85
ironclaw_common 87.07% 1152 / 1323
ironclaw_reborn_traces 87.61% 11720 / 13377
ironclaw_product 87.75% 22428 / 25558
ironclaw_scripts 87.87% 420 / 478
ironclaw_threads 88.14% 5189 / 5887
ironclaw_host_runtime 88.41% 21338 / 24135
ironclaw_turns 88.46% 3709 / 4193
ironclaw_telegram_extension 88.55% 588 / 664
ironclaw_skills 88.61% 2785 / 3143
ironclaw_process_sandbox 88.64% 281 / 317
ironclaw_processes 88.76% 5889 / 6635
ironclaw_reborn_openai_compat 89.4% 3644 / 4076
ironclaw_extensions 89.43% 6288 / 7031
ironclaw_telegram_v2_adapter 89.47% 1580 / 1766
ironclaw_loop_host 90.47% 18043 / 19944
ironclaw_resources 90.76% 4084 / 4500
ironclaw_approvals 91.05% 1820 / 1999
ironclaw_reborn_identity 91.3% 451 / 494
ironclaw_mcp 92% 1426 / 1550
ironclaw_conversations 92.08% 2383 / 2588
ironclaw_event_streams 92.5% 1048 / 1133
ironclaw_safety 92.75% 4468 / 4817
ironclaw_agent_loop 93.52% 10430 / 11153
ironclaw_slack_extension 93.95% 3697 / 3935
ironclaw_first_party_extension_ports 94.66% 3758 / 3970
ironclaw_outbound 94.68% 4271 / 4511
ironclaw_triggers 94.68% 3134 / 3310
ironclaw_prompt_envelope 97.46% 192 / 197
ironclaw_runtime_policy 97.6% 855 / 876
ironclaw_attachments 98.23% 831 / 846

This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors.

Exemptions (18 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657
crates/ironclaw_attachments/src/lib.rs Declarative crate facade: module declarations, constants, and re-exports only; executable attachment modules remain covered. #6524
crates/ironclaw_extension_host/src/ingress/mod.rs Declarative ingress module facade and documentation only; executable router modules remain covered. #6524
crates/ironclaw_host_api/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable host API modules remain covered. #6524
crates/ironclaw_host_api/src/product_adapter/mod.rs Declarative product-adapter facade: module declarations and re-exports only; executable adapter modules remain covered. #6524
crates/ironclaw_llm/src/rig_adapter/tests/finish_reason_tests.rs Test-only module stored under src/ for private adapter access; cargo-llvm-cov omits test harness source from production LCOV while the exercised rig_adapter.rs production lines remain coverage-gated. #6284
crates/ironclaw_loop_contracts/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable loop-contract behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_outbound/src/error.rs Declarative error vocabulary only; variants have no LLVM-instrumentable production statements. #6524
crates/ironclaw_outbound/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable outbound modules remain covered. #6524
crates/ironclaw_product/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable product behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_product/src/lib.rs Declarative crate facade: module declarations and re-exports only; executable product modules remain covered. #6524
crates/ironclaw_product/src/scoped_fs/mod.rs Declarative scoped-filesystem facade and documentation only; executable scoped filesystem modules remain covered. #6524
crates/ironclaw_reborn_composition/src/support/fs/mod.rs Declarative composition support facade: module declarations and re-exports only; executable filesystem adapters remain covered. #6524
crates/ironclaw_slack_extension/src/lib.rs Declarative Slack crate facade: module declarations and re-exports only; executable Slack modules remain covered. #6524
crates/ironclaw_telegram_extension/src/lib.rs Declarative Telegram crate facade: module declarations and re-exports only; executable Telegram modules remain covered. #6524
crates/ironclaw_threads/src/lib.rs Declaration-only public facade with no executable Rust statements; rustc emits no LCOV source record. Executable thread behavior remains covered in the owned implementation modules. #6524
crates/ironclaw_webui/src/webui_v2/mod.rs Declaration-only WebUI v2 facade with no executable Rust statements; rustc emits no LCOV source record. Executable route behavior remains covered in the owned implementation modules. #6524

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7024 August 3, 2026 01:26 Destroyed
@henrypark133 henrypark133 changed the title fix(extensions): handle metadata-less hosted MCP auth fix(extensions): resolve custom MCP auth during registration Aug 3, 2026
@henrypark133
henrypark133 marked this pull request as ready for review August 3, 2026 01:26
Copilot AI review requested due to automatic review settings August 3, 2026 01:26
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added size: XL 500+ changed lines and removed size: M 50-199 changed lines labels Aug 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated no new comments.

@henrypark133 henrypark133 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review (multi-agent)

Intent: Resolve custom MCP authentication during registration with safe OAuth/bearer selection, no persistence for ambiguity, and idempotent retries.

Stats: 8 findings (from 8 raw, 8 after dedup) across 4 files. Reviewers run: security, bugs, performance, tests, conventions, local-patterns, maintainability, approach. Reviewers failed: none. Body-only: 0. Evidence quality: degraded (CodeGraph unavailable in exact-head codeload snapshot).

Bugs

  1. Medium Bearer selection closes setup before token entry (crates/ironclaw_webui/frontend/src/pages/extensions/components/configure-modal.tsx:73-78, confidence 95) — anchor: crates/ironclaw_webui/frontend/src/pages/extensions/components/configure-modal.tsx:75
    Selecting Bearer invokes the backend activation path, which returns a setup-needed response containing the bearer-token credential requirement, but this success callback immediately closes the configure modal. The parent only invalidates queries, so the user cannot enter the required token in the current flow and must rediscover and reopen the extension setup.
    Fix: Keep the modal open and update its setup state when authentication selection returns credential blockers, or explicitly reopen the refreshed setup modal instead of calling onClose.

Performance

  1. Medium Concurrent registrations duplicate remote authentication probes (crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:88-119, confidence 88) — anchor: crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:88
    The existence check occurs before any per-extension synchronization, so concurrent Auto/OAuth registrations for the same extension can all observe no definition and independently perform the MCP handshake plus OAuth metadata requests. Only afterward do they serialize at admission; this multiplies remote traffic and latency during retries or concurrent tabs.
    Fix: Add keyed per-extension single-flight coordination around the existence check and authentication resolution, while retaining the global lock only for durable admission and catalog updates.
  2. Medium Registration permits unbounded concurrent outbound probes (crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:189-194, confidence 72) — candidate — validate claim — anchor: crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:189
    Each authenticated registration can trigger an MCP initialize request plus up to three sequential OAuth metadata requests, each with a 10-second timeout, before any durable admission. These probes run outside the lifecycle operation lock and the existing per-caller request rate limit does not cap concurrent in-flight work or impose a global/tenant budget. An attacker can submit many unique endpoints concurrently to consume outbound connection and task capacity.
    Fix: Enforce a bounded global or per-tenant semaphore for registration probes and reject or queue requests when the in-flight budget is exhausted.

Tests

  1. Medium Selected OAuth client profiles lack caller-level coverage (crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:727-803, confidence 85) — anchor: crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:727
    All integration OAuth registration tests pass client_profile_id: None. The selected-profile branch through SharedOAuthProfiles is only covered by frontend serialization, so successful profile resolution and unknown-profile rejection could regress without detection.
    Fix: Add integration coverage for explicit OAuth registration with a valid selected profile and an unknown profile that leaves registration unpersisted.

Conventions

  1. Medium Preserve causes when mapping admission validation errors (crates/ironclaw_auth/src/engine/admission.rs:136-167, confidence 100) — anchor: .claude/rules/error-handling.md:17-20
    The new admission validation paths repeatedly use map_err(|_| AuthProductError::MalformedConfig), discarding the underlying parsing and endpoint-validation causes. This violates the repository error-handling rule requiring server-side causes to be retained or logged even when client-facing errors are sanitized.
    Fix: Use cause-preserving error constructors or log the bound source before returning the sanitized AuthProductError.
  2. Medium Do not discard hosted-MCP validation causes (crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:786-792, confidence 100) — anchor: .claude/rules/error-handling.md:17-20
    The OAuth preparation path maps endpoint construction, canonicalization, and vendor-ID failures with map_err(|_| name_unavailable()), dropping each source error without logging or preserving it. The repository requires sanitized boundary errors to retain the server-side cause.
    Fix: Replace the discarded-error mappings with cause-preserving constructors or log each source error before sanitizing it.

Local-Patterns

  1. Low Document the new hosted-MCP auth lifecycle method (crates/ironclaw_extension_host/src/product_lifecycle.rs:258-263, confidence 85) — anchor: crates/ironclaw_extension_host/src/product_lifecycle.rs:258
    The new public select_hosted_mcp_auth method has no doc-comment explaining its explicit-selection and authorization behavior, unlike the neighboring public lifecycle method. This makes the new lifecycle surface harder to navigate and use correctly.
    Fix: Add a concise doc-comment describing that the method validates the caller and persists an explicit hosted-MCP authentication selection.

Maintainability

  1. Medium Option hides distinct OAuth preparation outcomes (crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:718-797, confidence 92) — anchor: crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:718
    prepare_oauth_manifest now returns Option, but None represents multiple different states: derived metadata was unavailable, or automatic OAuth metadata lacked dynamic client registration. Callers then reinterpret that same None differently for registration, activation, and explicit OAuth, spreading the real state machine across nested matches and making future outcome changes easy to misroute.
    Fix: Return an explicit outcome type (for example resolved, auth-selection-required, and invalid/unavailable metadata), or split registration resolution from activation preparation so each function has one unambiguous result contract.

Comment thread crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs
Comment thread crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs
Comment thread crates/ironclaw_auth/src/engine/admission.rs
Comment thread crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs
Comment thread crates/ironclaw_extension_host/src/product_lifecycle.rs
Comment thread crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs
Comment thread crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs
Copilot AI review requested due to automatic review settings August 3, 2026 17:30
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7024 August 3, 2026 17:30 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated no new comments.

Suppressed comments (2)

crates/ironclaw_extension_host/src/hosted_mcp_preparation.rs:953

  • registration_request_matches treats an incoming auth_selection: Auto as matching any existing registered definition, even if the existing definition was explicitly registered as Bearer. That means a client can submit Auto and silently get back a persisted Bearer-auth definition, which contradicts the new contract where Auto only resolves to NoAuth or validated OAuth and otherwise requires an explicit selection.
    match request.auth_selection.as_ref() {
        None | Some(HostedMcpAuthSelection::Auto) => true,
        Some(selection) => &mcp.registration_auth == selection,
    }

crates/ironclaw_webui/frontend/src/i18n/en.ts:1386

  • extensions.customMcpAuthHint is now used both for the registration modal (which offers only OAuth/Bearer in the auth-selection-required path) and for the setup ConfigureModal (which currently renders an additional no_auth radio). The current copy explicitly says “Choose OAuth or bearer token…”, which is inconsistent with the ConfigureModal choices and can mislead screen-reader users via the aria-label.
  "extensions.customMcpAuthHint": "This server requires authentication. Choose OAuth or bearer token to finish registration.",

@henrypark133
henrypark133 enabled auto-merge August 3, 2026 18:08
pranavraja99
pranavraja99 previously approved these changes Aug 3, 2026
@henrypark133
henrypark133 added this pull request to the merge queue Aug 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 3, 2026
Copilot AI review requested due to automatic review settings August 3, 2026 21:30
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-7024 August 3, 2026 21:30 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated no new comments.

Suppressed comments (1)

crates/ironclaw_webui/frontend/src/pages/extensions/hooks/useExtensions.ts:662

  • useHostedMcpAuthSelection only invalidates the extensions and extension-setup queries on onSuccess, but the mutation can still change server-side state and return an error (e.g., it re-projects into the same auth-selection setup blocker with a new message). In that case the UI won't refresh and can keep showing stale setup state. Move the invalidations to onSettled (or add onError) so the setup view is refreshed even when the mutation throws.
    onSuccess: (res) => {
      queryClient.invalidateQueries({ queryKey: ["extensions"] });
      queryClient.invalidateQueries({ queryKey: ["extension-setup", packageKey] });
      if (onSuccess) onSuccess(res);
    },
  });

@henrypark133
henrypark133 added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 4195f2c Aug 3, 2026
44 checks passed
@henrypark133
henrypark133 deleted the mcp-registration-followups branch August 3, 2026 22:16
l3ocifer pushed a commit to l3ocifer/frick-ironclaw that referenced this pull request Sep 3, 2026
…7024)

* fix(extensions): handle metadata-less MCP auth challenges

* fix(tests): follow extension contract split

* test(extensions): cover concurrent MCP refresh

* fix(extensions): resolve hosted MCP auth setup

* fix(extensions): keep rejected auth choice recoverable

* fix(extensions): preserve finalized no-auth state

* fix(extensions): persist unresolved auth recovery

* fix(extensions): release failed preparation checkpoints

* fix(extensions): fence preparation lease cleanup

* docs(extensions): clarify fenced lease cleanup

* fix(mcp): resolve auth during registration

* fix(extensions): address MCP lifecycle review findings

* refactor(mcp): share hosted client setup

* fix(mcp): require explicit auth when OAuth setup is unusable

* ci: map hosted MCP support to integration lane

* fix(mcp): address registration review findings

* test(mcp): cover auth recovery paths

* test(mcp): satisfy changed coverage gate

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-7024 — 57c2eb74 Deployed Aug 3, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: docs Documentation size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants