Skip to content

feat: adopt MCP 2026 and expand the curated catalog - #284

Merged
pedronauck merged 6 commits into
mainfrom
mcps
Jul 31, 2026
Merged

pedronauck merged 6 commits into
mainfrom
mcps

Conversation

@pedronauck

@pedronauck pedronauck commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

Summary

This PR upgrades Compozy's MCP stack to the official 2026 protocol family and turns the marketplace feed from a single-entry demo into an installable, typed catalog for high-value vendor servers.

  • Replace mark3labs/mcp-go with the stable official github.com/modelcontextprotocol/go-sdk v1.7.0.
  • Support public MCP protocol versions 2026-07-28 and 2025-11-25, while keeping the older Codex handshake isolated to the private hosted sidecar compatibility boundary.
  • Hard-cut the marketplace feed to manifest v2 with typed launch, input, authentication, and scope contracts.
  • Publish 17 curated entries: Context7 plus 11 Wave 1 and 5 verified Wave 2 servers.
  • Expose the complete install, authorization, repair, and status lifecycle through CLI, HTTP, UDS, Web, documentation, and the official Compozy skill.

What changed

MCP runtime and protocol

  • Migrated client/server integration to the official Go SDK.
  • Added strict public protocol negotiation, capability checks, cursor pagination, cancellation, and structured SDK error mapping.
  • Implemented stateless Streamable HTTP handling with secure URL validation and loopback/private-network protections.
  • Updated the hosted MCP proxy to project callable non-deferred native tools immediately and fall back to full discovery for external clients.
  • Preserved the provider-required legacy handshake only inside the private Codex-hosted proxy; it is not accepted by public MCP endpoints.
  • Removed public SSE transport support instead of carrying a compatibility shim.

OAuth, credentials, and persistence

  • Added protected-resource metadata, authorization-server metadata, client ID metadata documents, pre-registration, dynamic client registration, PKCE, RFC 9207 issuer validation, refresh, and step-up authorization handling.
  • Persisted OAuth client registrations by scope and added token endpoint authentication method support.
  • Routed credential material through Vault references and added redaction coverage so catalog/config/status surfaces never return secret values.
  • Added gap-free migrations after rebasing onto PR feat: select and switch runtime per session prompt #283:
    • 00031: scoped OAuth registrations.
    • 00032: invalidate regenerable manifest-v1 catalog cache state.
    • 00033: persist token_endpoint_auth_method.
  • Preserved PR feat: select and switch runtime per session prompt #283's published 00029_schema.sql and 00030_schema.sql byte-for-byte.

Curated marketplace catalog

The manifest-v2 feed now contains:

  • Existing: Context7.
  • Wave 1: GitHub, Linear, Notion, Sentry, PostgreSQL (Yaw Labs), Supabase, Brave Search, Playwright, Stripe, PostHog, and Airtable.
  • Wave 2: GitLab, Atlassian, Grafana, Cal.com, and Cloudflare Code Mode.

Stdio launches use exact package/image versions, credential prompts contain metadata only, and hosted entries use only authentication fields supported by the runtime schema.

The implementation deliberately omits entries that could not be represented honestly through the current install contract:

  • Figma: the current remote flow depends on client-managed OAuth/access details that do not map cleanly to the catalog contract.
  • Shopify: the maintained surfaces are store/dynamic-UCP specific rather than a stable catalog-wide launch contract.
  • Slack: the official server currently requires fixed app registration and does not provide a suitable dynamic-registration path for this feed.

Public and agent-manageable surfaces

  • Extended the CLI, HTTP API, and UDS contracts for typed catalog installation, authorization, repair, and status.
  • Updated generated OpenAPI/TypeScript contracts and the Web settings flows.
  • Updated the documentation site catalog parser and marketplace rendering for manifest v2.
  • Updated packages/site/content/docs/configuration/mcp-json.mdx, marketplace documentation, and the bundled official Compozy skill.
  • Added operator-facing validation and actionable diagnostics for unsupported transports, invalid auth metadata, missing inputs, and secret binding failures.

Test and QA coverage

  • Expanded the MCP fixture server with modern and legacy Streamable HTTP, stdio, OAuth discovery/authorization, mix-up, refresh, and step-up behavior.
  • Covered negotiation, pagination, cancellation, secret containment, replacement semantics, cross-workspace isolation, hosted lifecycle, private-cache behavior, token authentication, and redaction.
  • Exercised a real hosted stdio MCP project and a non-bootstrap compozy__network_channel_create native tool call.
  • Exercised CLI install, Web guided install/status, site catalog rendering, hosted native projection, and the real Codex-managed task claim/heartbeat/complete lifecycle.
  • Ran the previously completed deep-review cycle and remediated all 15 defects and 54 advisories it identified. No new deep review was activated during the final review/rebase work.

Breaking changes

  • MCP catalogs now require manifest_version: 2; manifest v1 is rejected.
  • Public MCP transport no longer accepts SSE.
  • The runtime dependency moves from mark3labs/mcp-go to the official MCP Go SDK.
  • Manifest-v1 catalog cache data is invalidated because it is regenerable discovery state.

CodeRabbit and React Doctor remediation

  • Rechecked 46 CodeRabbit findings against the final implementation: 38 valid findings were fixed at their owning layer and 8 were refuted with pinned-SDK or existing-contract evidence.
  • Hardened OAuth discovery, token requests, registration defaults, scope normalization, redirect handling, secret boundaries, manager concurrency, and error classification.
  • Fixed MCP pagination, caller-owned slice mutation, bootstrap conflict/policy enforcement, catalog input bindings, manifest diagnostics, CLI terminal cancellation, site validation/copy, and guided-install rendering.
  • Fixed the independent CI blocker where sdk/go race tests inherited CGO_ENABLED=0 instead of the hermetic race environment.
  • Addressed the post-rebase incremental findings:
    • Guarded the registration-start test signal with sync.Once.
    • Replaced a no-op response-body drain branch with explicit structured error logging.
    • Rejected case variants of the reserved Vault OAuth subtree.
  • Cached catalog binding fields once per validation loop and removed a duplicate Vitest server key.
  • All 19 CodeRabbit review threads and the React Doctor review thread are resolved.
  • React Doctor reports 100/100, with zero errors and zero warnings.

Remediation and integration commits:

  • e772c2ff: initial 43-finding CodeRabbit remediation.
  • b017a6cb: migration-boundary alignment after rebasing onto PR feat: select and switch runtime per session prompt #283.
  • d02293ee: three incremental CodeRabbit findings.
  • 7c533934: React Doctor warning and duplicate Vitest configuration.

Compozy Impact Audit

  • Native tools: hosted MCP projection, discovery, bootstrap, and dispatch were updated. Canonical compozy__* IDs and tool schemas remain the source of truth; capability checks and hosted-proxy tests cover availability and dispatch.
  • Extensibility and hooks: MCP sidecars, provider launch contracts, catalog registry decoding, OAuth enrollment, Vault-backed inputs, and config lifecycle changed. Extension hooks, bridge SDK webhook delivery, skills/capability registration, bundles, and non-MCP resources were checked and are unchanged.
  • Workspace data isolation: catalog metadata remains global discovery data; installed provider configuration, OAuth registration/token state, and Vault references retain their declared global/workspace ownership. Replacement, list/read, cache, hosted-proxy, and authorization tests cover cross-workspace isolation and prevent credential leakage.
  • Official Compozy skill: updated to describe manifest-v2 install, authorization, repair, status, and agent-manageable MCP workflows.

Release notes

Features

  • Added official MCP 2026 protocol support through the stable Go SDK.
  • Added a typed manifest-v2 marketplace with 17 curated MCP servers.
  • Added durable OAuth discovery, registration, PKCE, refresh, step-up, and Vault-backed credential lifecycle support.
  • Added consistent MCP management across CLI, HTTP, UDS, Web, site documentation, and the official skill.

Fixes and security

  • Prevented hosted startup from blocking on deferred vendor discovery while retaining all callable local native tools.
  • Isolated the provider-specific legacy handshake from public protocol negotiation.
  • Added SSRF/redirect validation, OAuth issuer checks, token-auth method persistence, secret redaction, workspace isolation coverage, and a case-insensitive guard for the daemon-managed OAuth Vault subtree.

Documentation

  • Documented the manifest-v2 contract, typed inputs/authentication, curated marketplace entries, omissions, and operator flows.

Verification

Verification completed before the final commit/publish instruction; no new global local gate or visual-evidence pass was run during the shipping, review, or rebase steps.

  • Catalog validation: go run ./cmd/compozy-catalog validate ./catalog.
  • Earlier full repository gate: 18,504 Go tests plus lint, typecheck, builds, code generation checks, and architecture boundaries.
  • Focused MCP race/integration suites and hosted stdio/native-tool verification.
  • CLI, Web, documentation-site, and managed-task QA walks.
  • Strict QA evidence audit: 0 blockers and 0 warnings.
  • QA teardown recorded clean; no lab processes remain.
  • Post-rebase globaldb suite, targeted migration race tests, sidebar suite, codegen consistency, and migration identity checks.
  • Incremental internal/mcp/auth and internal/vault suites under the race detector.
  • Documentation-site marketplace catalog suite: 46/46.
  • React Doctor: 100/100, zero findings.
  • Real Linear OAuth consent remains blocked-verify because it requires a human authorization grant.

Full report: docs/qa/reports/2026-07-30-mcp-2026-catalog-v2.md.

Local QA artifacts are retained and indexed by the report; absolute lab paths are intentionally not presented as reviewer-clickable repository evidence.

Known reviewer notes

  • Two historical QA artifacts still use the old stdio/http/sse wording: docs/qa/charters/CH-mcp-authorize-repair-truth.md and docs/qa/scenarios/MS-029.md. They do not define the current runtime contract.
  • The public MCP JSON guide documents mcp.oauth.redirect_uri; a consolidated canonical config.toml reference remains a documentation follow-up.
  • The branch also contains an existing lockfile-only @assistant-ui/* version change from the preceding spec commit; there is no corresponding package manifest change in this implementation commit.

Summary by CodeRabbit

  • New Features

    • Added support for marketplace MCP launch types, including npm, uvx, Docker, and remote HTTP servers.
    • Added typed installation inputs, defaults, secret bindings, and automatic scope selection.
    • Added MCP authentication status viewing and improved OAuth registration support.
    • Added explicit scope-escalation review and approval during authorization.
    • Added protocol-version visibility and improved tool discovery.
  • Bug Fixes

    • Improved redirect validation, secret redaction, version formatting, and secure remote connection handling.
    • Removed duplicate version prefixes in marketplace displays.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Too many files changed for review. (302 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@vercel

vercel Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Jul 31, 2026 1:36am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c680387b-f83e-4016-87de-12330212a347

📥 Commits

Reviewing files that changed from the base of the PR and between e772c2f and 7c53393.

⛔ Files ignored due to path filters (65)
  • bun.lock is excluded by !**/*.lock, !**/*.lock
  • catalog/extensions.json is excluded by !**/*.json
  • catalog/mcp.json is excluded by !**/*.json
  • catalog/skills.json is excluded by !**/*.json
  • docs/qa/charters/CH-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/journeys/J-mcp-authorize-repair.md is excluded by !**/*.md
  • docs/qa/journeys/J-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/reports/2026-07-30-mcp-2026-catalog-v2-plan.md is excluded by !**/*.md
  • docs/qa/reports/2026-07-30-mcp-2026-catalog-v2.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-046.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-047.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-048.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-049.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-marketplace-namespace.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-mcp-catalog-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-mcp-oauth-endpoints.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-info.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-refresh.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-search.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-auth-manual-exchange.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-authorize.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-compozy-native-tool-invocation.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-compozy-official-skill-discovery.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-site-marketplace-catalog.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-marketplace-installed-management.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-marketplace-mcp-authorize-installed.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-authorize-manual.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-authorize.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-guided-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-remote-editor.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-status-matrix.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-workspace-host-api-mcp.md is excluded by !**/*.md
  • docs/qa/scenarios/MS-029.md is excluded by !**/*.md
  • docs/qa/scenarios/MS-web-mcp-editor-simple-advanced.md is excluded by !**/*.md
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • internal/marketplace/testdata/catalog/missing-digest/extensions.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/missing-digest/mcp.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/missing-digest/skills.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/extensions.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/mcp.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/skills.json is excluded by !**/*.json
  • internal/store/globaldb/schema/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
  • openapi/compozy.json is excluded by !**/*.json
  • packages/site/content/docs/cli/mcp/auth/login.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/mcp/install.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/config-toml.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/lifecycle-matrix.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/mcp-json.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/index.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/mcp-catalog.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/meta.json is excluded by !**/*.json
  • packages/site/public/.well-known/mcp-client.json is excluded by !**/*.json
  • skills/compozy/references/tools-and-skills.md is excluded by !**/*.md
  • web/e2e/__tests__/marketplace.spec.ts is excluded by !web/e2e/**
  • web/e2e/fixtures/__tests__/runtime.test.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/marketplace-server.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/runtime-seed.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/package.json is excluded by !**/*.json
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
  • web/src/systems/marketplace/components/stories/marketplace-overlays.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/components/stories/mcp-authorize-dialog.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/components/stories/mcp-server-editor.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/routes/mcp-servers.stories.tsx is excluded by !**/*.stories.tsx
📒 Files selected for processing (237)
  • go.mod
  • internal/acp/client_test.go
  • internal/api/contract/marketplace.go
  • internal/api/contract/mcp_catalog_install.go
  • internal/api/contract/settings_collection_payloads.go
  • internal/api/contract/settings_mcp_auth.go
  • internal/api/core/agent_contracts_test.go
  • internal/api/core/marketplace_detail.go
  • internal/api/core/marketplace_installed_detail.go
  • internal/api/core/marketplace_list.go
  • internal/api/core/marketplace_test.go
  • internal/api/core/settings_collection_requests.go
  • internal/api/core/settings_mcp_auth.go
  • internal/api/core/settings_mcp_conversion.go
  • internal/api/core/settings_mcp_install.go
  • internal/api/core/settings_test.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/helpers_test.go
  • internal/api/httpapi/routes.go
  • internal/api/spec/schema_customizers.go
  • internal/api/spec/settings_mcp_auth.go
  • internal/api/spec/settings_mcp_schema.go
  • internal/api/spec/settings_test.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/helpers_test.go
  • internal/api/udsapi/hosted_mcp.go
  • internal/api/udsapi/routes.go
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/cli/client_mcp.go
  • internal/cli/client_mcp_test.go
  • internal/cli/helpers_test.go
  • internal/cli/mcp_auth.go
  • internal/cli/mcp_auth_flow.go
  • internal/cli/mcp_auth_output.go
  • internal/cli/mcp_auth_test.go
  • internal/cli/mcp_install.go
  • internal/cli/mcp_install_input.go
  • internal/cli/mcp_install_output.go
  • internal/cli/mcp_install_test.go
  • internal/cli/mcp_serve.go
  • internal/cli/mcp_serve_test.go
  • internal/config/agent_resource_test.go
  • internal/config/agent_test.go
  • internal/config/config_extensions_sandbox.go
  • internal/config/config_refac_test.go
  • internal/config/config_test.go
  • internal/config/config_validation.go
  • internal/config/defaults.go
  • internal/config/lifecycle/lifecycle.go
  • internal/config/lifecycle/lifecycle_test.go
  • internal/config/mcp.go
  • internal/config/mcp_resource.go
  • internal/config/mcp_server_validation.go
  • internal/config/mcpjson_test.go
  • internal/config/merge.go
  • internal/config/merge_features.go
  • internal/config/merge_mcp.go
  • internal/config/merge_overlay.go
  • internal/config/provider.go
  • internal/config/provider_resolved_validation.go
  • internal/config/provider_test.go
  • internal/config/provider_validation.go
  • internal/daemon/daemon_mcp_serve_e2e_integration_test.go
  • internal/daemon/daemon_memory_e2e_integration_test.go
  • internal/daemon/daemon_mock_agents_integration_test.go
  • internal/daemon/marketplace_test.go
  • internal/daemon/mcp_dead_entity_integration_test.go
  • internal/daemon/native_extension_tool_provider.go
  • internal/daemon/native_mcp_provider.go
  • internal/daemon/settings.go
  • internal/daemon/settings_mcp_auth.go
  • internal/daemon/settings_mcp_runtime.go
  • internal/daemon/settings_runtime_dependencies.go
  • internal/daemon/settings_test.go
  • internal/daemon/tool_approval_grants_integration_test.go
  • internal/daemon/tool_mcp_resources_clone_test.go
  • internal/daemon/tool_mcp_resources_encoding.go
  • internal/marketplace/entry_mcp.go
  • internal/marketplace/entry_mcp_input.go
  • internal/marketplace/entry_mcp_launch.go
  • internal/marketplace/errors.go
  • internal/marketplace/projection.go
  • internal/marketplace/service_integration_test.go
  • internal/marketplace/source.go
  • internal/marketplace/source_test.go
  • internal/marketplace/store_test.go
  • internal/marketplace/types.go
  • internal/mcp/auth/client_auth.go
  • internal/mcp/auth/dynamic_registration.go
  • internal/mcp/auth/exchange_input.go
  • internal/mcp/auth/fingerprint.go
  • internal/mcp/auth/manager.go
  • internal/mcp/auth/manager_test.go
  • internal/mcp/auth/metadata.go
  • internal/mcp/auth/metadata_test.go
  • internal/mcp/auth/pre_registered.go
  • internal/mcp/auth/sdk_adapter.go
  • internal/mcp/auth/sdk_adapter_test.go
  • internal/mcp/auth/service.go
  • internal/mcp/auth/service_direct_endpoint_test.go
  • internal/mcp/auth/service_expires_in_test.go
  • internal/mcp/auth/service_options.go
  • internal/mcp/auth/service_test.go
  • internal/mcp/auth/service_token_lifecycle.go
  • internal/mcp/auth/step_up.go
  • internal/mcp/auth/types.go
  • internal/mcp/auth/types_test.go
  • internal/mcp/authorization_required.go
  • internal/mcp/executor.go
  • internal/mcp/executor_auth.go
  • internal/mcp/executor_cache.go
  • internal/mcp/executor_client.go
  • internal/mcp/executor_setup.go
  • internal/mcp/executor_test.go
  • internal/mcp/executor_tools.go
  • internal/mcp/hosted.go
  • internal/mcp/hosted_binding.go
  • internal/mcp/hosted_bootstrap_projection.go
  • internal/mcp/hosted_proxy.go
  • internal/mcp/hosted_proxy_result.go
  • internal/mcp/hosted_proxy_result_test.go
  • internal/mcp/hosted_proxy_test.go
  • internal/mcp/hosted_records.go
  • internal/mcp/hosted_test.go
  • internal/mcp/insufficient_scope.go
  • internal/mcp/protocol.go
  • internal/mcp/securehttp/body.go
  • internal/mcp/securehttp/client.go
  • internal/mcp/securehttp/client_test.go
  • internal/mcp/securehttp/policy.go
  • internal/mcp/securehttp/transport.go
  • internal/mcp/serve.go
  • internal/mcp/serve_http.go
  • internal/mcp/serve_projection.go
  • internal/mcp/serve_test.go
  • internal/mcp/tool_descriptor.go
  • internal/mcp/unsupported_capability.go
  • internal/settings/collection_hook_mcp_map.go
  • internal/settings/collection_provider_write.go
  • internal/settings/config_reload_diff.go
  • internal/settings/mcp_auth.go
  • internal/settings/mcp_catalog_input.go
  • internal/settings/mcp_catalog_install.go
  • internal/settings/mcp_collection_probes.go
  • internal/settings/mcp_mutation.go
  • internal/settings/mcp_runtime_status_test.go
  • internal/settings/mcp_target_test.go
  • internal/settings/models_mcp.go
  • internal/settings/service.go
  • internal/settings/service_contract.go
  • internal/settings/service_integration_test.go
  • internal/settings/service_test.go
  • internal/store/globaldb/global_db_marketplace_catalog_test.go
  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go
  • internal/store/globaldb/global_db_mcp_auth_test.go
  • internal/store/globaldb/global_db_mcp_oauth_registration.go
  • internal/store/globaldb/global_db_session_test.go
  • internal/store/globaldb/global_db_test.go
  • internal/store/globaldb/global_db_workspace.go
  • internal/store/globaldb/queries/vault_mcp.sql
  • internal/store/globaldb/schema/definitions/35_mcp.sql
  • internal/store/globaldb/schema/migrations/00031_schema.sql
  • internal/store/globaldb/schema/migrations/00032_marketplace_catalog_manifest_v2.sql
  • internal/store/globaldb/schema/migrations/00033_schema.sql
  • internal/store/globaldb/sqlcgen/models.go
  • internal/store/globaldb/sqlcgen/vault_mcp.sql.go
  • internal/testutil/mcpfixture/fixture.go
  • internal/testutil/mcpfixture/fixture_test.go
  • internal/testutil/mcpfixture/http.go
  • internal/testutil/mcpfixture/oauth.go
  • internal/testutil/mcpfixture/oauth_test.go
  • internal/testutil/mcpfixture/profile.go
  • internal/tools/dispatch.go
  • internal/tools/mcp_discovery.go
  • internal/tools/registry.go
  • internal/tools/registry_index.go
  • internal/tools/registry_session_projection.go
  • internal/tools/registry_test.go
  • internal/vault/mcp_refs.go
  • internal/vault/types_test.go
  • magefiles/command.go
  • magefiles/gotest_lane_test.go
  • magefiles/test.go
  • packages/site/app/marketplace/[kind]/page.tsx
  • packages/site/components/marketplace/marketplace-entry-card.tsx
  • packages/site/components/marketplace/marketplace-entry-detail.tsx
  • packages/site/components/site/__tests__/sidebar-compact-tree.test.tsx
  • packages/site/lib/__tests__/marketplace-catalog.test.tsx
  • packages/site/lib/marketplace-catalog-validation.ts
  • packages/site/lib/marketplace-catalog.ts
  • web/src/lib/__tests__/settings-api-contract.test.ts
  • web/src/systems/agent/__tests__/types.test.ts
  • web/src/systems/agent/routes/agent-detail-story-fixtures.ts
  • web/src/systems/marketplace/adapters/__tests__/marketplace-api.test.ts
  • web/src/systems/marketplace/components/__tests__/marketplace-action-controller.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-components.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-detail-manage.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-ui.test.ts
  • web/src/systems/marketplace/components/__tests__/mcp-install-dialog-store.test.ts
  • web/src/systems/marketplace/components/index.ts
  • web/src/systems/marketplace/components/marketplace-card.tsx
  • web/src/systems/marketplace/components/marketplace-detail-extension-actions.tsx
  • web/src/systems/marketplace/components/marketplace-detail-mcp-manage.tsx
  • web/src/systems/marketplace/components/marketplace-detail-meta.tsx
  • web/src/systems/marketplace/components/marketplace-detail.tsx
  • web/src/systems/marketplace/components/marketplace-entry-actions.tsx
  • web/src/systems/marketplace/components/marketplace-installed-card.tsx
  • web/src/systems/marketplace/components/marketplace-ui.ts
  • web/src/systems/marketplace/components/mcp-install-dialog.tsx
  • web/src/systems/marketplace/components/mcp-install-model.ts
  • web/src/systems/marketplace/components/use-marketplace-action-controller.tsx
  • web/src/systems/marketplace/components/use-mcp-install-dialog.ts
  • web/src/systems/marketplace/lib/__tests__/mcp-installed-status.test.ts
  • web/src/systems/marketplace/mocks/fixtures.ts
  • web/src/systems/marketplace/mocks/handlers.ts
  • web/src/systems/settings/adapters/__tests__/settings-mcp-auth-api.test.ts
  • web/src/systems/settings/components/__tests__/mcp-authorize-dialog.test.tsx
  • web/src/systems/settings/components/__tests__/mcp-editor-secret-presence.test.tsx
  • web/src/systems/settings/components/__tests__/mcp-server-editor.test.tsx
  • web/src/systems/settings/components/mcp-authorize-dialog.tsx
  • web/src/systems/settings/components/mcp-editor-connection-section.tsx
  • web/src/systems/settings/components/mcp-editor-oauth-section.tsx
  • web/src/systems/settings/components/mcp-server-editor.tsx
  • web/src/systems/settings/hooks/__tests__/use-mcp-authorize.test.tsx
  • web/src/systems/settings/hooks/__tests__/use-settings-mutations.test.tsx
  • web/src/systems/settings/hooks/use-mcp-authorize.ts
  • web/src/systems/settings/hooks/use-settings-mutations.ts
  • web/src/systems/settings/index.ts
  • web/src/systems/settings/lib/__tests__/mcp-editor-model.test.ts
  • web/src/systems/settings/lib/__tests__/mcp-status-view-model.test.ts
  • web/src/systems/settings/lib/mcp-authorize-model.ts
  • web/src/systems/settings/lib/mcp-editor-model.ts
  • web/src/systems/settings/lib/mcp-status-view-model.ts
  • web/src/systems/settings/mocks/__tests__/handlers.test.ts
  • web/src/systems/settings/mocks/fixtures.ts
  • web/src/systems/settings/stores/__tests__/mcp-authorize-store.test.ts
  • web/src/systems/settings/stores/mcp-authorize-store.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

MCP support was migrated to the official Go SDK, with typed marketplace launch and input contracts, daemon-mediated OAuth flows, secure HTTP clients, protocol negotiation, deferred discovery, durable registration persistence, updated CLI/API/UI integrations, and manifest version 2.

Changes

MCP modernization

Layer / File(s) Summary
MCP contracts and configuration
internal/api/contract/*, internal/config/*, internal/mcp/auth/types.go
MCP transport and OAuth models now use HTTP/stdio transports, registration strategies, typed inputs, configured redirect URIs, and global OAuth client settings.
Marketplace schema and installation flow
internal/marketplace/*, internal/settings/mcp_catalog_*, internal/cli/mcp_install*, packages/site/lib/marketplace-catalog*
Marketplace manifests use version 2 with typed npm, uvx, Docker, and remote launches, validated bindings, normalized defaults, and unified input assignments.
OAuth lifecycle and API integration
internal/mcp/auth/*, internal/settings/mcp_auth.go, internal/api/*, internal/cli/mcp_auth*, web/src/systems/settings/*
OAuth discovery, dynamic registration, redirect exchange, status retrieval, token validation, scope escalation, and authorization-state deletion were added or revised across daemon, API, CLI, and web flows.
SDK execution and hosted MCP
internal/mcp/executor*, internal/mcp/serve*, internal/mcp/hosted*, internal/tools/registry_session_projection.go
MCP execution and hosted proxying now use the official SDK, negotiated protocol versions, tool-list caching, secure HTTP, deferred discovery, bootstrap projections, and SDK result conversion.
Registration persistence and migration
internal/store/globaldb/*, internal/vault/mcp_refs.go
Target-scoped OAuth registrations, credential references, atomic cleanup, workspace deletion handling, and marketplace manifest migrations were added.
Fixtures and validation coverage
internal/testutil/mcpfixture/*, internal/*/*_test.go, web/src/**/__tests__/*, packages/site/**/__tests__/*
SDK-based MCP and OAuth fixtures plus tests cover protocol compatibility, secure destinations, registration lifecycle, input validation, scope escalation, persistence, and updated UI contracts.

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

Possibly related PRs

  • compozy/compozy#240 — Both PRs modify MCP SDK integration, authentication, and transport handling.
  • compozy/compozy#276 — Both PRs change MCP CLI authentication exchange and timeout behavior.
  • compozy/compozy#277 — Both PRs update marketplace catalog schemas and MCP marketplace rendering.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary changes: adopting MCP 2026 and expanding the curated marketplace catalog.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mcps

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.

@github-actions

github-actions Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 7c53393.

@coderabbitai coderabbitai 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.

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
internal/api/core/settings_mcp_auth.go (1)

121-130: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Manual mode can return an empty callback URL.

Automatic mode validates MCP.OAuth.RedirectURI (scheme, host, loopback), but manual mode returns the trimmed value with a nil error. If redirect_uri is unset, BeginMCPAuth receives CallbackURL: "" and the operator gets an authorization URL that cannot complete, with no actionable diagnostic. Reject the blank case here unless the settings service already enforces it.

🛡️ Proposed guard
 	case contract.SettingsMCPAuthBeginModeManual:
-		return strings.TrimSpace(h.Config.MCP.OAuth.RedirectURI), nil
+		redirectURI := strings.TrimSpace(h.Config.MCP.OAuth.RedirectURI)
+		if redirectURI == "" {
+			return "", errors.New("settings: MCP OAuth redirect_uri is required")
+		}
+		return redirectURI, nil
#!/bin/bash
# Check whether BeginMCPAuth validates a blank CallbackURL, and whether a default redirect_uri exists.
rg -nP --type=go -C6 'func .*BeginMCPAuth\(' internal/settings internal/daemon internal/mcp/auth
rg -nP --type=go -C3 'RedirectURI' internal/config
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/core/settings_mcp_auth.go` around lines 121 - 130, Update
mcpOAuthCallbackURLForMode for manual mode to reject a trimmed-empty
MCP.OAuth.RedirectURI with an actionable error instead of returning it with nil
error; preserve the existing non-empty manual value and automatic-mode behavior,
unless an existing settings validation already guarantees this.
internal/mcp/auth/types.go (1)

292-306: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate no longer constrains the auth type.

Only emptiness is checked, so any Type value passes even though authTypeOAuth is the single supported mode. ServerConfigFromMCP always sets oauth, but configs assembled elsewhere would silently reach the OAuth service.

#!/bin/bash
# Find ServerConfig literals built outside ServerConfigFromMCP and inspect the Type they set.
ast-grep run --pattern 'auth.ServerConfig{$$$}' --lang go
rg -nP --type=go -C3 'ServerConfig\{' internal | rg -n 'Type:' -C3
🛡️ Optional tightening
-	case strings.TrimSpace(c.Type) == "":
-		return errors.New("mcp auth: auth type is required")
+	case strings.TrimSpace(c.Type) == "":
+		return errors.New("mcp auth: auth type is required")
+	case strings.TrimSpace(c.Type) != authTypeOAuth:
+		return fmt.Errorf("mcp auth: unsupported auth type %q", c.Type)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/auth/types.go` around lines 292 - 306, Update
ServerConfig.Validate to reject any non-empty Type other than the supported
authTypeOAuth value, while preserving the existing required-type, client-ID, and
registration-strategy validation. Anchor the change in the Type validation case
within Validate and return an appropriate validation error for unsupported
types.
internal/daemon/native_mcp_provider.go (1)

17-24: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Confirm MCP call timeouts are still enforced after removing the default probe timeout option.

options no longer seeds mcppkg.WithTimeout with the configured agent probe timeout, per the line-range summary. Coding guidelines require explicit timeouts on outbound network calls; please confirm mcppkg.NewMCPCallExecutor (or its underlying transport) still enforces a bound when no WithTimeout option is supplied, otherwise MCP calls could rely solely on caller-supplied context deadlines.

As per coding guidelines, "All outbound HTTP/network calls must use clients with explicit timeouts; http.DefaultClient is forbidden in production paths."

#!/bin/bash
# Inspect the MCP call executor for a fallback/default timeout when WithTimeout is not supplied.
rg -n -A5 -B5 'func NewMCPCallExecutor|func WithTimeout' internal/mcp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/daemon/native_mcp_provider.go` around lines 17 - 24, Verify the
timeout behavior of mcppkg.NewMCPCallExecutor and its underlying transport when
options excludes mcppkg.WithTimeout; if no explicit fallback exists, restore or
supply the configured agent probe timeout in the resolver’s options. Ensure
every outbound MCP network call retains a bounded timeout without relying only
on caller context deadlines.
internal/daemon/daemon_mcp_serve_e2e_integration_test.go (1)

33-37: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Register the timeout cancellation with t.Cleanup.

cancel() is a plain defer in the test body, while startMCPServeClient registers session.Close() via t.Cleanup. Go runs the test function’s own deferred statements before t.Cleanup callbacks, so the context is canceled—and the subprocess managed by exec.CommandContext is shut down by that time. Tuck cancel() into t.Cleanup so the teardown ordering stays explicit and does not depend on the SDK’s handling of an already-terminated command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/daemon/daemon_mcp_serve_e2e_integration_test.go` around lines 33 -
37, Update the timeout setup in the test to register cancel with t.Cleanup
instead of deferring it directly, preserving cleanup ordering with
startMCPServeClient’s session.Close callback.
internal/mcp/auth/manager.go (1)

446-458: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Store I/O now runs while m.mu is held.

tokenMatchesServerDefinition performs a registration-store lookup (and persistReplacementTokenAtGeneration a write), both executed under the manager mutex taken at Line 446. Previously the match was a pure fingerprint comparison. Every other manager operation (Begin, Invalidate, claims) now blocks on that database round-trip. Consider validating the revision/generation under the lock, releasing it, then performing the lookup and persistence with a re-check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/auth/manager.go` around lines 446 - 458, The token replacement
flow currently holds m.mu during store lookup and persistence; update it to
validate the session revision/generation under the lock, release the lock before
tokenMatchesServerDefinition and persistReplacementTokenAtGeneration, then
re-acquire and re-check the session state before committing so concurrent
invalidation cannot succeed.
internal/daemon/settings_mcp_runtime.go (1)

96-112: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Confirm the MCP probe still has a bounded timeout.

The default 5s probe timeout and the WithProbeTimeout-style option were removed from newMCPProbeExecutor, and MCPServerRuntimeStatus now depends entirely on the caller's ctx for cancellation. If this is invoked with an undeadlined context (as settings_test.go now does with context.Background()), a slow/unresponsive remote MCP server could block the probe indefinitely.

Please confirm mcppkg.NewMCPCallExecutor/CallExecutor enforces its own internal timeout independent of this removed option; if not, this call site should wrap ctx with an explicit timeout before invoking ListToolsWithProtocol.

#!/bin/bash
# Inspect executor construction for any default/internal timeout enforcement.
ast-grep outline internal/mcp --items all --type function --match 'NewMCPCallExecutor|ListToolsWithProtocol'
rg -n --type=go -C5 'func NewMCPCallExecutor' internal/mcp
rg -n --type=go -C5 'func.*ListToolsWithProtocol' internal/mcp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/daemon/settings_mcp_runtime.go` around lines 96 - 112, Verify
whether mcppkg.NewMCPCallExecutor or CallExecutor provides an internal probe
timeout independent of the caller context. If it does not, update
MCPServerRuntimeStatus to derive a context with the established 5-second timeout
before invoking ListToolsWithProtocol, while preserving cancellation from the
caller; do not rely on newMCPProbeExecutor alone to enforce the bound.
internal/settings/service_test.go (1)

3201-3243: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Subtest name contradicts its assertions.

Should garbage collect a superseded install-owned ref asserts the opposite: after reinstalling with VaultRef: ownedRef, the ref must still be Present (Lines 3239-3242). As written it is a near duplicate of the following retention test, and no case actually covers collecting a superseded owned ref. Rename to reflect retention, or make it assert real collection so it fails if GC behavior regresses.

♻️ Rename to match the asserted behavior
-	t.Run("Should garbage collect a superseded install-owned ref", func(t *testing.T) {
+	t.Run("Should retain an owned ref supplied explicitly as a vault_ref", func(t *testing.T) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/settings/service_test.go` around lines 3201 - 3243, Rename the
subtest beginning with “Should garbage collect a superseded install-owned ref”
to describe retention of the superseded install-owned reference, matching its
assertion that metadata remains Present after replacement with VaultRef:
ownedRef. Leave the test behavior and assertions unchanged.
🟡 Minor comments (20)
web/src/systems/settings/stores/mcp-authorize-store.ts-244-248 (1)

244-248: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalid redirect URL is a silent no-op.

When an operator pastes something that isn't an absolute MCP redirect URL, the reducer returns without transitioning and without recording an error, so the dialog appears frozen. Consider transitioning to the completion-failure state (or having the caller validate and surface a message) so the error state is explicit.

As per coding guidelines, "Handle loading, error, and empty states explicitly".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/settings/stores/mcp-authorize-store.ts` around lines 244 -
248, The manualRedirectSubmitted handler silently returns for invalid redirect
URLs, leaving the dialog in its current phase. Update this handler to explicitly
transition to the completion-failure/error state and record the invalid URL
error, while preserving the existing exchange flow for valid absolute MCP
redirect URLs.

Source: Coding guidelines

web/src/systems/settings/stores/mcp-authorize-store.ts-191-214 (1)

191-214: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

authorizeRequested forwards un-normalized approved scopes.

scopeEscalationRequested stores normalizeMCPAuthScopes(event.approvedScopes) (Line 160), but here event.scopeApproval is validated through hasExplicitScopeApproval — which normalizes only for the length check — and then stored and sent to begin as-is. The two entry points therefore put differently-shaped scope lists on the wire.

🐛 Normalize once before storing and forwarding
       authorizeRequested: (context, event, enqueue) => {
         if (context.phase !== "idle") return;
         if (event.scopeApproval && !hasExplicitScopeApproval(event.scopeApproval)) return;
+        const scopeApproval = event.scopeApproval
+          ? {
+              approveScopeEscalation: event.scopeApproval.approveScopeEscalation,
+              approvedScopes: normalizeMCPAuthScopes(event.scopeApproval.approvedScopes),
+            }
+          : undefined;
         const attemptId = context.attemptId + 1;
         enqueueBegin(
           event.begin,
           enqueue,
           attemptId,
           event.server,
           event.filter,
           event.mode,
-          event.scopeApproval
+          scopeApproval
         );
         return {
           phase: "beginning",
           attemptId,
           dismissOnConfirmation: event.dismissOnConfirmation,
           server: event.server,
           filter: event.filter,
           prior: event.prior,
           mode: event.mode,
-          scopeApproval: event.scopeApproval,
+          scopeApproval,
         };
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/settings/stores/mcp-authorize-store.ts` around lines 191 -
214, Update authorizeRequested to normalize event.scopeApproval once after
validation, then use the normalized value for both enqueueBegin and the returned
state’s scopeApproval field. Preserve undefined when no scope approval is
provided and keep the existing idle and validation guards unchanged.
internal/marketplace/source_test.go-290-294 (1)

290-294: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Verify the diagnostic for an older manifest.

A manifest_version: 1 feed is asserted to report client too old, which inverts the actual situation (the feed is older than the client). If UnsupportedManifestVersionError emits one message for both directions, operators reading logs for a stale feed will be told to upgrade the client.

#!/bin/bash
# Inspect the unsupported-version error construction and its wording.
rg -nP --type=go -C8 'UnsupportedManifestVersionError' internal/marketplace
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/marketplace/source_test.go` around lines 290 - 294, Correct the
expected diagnostic in the “Should reject a v1 manifest” test to describe an
older feed rather than an outdated client, and inspect
UnsupportedManifestVersionError to ensure its wording distinguishes manifest
versions older than the client from versions newer than the client.
internal/api/core/settings_mcp_install.go-62-62 (1)

62-62: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Confirm the settings service resolves an omitted scope.

scope became optional and the required-scope check was dropped, so MCPCatalogInstallRequest.Scope can now be "" while workspace_id may still be present. Unless InstallMCPCatalog defaults and re-validates the scope/workspace pairing, an install can land in an unclassified scope — the changed tests always send an explicit scope, so this path is uncovered.

#!/bin/bash
# Verify scope defaulting/validation for catalog installs.
rg -nP --type=go -C10 'func .*InstallMCPCatalog\(' internal/settings internal/daemon
rg -nP --type=go -C4 'Scope\s*==\s*""' internal/settings

As per coding guidelines, "workspace data must be classified by scope and checked for cross-workspace leakage".

Also applies to: 76-80, 97-103

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/core/settings_mcp_install.go` at line 62, Update
InstallMCPCatalog and its MCPCatalogInstallRequest handling to resolve an
omitted Scope before installation. Default the scope using the existing settings
behavior, then validate the scope/workspace_id pairing so workspace data is
classified and cross-workspace installs are rejected; preserve explicit-scope
behavior and apply the same handling to the related request construction and
validation paths.
internal/mcp/executor.go-156-190 (1)

156-190: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Authorization errors are masked once the call deadline elapses.

Lines 157-166 overwrite err with ctx.Err()/DeadlineExceeded before the scope/authorization checks, so a 401 or insufficient_scope that arrives as the deadline expires is reported as a timeout and ReasonMCPAuthRequired never reaches the caller — the re-authorize path is lost. Classify auth first, then fall back to context normalization.

🐛 Proposed fix
 func normalizeMCPErrorWithContext(ctx context.Context, id toolspkg.ToolID, err error, discovery bool) error {
+	var scopeErr *InsufficientScopeError
+	if errors.As(err, &scopeErr) {
+		return toolspkg.NewToolError(
+			toolspkg.ErrorCodeUnavailable,
+			id,
+			scopeErr.Error(),
+			scopeErr,
+			toolspkg.ReasonMCPAuthRequired,
+		)
+	}
+	if errors.Is(err, ErrAuthorizationRequired) {
+		return toolspkg.NewToolError(
+			toolspkg.ErrorCodeUnavailable,
+			id,
+			ErrAuthorizationRequired.Error(),
+			ErrAuthorizationRequired,
+			toolspkg.ReasonMCPAuthRequired,
+		)
+	}
 	if ctxErr := ctx.Err(); ctxErr != nil {
 		err = ctxErr
 	} else if deadline, hasDeadline := ctx.Deadline(); hasDeadline && !time.Now().Before(deadline) {
 		err = context.DeadlineExceeded
 	} else {
 		var networkError net.Error
 		if errors.As(err, &networkError) && networkError.Timeout() {
 			err = context.DeadlineExceeded
 		}
 	}
-	var scopeErr *InsufficientScopeError
-	if errors.As(err, &scopeErr) {
-		return toolspkg.NewToolError(
-			toolspkg.ErrorCodeUnavailable,
-			id,
-			scopeErr.Error(),
-			scopeErr,
-			toolspkg.ReasonMCPAuthRequired,
-		)
-	}
-	if errors.Is(err, ErrAuthorizationRequired) {
-		return toolspkg.NewToolError(
-			toolspkg.ErrorCodeUnavailable,
-			id,
-			ErrAuthorizationRequired.Error(),
-			ErrAuthorizationRequired,
-			toolspkg.ReasonMCPAuthRequired,
-		)
-	}
 	if discovery {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/executor.go` around lines 156 - 190, Update
normalizeMCPErrorWithContext to perform the InsufficientScopeError and
ErrAuthorizationRequired checks before overwriting err with context or timeout
errors, preserving the existing auth-specific ToolError responses; only apply
context/deadline normalization when the original error is not
authorization-related, then retain the existing discovery and general error
handling.
internal/mcp/executor.go-192-216 (1)

192-216: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Bound tools/list pagination and dereference safely.

ListToolsResult has no GetTTLMs() method here, so reading the TTL should go through the SDK’s actual cache/TTL field if required. Pagination also has no cap: keep adding pages until the context deadline if NextCursor never stabilizes or terminates, so enforce a page limit. A nil result guard could avoid a panic if the SDK/transport returns nil before an error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/executor.go` around lines 192 - 216, The listMCPTools pagination
flow must use the SDK-supported cache/TTL field instead of calling the
unavailable result.GetTTLMs() method, guard against a nil result before
dereferencing Tools or NextCursor, and enforce a finite maximum page count while
preserving repeated-cursor detection and context/error propagation.
internal/config/provider_test.go-1283-1305 (1)

1283-1305: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Wrap the two validation cases in t.Run("Should ...") subtests.

This test asserts two distinct behaviors (valid pre-registered OAuth passes; missing ClientID fails) sequentially in one function, unlike the table-driven t.Run pattern used by the neighboring tests in this file.

As per path instructions, "MUST use t.Run("Should...") pattern for ALL test cases."

♻️ Suggested refactor
func TestMCPServerValidateSupportsRemotePreRegisteredOAuth(t *testing.T) {
	t.Parallel()

	server := MCPServer{
		Name:      "linear",
		Transport: MCPServerTransportHTTP,
		URL:       "https://mcp.example/mcp",
		Auth: MCPAuthConfig{
			Registration: MCPAuthRegistrationPreRegistered,
			IssuerURL:    "https://auth.example",
			ClientID:     "client-1",
			Scopes:       []string{"read", "write"},
		},
	}

	t.Run("Should validate a pre-registered remote OAuth config", func(t *testing.T) {
		t.Parallel()
		if err := server.Validate("mcp_servers[0]"); err != nil {
			t.Fatalf("Validate(remote OAuth) error = %v", err)
		}
	})

	t.Run("Should reject a missing pre-registered client ID", func(t *testing.T) {
		t.Parallel()
		invalid := server
		invalid.Auth.ClientID = ""
		if err := invalid.Validate("mcp_servers[0]"); err == nil {
			t.Fatal("Validate(missing pre-registered client ID) error = nil, want validation failure")
		}
	})
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/config/provider_test.go` around lines 1283 - 1305, Refactor
TestMCPServerValidateSupportsRemotePreRegisteredOAuth to place each validation
case in its own t.Run subtest using the “Should ...” naming pattern: one subtest
for accepting valid pre-registered remote OAuth and one for rejecting a missing
ClientID. Keep the cases independent by validating a copied server with the
empty ClientID, and preserve parallel execution where appropriate.

Source: Path instructions

internal/mcp/auth/fingerprint.go-113-121 (1)

113-121: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Trim both sides of the resource URL comparison.

The left side is trimmed but firstNonEmpty(cfg.ResourceURL, cfg.RemoteURL) is compared raw. If the config value carries surrounding whitespace, the binding check fails and forces an unnecessary re-authorization. Please confirm firstNonEmpty trims; otherwise wrap the right side in strings.TrimSpace.

#!/bin/bash
ast-grep run --pattern 'func firstNonEmpty($$$) $_ { $$$ }' --lang go internal/mcp/auth
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/auth/fingerprint.go` around lines 113 - 121, Update the resource
URL comparison in the registration/token binding logic to trim the result of
firstNonEmpty(cfg.ResourceURL, cfg.RemoteURL) with strings.TrimSpace, unless
firstNonEmpty already guarantees trimming. Preserve the existing fallback
selection and other comparison checks.
internal/mcp/auth/sdk_adapter.go-199-211 (1)

199-211: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Well-known path candidate retains the resource URL's query and fragment.

atEndpoint copies the resource URL wholesale, so https://host/mcp?tenant=a#x yields /.well-known/oauth-protected-resource/mcp?tenant=a#x. The root candidate already clears both; do the same here to build a conformant URL and avoid forwarding resource query params to the metadata endpoint.

🐛 Proposed fix
 	atEndpoint := *resource
 	atEndpoint.Path = "/.well-known/oauth-protected-resource/" + strings.TrimLeft(
 		resource.Path,
 		"/",
 	)
+	atEndpoint.RawQuery = ""
+	atEndpoint.Fragment = ""
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/auth/sdk_adapter.go` around lines 199 - 211, Update the
atEndpoint construction in the resource URL candidate logic to clear the copied
query and fragment fields before appending its metadata URL. Preserve the
existing host, scheme, and transformed path behavior while ensuring the
well-known candidate never forwards resource query parameters or fragments.
web/src/systems/marketplace/components/marketplace-detail-extension-actions.tsx-185-185 (1)

185-185: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

formatMarketplaceVersion's empty-input contract is unspecified, and one caller assumes undefined. The consent dialog falls back with ??, which only triggers for null/undefined, while the sibling branch at Line 87 of the same file and marketplace-card.tsx treat the result as possibly empty-string. The new test table never pins the empty case, so neither assumption is enforced.

  • web/src/systems/marketplace/components/marketplace-detail-extension-actions.tsx#L185-L185: switch the fallback to || so an empty formatted version still renders "the newer release".
  • web/src/systems/marketplace/components/__tests__/marketplace-ui.test.ts#L42-L49: add ["", ""] (and a whitespace-only input) to the table so the empty-input return value becomes a pinned contract.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/src/systems/marketplace/components/marketplace-detail-extension-actions.tsx`
at line 185, Update marketplace-detail-extension-actions.tsx at lines 185-185 to
use a truthiness fallback for formatMarketplaceVersion so empty results render
“the newer release”; in marketplace-ui.test.ts lines 42-49, add table cases for
empty and whitespace-only inputs, pinning the formatter’s empty-input contract
to return an empty string.
internal/mcp/hosted_proxy_test.go-151-155 (1)

151-155: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Double session.Close() can fail the test spuriously.

The session is closed explicitly at Line 205 and again by the t.Cleanup registered at Line 151, which calls t.Errorf on any error. Rely on the cleanup only (or make the cleanup tolerant of an already-closed session). Same for the provider-protocol test: cancel() at Line 324 is not deferred, so an early t.Fatalf leaks the context.

🧪 Proposed change
 	cancel()
-	if err := session.Close(); err != nil {
-		t.Fatalf("session.Close() error = %v", err)
-	}
 	select {

Also applies to: 204-207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/hosted_proxy_test.go` around lines 151 - 155, Remove the
explicit session.Close call in the hosted-proxy test so the t.Cleanup callback
is the sole owner of session cleanup and cannot report a duplicate-close error.
Apply the same cleanup ownership to the provider-protocol test, ensuring
cancel() is deferred immediately after context creation so early t.Fatalf paths
still release the context.
internal/mcp/serve_test.go-68-76 (1)

68-76: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assertion no longer fails on a missing schema.

json.Marshal(nil) returns null, which passes both len(schema) == 0 and json.Valid, so the "schema-backed tool" invariant can no longer fail. Assert presence explicitly.

💚 Proposed fix
 			schema, err := json.Marshal(tool.InputSchema)
 			if err != nil {
 				t.Fatalf("json.Marshal(tool %q input schema) error = %v", tool.Name, err)
 			}
-			if len(schema) == 0 || !json.Valid(schema) {
+			if len(schema) == 0 || string(schema) == "null" || !json.Valid(schema) {
 				t.Fatalf("tool %q schema = %s, want valid JSON", tool.Name, schema)
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/serve_test.go` around lines 68 - 76, Update the schema
validation loop in the tool test to explicitly reject a missing or nil
tool.InputSchema before marshaling it. Preserve the existing JSON marshaling and
validity checks for present schemas so schema-backed tools must provide valid,
non-null JSON.
internal/settings/mcp_catalog_input.go-185-191 (1)

185-191: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Secret inputs ignore input.Binding.Type.

Both branches bind unconditionally to env (setMCPSecretEnvRef / secrets.SecretEnv). A catalog entry declaring a secret with a url_query (or any non-env) binding is accepted and silently mis-bound, unlike the plain-input path which rejects unsupported binding types. Reject non-env bindings here (or explicitly support them).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/settings/mcp_catalog_input.go` around lines 185 - 191, Update the
secret-input handling around setMCPSecretEnvRef and secrets.SecretEnv so it
validates input.Binding.Type before binding; reject any type other than env,
matching the plain-input path’s unsupported-binding behavior, while preserving
both existing env-reference and literal-secret handling for valid env bindings.
internal/store/globaldb/global_db_mcp_auth_atomic_test.go-136-138 (1)

136-138: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Error-path assertions in the globaldb MCP auth suite only check non-nil. All four sites force a specific failure (SQLite trigger or record validation) but assert only err == nil, so an unrelated error — validation rejecting the record before the trigger fires, a target mismatch, a closed handle — still passes and hides the rollback/validation regression under test. global_db_test.go already asserts the forced message; apply the same discipline here.

  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go#L136-L138: assert the error contains forced MCP registration update failure.
  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go#L194-L196: assert the error contains forced MCP registration secret cleanup failure.
  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go#L235-L237: assert the error contains forced authorization cleanup failure.
  • internal/store/globaldb/global_db_mcp_auth_test.go#L417-L432: assert the validation error contains access token and client URI must be set together.

As per path instructions, tests MUST have specific error assertions (ErrorContains, ErrorAs) and must avoid "weak assertions like assert.Error(t, err) without message validation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/store/globaldb/global_db_mcp_auth_atomic_test.go` around lines 136 -
138, Strengthen the error assertions in
internal/store/globaldb/global_db_mcp_auth_atomic_test.go at lines 136-138,
194-196, and 235-237 by requiring errors containing “forced MCP registration
update failure”, “forced MCP registration secret cleanup failure”, and “forced
authorization cleanup failure” respectively; update
internal/store/globaldb/global_db_mcp_auth_test.go lines 417-432 to require the
validation error containing “access token and client URI must be set together”.
Use specific message validation such as ErrorContains rather than non-nil-only
assertions, preserving the existing test scenarios around
SaveMCPAuthRegistration and authorization cleanup.

Source: Path instructions

internal/testutil/mcpfixture/fixture_test.go-185-204 (1)

185-204: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Strengthen error assertions in TestFixtureValidation.

All three checks only assert err != nil, never validating the error message/content. This can't detect whether the correct validation branch fired (e.g., "unsupported profile" vs. a completely different failure) if the underlying logic regresses.

As per path instructions, tests "MUST have specific error assertions (ErrorContains, ErrorAs)" and must avoid "Weak assertions like assert.Error(t, err) without message validation."

🧪 Proposed fix using message-specific assertions
-		if _, err := New(Profile("unsupported")); err == nil {
-			t.Fatal("New(unsupported profile) error = nil, want validation error")
+		if _, err := New(Profile("unsupported")); err == nil || !strings.Contains(err.Error(), "profile") {
+			t.Fatalf("New(unsupported profile) error = %v, want validation error mentioning profile", err)
 		}
 		fixture := MustNew(ProfileModern2026)
-		if err := fixture.RunStdio(t.Context(), nil, nil); err == nil {
-			t.Fatal("RunStdio(nil input) error = nil, want validation error")
+		if err := fixture.RunStdio(t.Context(), nil, nil); err == nil || !strings.Contains(err.Error(), "input") {
+			t.Fatalf("RunStdio(nil input) error = %v, want validation error mentioning input", err)
 		}
 		input := io.NopCloser(strings.NewReader(""))
-		if err := fixture.RunStdio(t.Context(), input, nil); err == nil {
-			t.Fatal("RunStdio(nil output) error = nil, want validation error")
+		if err := fixture.RunStdio(t.Context(), input, nil); err == nil || !strings.Contains(err.Error(), "output") {
+			t.Fatalf("RunStdio(nil output) error = %v, want validation error mentioning output", err)
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/testutil/mcpfixture/fixture_test.go` around lines 185 - 204,
Strengthen the three error checks in TestFixtureValidation by asserting each
error contains the expected validation message, not merely that an error exists.
Add message-specific assertions for New(Profile("unsupported")), RunStdio with
nil input, and RunStdio with nil output, while preserving the existing
close-error check.

Source: Path instructions

internal/testutil/mcpfixture/oauth_test.go-226-234 (1)

226-234: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the redirect status before trusting Location.

If /authorize rejects the request (e.g. missing resource match), Location is empty and url.Parse("") succeeds, so the assertion fails with a misleading iss mismatch instead of the real cause.

🐛 Proposed fix
 		response := performRequest(t, client, http.MethodGet, authorizeURL.String(), nil)
 		defer closeResponse(t, response)
+		if got, want := response.StatusCode, http.StatusFound; got != want {
+			t.Fatalf("mix-up authorize status = %d, want %d", got, want)
+		}
 		callbackURL, err := url.Parse(response.Header.Get("Location"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/testutil/mcpfixture/oauth_test.go` around lines 226 - 234, In the
authorization test around performRequest, assert that the response has the
expected redirect status before parsing or validating its Location header. Keep
the existing callbackURL parsing and issuer assertion afterward so rejected
/authorize responses fail with the actual status-related cause rather than an
empty URL mismatch.
internal/testutil/mcpfixture/oauth.go-326-338 (1)

326-338: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

continue leaves a nil map in the cloned slice.

On a marshal/unmarshal failure the pre-sized element stays nil, so Requests() silently returns a hole instead of the recorded registration, which makes any dependent assertion fail with a confusing nil dereference. Prefer appending only successful clones (or failing the clone loudly).

🐛 Proposed fix
 func cloneRegistrationSlice(values []map[string]any) []map[string]any {
-	cloned := make([]map[string]any, len(values))
-	for index, value := range values {
+	cloned := make([]map[string]any, 0, len(values))
+	for _, value := range values {
 		data, err := json.Marshal(value)
 		if err != nil {
 			continue
 		}
-		if err := json.Unmarshal(data, &cloned[index]); err != nil {
+		var entry map[string]any
+		if err := json.Unmarshal(data, &entry); err != nil {
 			continue
 		}
+		cloned = append(cloned, entry)
 	}
 	return cloned
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/testutil/mcpfixture/oauth.go` around lines 326 - 338, Update
cloneRegistrationSlice so marshal or unmarshal failures do not leave nil entries
in the returned slice; build the result by appending only successfully cloned
maps, or propagate the failure explicitly. Preserve the order of successful
registrations and ensure Requests() cannot receive holes from this helper.
packages/site/components/marketplace/marketplace-entry-card.tsx-79-86 (1)

79-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Missing separator between the launch pill and the input summary.

When inputs is present the row renders pill, inputs, separator, scope — the input summary has no separator preceding it, so it visually merges with the pill, while the same markup renders correctly when inputs is null.

🐛 Proposed fix
-      {inputs ? <span>{inputs}</span> : null}
+      {inputs ? (
+        <>
+          <MetaSeparator />
+          <span>{inputs}</span>
+        </>
+      ) : null}
       <MetaSeparator />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/site/components/marketplace/marketplace-entry-card.tsx` around lines
79 - 86, Update the JSX around the launch type Pill and conditional inputs
summary in the marketplace entry card to render a MetaSeparator between them
when inputs is present. Preserve the existing separator before the default-scope
text and avoid adding an extra separator when inputs is absent.
packages/site/lib/marketplace-catalog.ts-319-325 (1)

319-325: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Message references a launch type that no longer exists.

launch.type is npm | uvx | docker | remote; "stdio" is not one of them, so the validation error is confusing for catalog authors.

🐛 Proposed fix
-          message: "env inputs are only allowed for stdio launch",
+          message: "env inputs are only allowed for local launch (npm, uvx, docker)",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/site/lib/marketplace-catalog.ts` around lines 319 - 325, Update the
validation message in the env-binding check within the catalog validation flow
to reference the supported launch types accurately instead of “stdio”; preserve
the existing condition and issue path.
web/src/systems/settings/components/mcp-authorize-dialog.tsx-294-294 (1)

294-294: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate the trimmed value to match submission behavior.

submitManual accepts padded input (the hook trims before exchanging, per use-mcp-authorize.test.tsx line 243), but the enablement check validates the raw textarea value. A pasted URL with a leading/trailing newline would keep the button disabled with no explanation.

🛠️ Proposed fix
-        disabled={!isAbsoluteMCPRedirectURL(manualValue) || exchanging}
+        disabled={!isAbsoluteMCPRedirectURL(manualValue.trim()) || exchanging}

Please confirm whether isAbsoluteMCPRedirectURL already trims internally; if it does, this is a no-op.

#!/bin/bash
fd -t f 'mcp-authorize-model.ts' web/src --exec cat -n
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/settings/components/mcp-authorize-dialog.tsx` at line 294,
Update the manual submission button’s disabled condition near submitManual to
validate the same trimmed manualValue that submission uses, while preserving the
existing exchanging check. First verify whether isAbsoluteMCPRedirectURL trims
internally; if it does, leave this condition unchanged, otherwise pass the
trimmed value for padded URLs to enable correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: eccdddc5-3fd7-469a-a7b8-261fde51eea5

📥 Commits

Reviewing files that changed from the base of the PR and between a81b106 and 7b9da2a.

⛔ Files ignored due to path filters (65)
  • bun.lock is excluded by !**/*.lock, !**/*.lock
  • catalog/extensions.json is excluded by !**/*.json
  • catalog/mcp.json is excluded by !**/*.json
  • catalog/skills.json is excluded by !**/*.json
  • docs/qa/charters/CH-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/journeys/J-mcp-authorize-repair.md is excluded by !**/*.md
  • docs/qa/journeys/J-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/reports/2026-07-30-mcp-2026-catalog-v2-plan.md is excluded by !**/*.md
  • docs/qa/reports/2026-07-30-mcp-2026-catalog-v2.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-046.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-047.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-048.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-049.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-marketplace-namespace.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-mcp-catalog-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-api-mcp-oauth-endpoints.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-info.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-refresh.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-marketplace-search.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-auth-manual-exchange.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-authorize.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-cli-mcp-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-compozy-native-tool-invocation.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-compozy-official-skill-discovery.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-mcp-protocol-interop.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-site-marketplace-catalog.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-marketplace-installed-management.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-marketplace-mcp-authorize-installed.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-authorize-manual.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-authorize.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-guided-install.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-remote-editor.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-web-mcp-status-matrix.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-workspace-host-api-mcp.md is excluded by !**/*.md
  • docs/qa/scenarios/MS-029.md is excluded by !**/*.md
  • docs/qa/scenarios/MS-web-mcp-editor-simple-advanced.md is excluded by !**/*.md
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • internal/marketplace/testdata/catalog/missing-digest/extensions.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/missing-digest/mcp.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/missing-digest/skills.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/extensions.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/mcp.json is excluded by !**/*.json
  • internal/marketplace/testdata/catalog/valid/skills.json is excluded by !**/*.json
  • internal/store/globaldb/schema/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
  • openapi/compozy.json is excluded by !**/*.json
  • packages/site/content/docs/cli/mcp/auth/login.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/cli/mcp/install.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/config-toml.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/lifecycle-matrix.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/configuration/mcp-json.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/index.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/mcp-catalog.mdx is excluded by !**/*.mdx
  • packages/site/content/docs/marketplace/meta.json is excluded by !**/*.json
  • packages/site/public/.well-known/mcp-client.json is excluded by !**/*.json
  • skills/compozy/references/tools-and-skills.md is excluded by !**/*.md
  • web/e2e/__tests__/marketplace.spec.ts is excluded by !web/e2e/**
  • web/e2e/fixtures/__tests__/runtime.test.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/marketplace-server.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/e2e/fixtures/runtime-seed.ts is excluded by !**/fixtures/**, !web/e2e/**
  • web/package.json is excluded by !**/*.json
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
  • web/src/systems/marketplace/components/stories/marketplace-overlays.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/components/stories/mcp-authorize-dialog.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/components/stories/mcp-server-editor.stories.tsx is excluded by !**/*.stories.tsx
  • web/src/systems/settings/routes/mcp-servers.stories.tsx is excluded by !**/*.stories.tsx
📒 Files selected for processing (231)
  • go.mod
  • internal/acp/client_test.go
  • internal/api/contract/marketplace.go
  • internal/api/contract/mcp_catalog_install.go
  • internal/api/contract/settings_collection_payloads.go
  • internal/api/contract/settings_mcp_auth.go
  • internal/api/core/agent_contracts_test.go
  • internal/api/core/marketplace_detail.go
  • internal/api/core/marketplace_installed_detail.go
  • internal/api/core/marketplace_list.go
  • internal/api/core/marketplace_test.go
  • internal/api/core/settings_collection_requests.go
  • internal/api/core/settings_mcp_auth.go
  • internal/api/core/settings_mcp_conversion.go
  • internal/api/core/settings_mcp_install.go
  • internal/api/core/settings_test.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/helpers_test.go
  • internal/api/httpapi/routes.go
  • internal/api/spec/schema_customizers.go
  • internal/api/spec/settings_mcp_auth.go
  • internal/api/spec/settings_mcp_schema.go
  • internal/api/spec/settings_test.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/helpers_test.go
  • internal/api/udsapi/hosted_mcp.go
  • internal/api/udsapi/routes.go
  • internal/api/udsapi/transport_parity_integration_test.go
  • internal/cli/client_mcp.go
  • internal/cli/client_mcp_test.go
  • internal/cli/helpers_test.go
  • internal/cli/mcp_auth.go
  • internal/cli/mcp_auth_flow.go
  • internal/cli/mcp_auth_output.go
  • internal/cli/mcp_auth_test.go
  • internal/cli/mcp_install.go
  • internal/cli/mcp_install_input.go
  • internal/cli/mcp_install_output.go
  • internal/cli/mcp_install_test.go
  • internal/cli/mcp_serve.go
  • internal/cli/mcp_serve_test.go
  • internal/config/agent_resource_test.go
  • internal/config/agent_test.go
  • internal/config/config_extensions_sandbox.go
  • internal/config/config_refac_test.go
  • internal/config/config_test.go
  • internal/config/config_validation.go
  • internal/config/defaults.go
  • internal/config/lifecycle/lifecycle.go
  • internal/config/lifecycle/lifecycle_test.go
  • internal/config/mcp.go
  • internal/config/mcp_resource.go
  • internal/config/mcp_server_validation.go
  • internal/config/mcpjson_test.go
  • internal/config/merge.go
  • internal/config/merge_features.go
  • internal/config/merge_mcp.go
  • internal/config/merge_overlay.go
  • internal/config/provider.go
  • internal/config/provider_resolved_validation.go
  • internal/config/provider_test.go
  • internal/config/provider_validation.go
  • internal/daemon/daemon_mcp_serve_e2e_integration_test.go
  • internal/daemon/daemon_memory_e2e_integration_test.go
  • internal/daemon/daemon_mock_agents_integration_test.go
  • internal/daemon/marketplace_test.go
  • internal/daemon/mcp_dead_entity_integration_test.go
  • internal/daemon/native_extension_tool_provider.go
  • internal/daemon/native_mcp_provider.go
  • internal/daemon/settings.go
  • internal/daemon/settings_mcp_auth.go
  • internal/daemon/settings_mcp_runtime.go
  • internal/daemon/settings_runtime_dependencies.go
  • internal/daemon/settings_test.go
  • internal/daemon/tool_approval_grants_integration_test.go
  • internal/daemon/tool_mcp_resources_clone_test.go
  • internal/daemon/tool_mcp_resources_encoding.go
  • internal/marketplace/entry_mcp.go
  • internal/marketplace/entry_mcp_input.go
  • internal/marketplace/entry_mcp_launch.go
  • internal/marketplace/projection.go
  • internal/marketplace/service_integration_test.go
  • internal/marketplace/source.go
  • internal/marketplace/source_test.go
  • internal/marketplace/store_test.go
  • internal/marketplace/types.go
  • internal/mcp/auth/client_auth.go
  • internal/mcp/auth/dynamic_registration.go
  • internal/mcp/auth/exchange_input.go
  • internal/mcp/auth/fingerprint.go
  • internal/mcp/auth/manager.go
  • internal/mcp/auth/manager_test.go
  • internal/mcp/auth/metadata.go
  • internal/mcp/auth/metadata_test.go
  • internal/mcp/auth/pre_registered.go
  • internal/mcp/auth/sdk_adapter.go
  • internal/mcp/auth/sdk_adapter_test.go
  • internal/mcp/auth/service.go
  • internal/mcp/auth/service_direct_endpoint_test.go
  • internal/mcp/auth/service_expires_in_test.go
  • internal/mcp/auth/service_options.go
  • internal/mcp/auth/service_test.go
  • internal/mcp/auth/service_token_lifecycle.go
  • internal/mcp/auth/step_up.go
  • internal/mcp/auth/types.go
  • internal/mcp/authorization_required.go
  • internal/mcp/executor.go
  • internal/mcp/executor_auth.go
  • internal/mcp/executor_cache.go
  • internal/mcp/executor_client.go
  • internal/mcp/executor_setup.go
  • internal/mcp/executor_test.go
  • internal/mcp/hosted.go
  • internal/mcp/hosted_binding.go
  • internal/mcp/hosted_bootstrap_projection.go
  • internal/mcp/hosted_proxy.go
  • internal/mcp/hosted_proxy_result.go
  • internal/mcp/hosted_proxy_result_test.go
  • internal/mcp/hosted_proxy_test.go
  • internal/mcp/hosted_records.go
  • internal/mcp/hosted_test.go
  • internal/mcp/insufficient_scope.go
  • internal/mcp/protocol.go
  • internal/mcp/securehttp/body.go
  • internal/mcp/securehttp/client.go
  • internal/mcp/securehttp/client_test.go
  • internal/mcp/securehttp/policy.go
  • internal/mcp/securehttp/transport.go
  • internal/mcp/serve.go
  • internal/mcp/serve_http.go
  • internal/mcp/serve_projection.go
  • internal/mcp/serve_test.go
  • internal/mcp/tool_descriptor.go
  • internal/mcp/unsupported_capability.go
  • internal/settings/collection_hook_mcp_map.go
  • internal/settings/collection_provider_write.go
  • internal/settings/config_reload_diff.go
  • internal/settings/mcp_auth.go
  • internal/settings/mcp_catalog_input.go
  • internal/settings/mcp_catalog_install.go
  • internal/settings/mcp_collection_probes.go
  • internal/settings/mcp_mutation.go
  • internal/settings/mcp_runtime_status_test.go
  • internal/settings/mcp_target_test.go
  • internal/settings/models_mcp.go
  • internal/settings/service.go
  • internal/settings/service_contract.go
  • internal/settings/service_integration_test.go
  • internal/settings/service_test.go
  • internal/store/globaldb/global_db_marketplace_catalog_test.go
  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go
  • internal/store/globaldb/global_db_mcp_auth_test.go
  • internal/store/globaldb/global_db_mcp_oauth_registration.go
  • internal/store/globaldb/global_db_test.go
  • internal/store/globaldb/global_db_workspace.go
  • internal/store/globaldb/queries/vault_mcp.sql
  • internal/store/globaldb/schema/definitions/35_mcp.sql
  • internal/store/globaldb/schema/migrations/00029_schema.sql
  • internal/store/globaldb/schema/migrations/00030_marketplace_catalog_manifest_v2.sql
  • internal/store/globaldb/schema/migrations/00031_schema.sql
  • internal/store/globaldb/sqlcgen/models.go
  • internal/store/globaldb/sqlcgen/vault_mcp.sql.go
  • internal/testutil/mcpfixture/fixture.go
  • internal/testutil/mcpfixture/fixture_test.go
  • internal/testutil/mcpfixture/http.go
  • internal/testutil/mcpfixture/oauth.go
  • internal/testutil/mcpfixture/oauth_test.go
  • internal/testutil/mcpfixture/profile.go
  • internal/tools/dispatch.go
  • internal/tools/mcp_discovery.go
  • internal/tools/registry.go
  • internal/tools/registry_index.go
  • internal/tools/registry_session_projection.go
  • internal/tools/registry_test.go
  • internal/vault/mcp_refs.go
  • internal/vault/types_test.go
  • packages/site/app/marketplace/[kind]/page.tsx
  • packages/site/components/marketplace/marketplace-entry-card.tsx
  • packages/site/components/marketplace/marketplace-entry-detail.tsx
  • packages/site/components/site/__tests__/sidebar-compact-tree.test.tsx
  • packages/site/lib/__tests__/marketplace-catalog.test.tsx
  • packages/site/lib/marketplace-catalog-validation.ts
  • packages/site/lib/marketplace-catalog.ts
  • packages/site/vitest.config.ts
  • web/src/lib/__tests__/settings-api-contract.test.ts
  • web/src/systems/agent/__tests__/types.test.ts
  • web/src/systems/agent/routes/agent-detail-story-fixtures.ts
  • web/src/systems/marketplace/adapters/__tests__/marketplace-api.test.ts
  • web/src/systems/marketplace/components/__tests__/marketplace-action-controller.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-components.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-detail-manage.test.tsx
  • web/src/systems/marketplace/components/__tests__/marketplace-ui.test.ts
  • web/src/systems/marketplace/components/__tests__/mcp-install-dialog-store.test.ts
  • web/src/systems/marketplace/components/index.ts
  • web/src/systems/marketplace/components/marketplace-card.tsx
  • web/src/systems/marketplace/components/marketplace-detail-extension-actions.tsx
  • web/src/systems/marketplace/components/marketplace-detail-mcp-manage.tsx
  • web/src/systems/marketplace/components/marketplace-detail-meta.tsx
  • web/src/systems/marketplace/components/marketplace-detail.tsx
  • web/src/systems/marketplace/components/marketplace-entry-actions.tsx
  • web/src/systems/marketplace/components/marketplace-installed-card.tsx
  • web/src/systems/marketplace/components/marketplace-ui.ts
  • web/src/systems/marketplace/components/mcp-install-dialog.tsx
  • web/src/systems/marketplace/components/mcp-install-model.ts
  • web/src/systems/marketplace/components/use-marketplace-action-controller.tsx
  • web/src/systems/marketplace/components/use-mcp-install-dialog.ts
  • web/src/systems/marketplace/lib/__tests__/mcp-installed-status.test.ts
  • web/src/systems/marketplace/mocks/fixtures.ts
  • web/src/systems/marketplace/mocks/handlers.ts
  • web/src/systems/settings/adapters/__tests__/settings-mcp-auth-api.test.ts
  • web/src/systems/settings/components/__tests__/mcp-authorize-dialog.test.tsx
  • web/src/systems/settings/components/__tests__/mcp-editor-secret-presence.test.tsx
  • web/src/systems/settings/components/__tests__/mcp-server-editor.test.tsx
  • web/src/systems/settings/components/mcp-authorize-dialog.tsx
  • web/src/systems/settings/components/mcp-editor-connection-section.tsx
  • web/src/systems/settings/components/mcp-editor-oauth-section.tsx
  • web/src/systems/settings/components/mcp-server-editor.tsx
  • web/src/systems/settings/hooks/__tests__/use-mcp-authorize.test.tsx
  • web/src/systems/settings/hooks/__tests__/use-settings-mutations.test.tsx
  • web/src/systems/settings/hooks/use-mcp-authorize.ts
  • web/src/systems/settings/hooks/use-settings-mutations.ts
  • web/src/systems/settings/index.ts
  • web/src/systems/settings/lib/__tests__/mcp-editor-model.test.ts
  • web/src/systems/settings/lib/__tests__/mcp-status-view-model.test.ts
  • web/src/systems/settings/lib/mcp-authorize-model.ts
  • web/src/systems/settings/lib/mcp-editor-model.ts
  • web/src/systems/settings/lib/mcp-status-view-model.ts
  • web/src/systems/settings/mocks/__tests__/handlers.test.ts
  • web/src/systems/settings/mocks/fixtures.ts
  • web/src/systems/settings/stores/__tests__/mcp-authorize-store.test.ts
  • web/src/systems/settings/stores/mcp-authorize-store.ts
💤 Files with no reviewable changes (9)
  • internal/api/core/marketplace_installed_detail.go
  • internal/acp/client_test.go
  • web/src/systems/settings/index.ts
  • internal/mcp/auth/service_expires_in_test.go
  • internal/mcp/auth/service_test.go
  • internal/mcp/auth/metadata_test.go
  • web/src/systems/settings/components/tests/mcp-editor-secret-presence.test.tsx
  • internal/cli/mcp_serve.go
  • internal/mcp/auth/service_direct_endpoint_test.go

Comment thread internal/api/core/marketplace_detail.go
Comment thread internal/cli/mcp_auth_flow.go
Comment thread internal/config/config_test.go
Comment thread internal/daemon/settings_test.go
Comment thread internal/marketplace/entry_mcp_input.go
Comment thread internal/settings/mcp_catalog_input.go
Comment thread internal/tools/registry_index.go
Comment thread internal/vault/mcp_refs.go
Comment thread packages/site/lib/marketplace-catalog-validation.ts
Comment thread web/src/systems/marketplace/components/mcp-install-dialog.tsx
@pedronauck

Copy link
Copy Markdown
Member Author

CodeRabbit resolution report — PR #284

All 43 normalized findings from issues.json were rechecked against the current implementation. Thirty-five were valid and fixed at their owning layer; eight were invalid against the pinned SDK or existing normalization/timeout contracts.

# Disposition Resolution
1 Fixed Marketplace MCP projection now propagates invalid input-default errors instead of dropping the payload.
2 Fixed Terminal password reads are never canceled by closing the TTY descriptor; cancelable pipes are closed and joined safely.
3 Fixed MCP OAuth default assertions now live in Should ... subtests.
4 Invalid The pinned official SDK declares Tool.InputSchema and Tool.OutputSchema as any and explicitly accepts json.RawMessage; the fixture is type-correct.
5 Fixed Secret input normalization validates transport bounds and preserves the original secret bytes.
6 Fixed Omitted DCR token auth defaults to none for public clients and client_secret_basic only when a secret exists.
7 Fixed Token form requests now send form Content-Type and JSON Accept headers.
8 Fixed Advertised protected-resource metadata URLs must be absolute HTTP(S) and match the resource authority.
9 Fixed The production OAuth service can no longer bypass securehttp with an arbitrary HTTP client; the bypass is test-only.
10 Fixed Hosted tool records clone the registry-owned slice before sorting.
11 Fixed Protocol-version filtering allocates a new result slice and preserves the caller-owned input.
12 Fixed url_query bindings on non-remote launches fail validation instead of mutating an empty URL.
13 Fixed Bootstrap dispatch uses the full registry for conflicts and policy; partial discovery no longer overwrites authoritative descriptor metadata.
14 Fixed Operator MCP secret refs cannot enter the daemon-owned oauth/ subtree.
15 Fixed Site URL validation now applies the IPv4 block list to IPv4-compatible IPv6 forms such as ::127.0.0.1.
16 Fixed Guided install renders the catalog's actual automatic or pre-registered OAuth mode.
17 Fixed Manual OAuth begin rejects an empty configured redirect URI before calling the settings service.
18 Fixed MCP auth configuration validation rejects unsupported auth types.
19 Invalid Every MCP call is already wrapped by the executor's bounded 30-second default and its HTTP client timeout is clamped to the same bound.
20 Fixed The E2E timeout cancel is registered with t.Cleanup, after session cleanup ownership.
21 Fixed Manager store I/O runs outside the mutex with revision and generation fencing on re-entry.
22 Invalid Settings probes use NewMCPCallExecutor and therefore inherit the same bounded 30-second call and HTTP timeout.
23 Fixed The Vault-reference subtest name now matches its retention assertions.
24 Fixed Invalid manual redirect submissions enter an explicit completion-failure state instead of silently doing nothing.
25 Fixed Approved scopes are trimmed, deduplicated, and persisted at the store event boundary before authorization begins.
26 Fixed Older manifests report feed too old; future manifests continue to report client too old.
27 Invalid prepareMCPCatalogInstall already resolves omitted scope from the manifest and passes the scope/workspace pair through normalizeReadScope.
28 Fixed Authorization and insufficient-scope errors are classified before deadline normalization can mask them.
29 Fixed tools/list handles nil results and caps pagination at 1,000 pages; SDK TTL metadata was already consumed.
30 Fixed Pre-registered OAuth validation cases now use independent Should ... subtests.
31 Invalid firstNonEmpty already trims every candidate before returning it, so both resource URL operands are normalized.
32 Fixed Well-known protected-resource candidates remove query strings and fragments.
33 Invalid formatMarketplaceVersion returns null for blank input, so the existing nullish fallback already renders “the newer release.”
34 Invalid Official SDK ClientSession.Close is documented and implemented as idempotent; the explicit close is required to synchronize proxy shutdown before assertions.
35 Fixed The serve contract now fails when the tool schema is absent.
36 Fixed Secret catalog inputs require an env binding before any Vault or configuration write.
37 Fixed GlobalDB failure-path tests now assert the injected causal diagnostics, not merely non-nil errors.
38 Fixed Fixture validation tests assert the failing profile, input, and output contracts.
39 Fixed OAuth mix-up tests assert 302 Found before parsing Location.
40 Fixed Registration cloning appends only successful clones and cannot leave nil holes.
41 Fixed Site MCP card metadata inserts the missing separator before the input summary.
42 Fixed Site validation now names the supported local launch types: npm, uvx, and docker.
43 Invalid isAbsoluteMCPRedirectURL already validates normalizeMCPRedirectURL(value), which trims the submitted value.

Additional CI blocker fixed: the sdk/go race test now uses the same hermetic helper that forces CGO_ENABLED=1; the previous subdirectory call inherited the workflow-wide CGO_ENABLED=0 after all package tests had passed.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (7)
web/src/systems/settings/stores/mcp-authorize-store.ts (2)

158-176: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Same silent-drop pattern in scopeEscalationRequested.

Shares the same root cause as the authorizeRequested handler above (line 165): an invalid scopeApproval (empty normalized scopes) causes a silent no-op instead of an explicit failure state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/settings/stores/mcp-authorize-store.ts` around lines 158 -
176, The scopeEscalationRequested handler currently drops invalid normalized
approvals silently. Replace the hasExplicitScopeApproval early return with the
same explicit failure-state transition used by authorizeRequested, while
preserving the existing reviewing_scopes transition for valid approvals and the
idle-phase guard.

191-220: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Silent no-op when scopeApproval fails hasExplicitScopeApproval.

If event.scopeApproval is provided but invalid (e.g. approveScopeEscalation: false, or all scopes normalize to empty), authorizeRequested returns without any state transition (line 199) — the UI stays in "idle" with no error, unlike the newly added manualRedirectSubmitted handling which surfaces an explicit failed state for an analogous invalid-input case. Worth confirming whether use-mcp-authorize.ts can ever dispatch such a malformed event; if so, consider surfacing a failed transition here too for consistency and debuggability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/systems/settings/stores/mcp-authorize-store.ts` around lines 191 -
220, Update authorizeRequested to handle invalid provided scopeApproval instead
of silently returning when hasExplicitScopeApproval fails. Transition to the
existing failed state with an appropriate error, matching the
manualRedirectSubmitted invalid-input handling, while preserving the current
beginning transition for valid approvals and the idle behavior when no
scopeApproval is provided.
internal/settings/mcp_catalog_input.go (1)

155-203: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing validation for empty input.Binding.Name in secret-input binding.

bindingName (line 172) is used directly as an env key for setMCPSecretEnvRef (195) and secrets.SecretEnv (201) without checking it's non-empty. A malformed catalog manifest with a blank binding name would silently create an invalid empty-key environment binding, the same class of manifest-defect issue already fixed for url_query on the URL side.

🛡️ Proposed guard
 	bindingName := strings.TrimSpace(input.Binding.Name)
+	if bindingName == "" {
+		return validationError(fmt.Errorf(
+			"settings: catalog secret input %q has an empty binding name",
+			id,
+		))
+	}
 	if !supplied {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/settings/mcp_catalog_input.go` around lines 155 - 203, Validate the
trimmed bindingName in applyCatalogSecretInput before using it as an environment
key. Return the existing validationError pattern with a clear catalog
secret-input binding-name error when it is empty, while preserving the current
vault-reference and SecretEnv assignment flows for valid names.
internal/api/core/settings_mcp_auth.go (1)

126-130: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider validating the manual redirect URI, not just its presence.

Automatic mode parses the configured redirect URI and enforces scheme/host/loopback, while manual mode returns the raw trimmed value. A malformed configured value (e.g. missing scheme) would flow into the authorization request and fail later with an opaque provider error. Reusing the same parse/validation keeps both modes consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/core/settings_mcp_auth.go` around lines 126 - 130, The manual
redirect URI path in the MCP OAuth settings validation must validate the trimmed
value using the same parsing and scheme/host/loopback checks as automatic mode,
rather than returning it immediately. Reuse the existing redirect URI validation
logic and return its validation error before returning the URI.
internal/mcp/auth/manager.go (1)

446-465: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the duplicated revision check.

The lock/check/unlock block appears twice verbatim; a small func (m *Manager) revisionMatches(targetKey string, revision uint64) bool helper would remove the duplication and the manual unlock paths. The final persist relies on the generation fence rather than the released lock, which is consistent with the mutation-race tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/auth/manager.go` around lines 446 - 465, The revision validation
in the surrounding Manager method is duplicated before and after token matching.
Add a Manager.revisionMatches helper that locks internally, compares the target
revision, and returns the result, then replace both manual lock/check/unlock
blocks with this helper while preserving ErrLoginSessionStale handling and the
existing generation-fenced persistReplacementTokenAtGeneration call.
internal/testutil/mcpfixture/oauth.go (1)

326-340: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Silently dropping un-clonable entries can distort snapshot counts.

Tests assert on len(fixture.Requests().Registration); a skipped entry would make the snapshot shorter than what the server actually recorded, turning a fixture bug into a confusing count mismatch. Since these maps come from json.Unmarshal, a failure here is effectively impossible — so preserving a shallow copy (or panicking) is more honest than continue.

♻️ Suggested handling
 func cloneRegistrationSlice(values []map[string]any) []map[string]any {
 	cloned := make([]map[string]any, 0, len(values))
 	for _, value := range values {
 		data, err := json.Marshal(value)
 		if err != nil {
-			continue
+			cloned = append(cloned, value)
+			continue
 		}
 		var entry map[string]any
 		if err := json.Unmarshal(data, &entry); err != nil {
-			continue
+			cloned = append(cloned, value)
+			continue
 		}
 		cloned = append(cloned, entry)
 	}
 	return cloned
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/testutil/mcpfixture/oauth.go` around lines 326 - 340, Update
cloneRegistrationSlice so entries that cannot be JSON-cloned are not silently
omitted, preserving the original slice length and snapshot counts. Replace the
continue-based failure handling in the function with an honest fallback such as
preserving a shallow copy or panicking, while retaining the existing deep-clone
behavior for successful entries.
internal/mcp/executor_test.go (1)

881-921: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Prefer an atomic counter for calls.

calls++ runs on the server goroutine while line 919 reads it from the test goroutine; ordering relies on the transport's happens-before edges rather than explicit synchronization. internal/mcp/auth/sdk_adapter_test.go already uses atomic.Int32 for the same pattern — matching it removes any -race ambiguity for free.

♻️ Suggested change
-		calls := 0
+		var calls atomic.Int32
 		server.AddReceivingMiddleware(func(next mcpsdk.MethodHandler) mcpsdk.MethodHandler {
 			return func(ctx context.Context, method string, request mcpsdk.Request) (mcpsdk.Result, error) {
 				if method == "tools/list" {
-					calls++
-					return &mcpsdk.ListToolsResult{NextCursor: fmt.Sprintf("page-%d", calls)}, nil
+					return &mcpsdk.ListToolsResult{
+						NextCursor: fmt.Sprintf("page-%d", calls.Add(1)),
+					}, nil
 				}
 				return next(ctx, method, request)
 			}
 		})
-		if got, want := calls, maxMCPToolListPages; got != want {
+		if got, want := int(calls.Load()), maxMCPToolListPages; got != want {
 			t.Fatalf("tools/list calls = %d, want %d", got, want)
 		}

As per coding guidelines, "Go tests must use Should … subtest names, default to t.Parallel, ... run with race detection".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/executor_test.go` around lines 881 - 921, Replace the
unsynchronized calls counter in the paginated tools test with an atomic counter,
following the atomic.Int32 pattern used in
internal/mcp/auth/sdk_adapter_test.go. Update the increment in the tools/list
middleware and the assertion comparing the count with maxMCPToolListPages to use
atomic operations, preserving the existing pagination behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/mcp/auth/manager_test.go`:
- Around line 399-418: Update managerTestStore.GetMCPAuthRegistration to consume
registrationStarted under s.mu and clear the field before releasing the mutex,
then close only the captured channel outside the lock. Preserve the existing
release synchronization and registration lookup behavior while ensuring repeated
calls cannot close the same channel twice.

In `@internal/mcp/auth/sdk_adapter.go`:
- Around line 170-174: Remove the no-op deferred branch around
drainAndCloseResponseBody in the response handling flow, or explicitly handle
the returned drainErr by logging or annotating it; do not silently discard the
error. Update the defer block associated with resp.Body and metadataURL so its
intent is clear and every error is handled.

In `@internal/vault/mcp_refs.go`:
- Around line 41-43: Update NormalizeRef and the MCP reference validation around
the OAuth-subtree guard to normalize each non-protected path segment before
comparing the guarded prefix, while preserving case-sensitive comparison after
normalization. Ensure uppercase user input such as OAuth is rejected before
canonical storage or resolution, while protected segments remain unchanged.

---

Nitpick comments:
In `@internal/api/core/settings_mcp_auth.go`:
- Around line 126-130: The manual redirect URI path in the MCP OAuth settings
validation must validate the trimmed value using the same parsing and
scheme/host/loopback checks as automatic mode, rather than returning it
immediately. Reuse the existing redirect URI validation logic and return its
validation error before returning the URI.

In `@internal/mcp/auth/manager.go`:
- Around line 446-465: The revision validation in the surrounding Manager method
is duplicated before and after token matching. Add a Manager.revisionMatches
helper that locks internally, compares the target revision, and returns the
result, then replace both manual lock/check/unlock blocks with this helper while
preserving ErrLoginSessionStale handling and the existing generation-fenced
persistReplacementTokenAtGeneration call.

In `@internal/mcp/executor_test.go`:
- Around line 881-921: Replace the unsynchronized calls counter in the paginated
tools test with an atomic counter, following the atomic.Int32 pattern used in
internal/mcp/auth/sdk_adapter_test.go. Update the increment in the tools/list
middleware and the assertion comparing the count with maxMCPToolListPages to use
atomic operations, preserving the existing pagination behavior.

In `@internal/settings/mcp_catalog_input.go`:
- Around line 155-203: Validate the trimmed bindingName in
applyCatalogSecretInput before using it as an environment key. Return the
existing validationError pattern with a clear catalog secret-input binding-name
error when it is empty, while preserving the current vault-reference and
SecretEnv assignment flows for valid names.

In `@internal/testutil/mcpfixture/oauth.go`:
- Around line 326-340: Update cloneRegistrationSlice so entries that cannot be
JSON-cloned are not silently omitted, preserving the original slice length and
snapshot counts. Replace the continue-based failure handling in the function
with an honest fallback such as preserving a shallow copy or panicking, while
retaining the existing deep-clone behavior for successful entries.

In `@web/src/systems/settings/stores/mcp-authorize-store.ts`:
- Around line 158-176: The scopeEscalationRequested handler currently drops
invalid normalized approvals silently. Replace the hasExplicitScopeApproval
early return with the same explicit failure-state transition used by
authorizeRequested, while preserving the existing reviewing_scopes transition
for valid approvals and the idle-phase guard.
- Around line 191-220: Update authorizeRequested to handle invalid provided
scopeApproval instead of silently returning when hasExplicitScopeApproval fails.
Transition to the existing failed state with an appropriate error, matching the
manualRedirectSubmitted invalid-input handling, while preserving the current
beginning transition for valid approvals and the idle behavior when no
scopeApproval is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cab60c7-dcd4-4b26-afbe-dd0f54d7ca66

📥 Commits

Reviewing files that changed from the base of the PR and between 7b9da2a and e772c2f.

📒 Files selected for processing (52)
  • internal/api/core/marketplace_detail.go
  • internal/api/core/settings_mcp_auth.go
  • internal/api/core/settings_test.go
  • internal/cli/mcp_auth_flow.go
  • internal/cli/mcp_auth_test.go
  • internal/config/config_test.go
  • internal/config/provider_test.go
  • internal/daemon/daemon_mcp_serve_e2e_integration_test.go
  • internal/marketplace/entry_mcp_input.go
  • internal/marketplace/errors.go
  • internal/marketplace/source_test.go
  • internal/mcp/auth/client_auth.go
  • internal/mcp/auth/manager.go
  • internal/mcp/auth/manager_test.go
  • internal/mcp/auth/sdk_adapter.go
  • internal/mcp/auth/sdk_adapter_test.go
  • internal/mcp/auth/service_options.go
  • internal/mcp/auth/types.go
  • internal/mcp/auth/types_test.go
  • internal/mcp/executor.go
  • internal/mcp/executor_setup.go
  • internal/mcp/executor_test.go
  • internal/mcp/executor_tools.go
  • internal/mcp/hosted_proxy_test.go
  • internal/mcp/hosted_records.go
  • internal/mcp/hosted_test.go
  • internal/mcp/protocol.go
  • internal/mcp/serve_test.go
  • internal/settings/mcp_catalog_input.go
  • internal/settings/service_test.go
  • internal/store/globaldb/global_db_mcp_auth_atomic_test.go
  • internal/store/globaldb/global_db_mcp_auth_test.go
  • internal/testutil/mcpfixture/fixture_test.go
  • internal/testutil/mcpfixture/oauth.go
  • internal/testutil/mcpfixture/oauth_test.go
  • internal/tools/dispatch.go
  • internal/tools/registry_index.go
  • internal/tools/registry_session_projection.go
  • internal/tools/registry_test.go
  • internal/vault/mcp_refs.go
  • internal/vault/types_test.go
  • magefiles/command.go
  • magefiles/gotest_lane_test.go
  • magefiles/test.go
  • packages/site/components/marketplace/marketplace-entry-card.tsx
  • packages/site/lib/__tests__/marketplace-catalog.test.tsx
  • packages/site/lib/marketplace-catalog-validation.ts
  • packages/site/lib/marketplace-catalog.ts
  • web/src/systems/marketplace/components/__tests__/marketplace-components.test.tsx
  • web/src/systems/marketplace/components/mcp-install-dialog.tsx
  • web/src/systems/settings/stores/__tests__/mcp-authorize-store.test.ts
  • web/src/systems/settings/stores/mcp-authorize-store.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/site/lib/marketplace-catalog.ts

Comment thread internal/mcp/auth/manager_test.go
Comment thread internal/mcp/auth/sdk_adapter.go
Comment thread internal/vault/mcp_refs.go Outdated
Migrate the runtime to the official Go SDK, hard-cut the marketplace
manifest to v2, and add durable OAuth registration and token lifecycle.

Populate the curated Wave 1/2 catalog and expose typed install,
authorization, and status flows across CLI, HTTP, UDS, Web, site, and
the official Compozy skill, with production-like QA evidence.

Preserve the public protocol allowlist while adapting the private hosted
proxy for the Codex legacy handshake and non-deferred native projection.

BREAKING CHANGE: MCP catalogs now require manifest_version 2 and public
MCP transport no longer accepts SSE.
Comment thread packages/site/lib/marketplace-catalog.ts Outdated
@pedronauck
pedronauck merged commit 7f3fb64 into main Jul 31, 2026
16 of 19 checks passed
@pedronauck
pedronauck deleted the mcps branch July 31, 2026 01:36

This branch was successfully deployed

1 active deployment
Preview — 7c533934 Deployed Jul 31, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant