Skip to content

feat: model dual-era ChatGPT protocol selection - #3828

Open
ignaciojimenezr wants to merge 1 commit into
mainfrom
codex/chatgpt-protocol-profile
Open

feat: model dual-era ChatGPT protocol selection#3828
ignaciojimenezr wants to merge 1 commit into
mainfrom
codex/chatgpt-protocol-profile

Conversation

@ignaciojimenezr

@ignaciojimenezr ignaciojimenezr commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Purpose

Model ChatGPT as one dual-era host profile instead of mixing protocol support, protocol selection, and legacy initialize data.

Behavior

  • ChatGPT advertises 2025-11-25 and 2026-07-28 once through supportedProtocolVersions.
  • mcpProtocolVersion: "auto" means negotiate at connection time.
  • Forced 2025 uses the legacy initialize flow.
  • Forced 2026 uses server/discover and never sends initialize.
  • Automatic probes modern support and falls back to legacy.
  • A concrete per-server protocol override wins over host Automatic.
  • Existing stored profiles with nested initialize remain readable and keep their historical canonical hashes.

Data model

supportedProtocolVersions and clientInfo are now sibling profile fields. The old nested initialize shape remains deprecated read compatibility, not the ChatGPT source of truth.

The generated local fallback catalog includes the backend-owned ChatGPT profile from MCPJam/mcpjam-backend#872.

Validation

  • SDK and Inspector TypeScript checks passed.
  • Focused protocol-selection, canonicalization, connection-profile, host round-trip, catalog, resolver, and Protocol tab tests passed.
  • Full SDK test suite passed.

Companion backend PR: https://github.com/MCPJam/mcpjam-backend/pull/872


Summary by cubic

ChatGPT is now modeled as a dual‑era MCP host with explicit negotiation. We add mcpProtocolVersion: "auto" and move supportedProtocolVersions/clientInfo out of legacy initialize to enable 2026 discovery with 2025 fallback.

  • New Features

    • ChatGPT advertises supportedProtocolVersions: ["2025-11-25", "2026-07-28"]; default mcpProtocolVersion: "auto".
    • Automatic negotiates via server/discover and falls back to legacy initialize; per‑server pins override host Automatic.
    • SDK reduces "auto" to no wire pin; negotiation is independent from the support list, which Automatic still honors.
    • Public API: Host.mcp.protocolVersion accepts "auto"; supportedProtocolVersions and clientInfo are era‑neutral siblings.
    • Catalog and seed templates updated for ChatGPT; Inspector Protocol tab now persists "auto" and reads/writes the new fields.
  • Migration

    • No action required: legacy initialize remains readable and keeps canonical hashes.
    • Conflicts between sibling fields and deprecated initialize are rejected during canonicalization.
    • For a 2025 pin, ensure that version is included in supportedProtocolVersions.

Written for commit 2b185fa. Summary will update on new commits.

Review in cubic

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 10, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@dosubot dosubot Bot added the enhancement New feature or request label Aug 10, 2026
@chelojimenez

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

MCP host profiles now support explicit "auto" protocol negotiation, top-level supported protocol versions, and top-level client information. Canonicalization validates the new fields, detects conflicts with legacy initialize data, and preserves backward compatibility. Runtime connections use configured version lists during automatic negotiation and omit protocol pins. The inspector stores "auto" explicitly and migrates edited profiles away from nested initialize fields. Tests cover canonicalization, conversion, reconnection, negotiation, and UI behavior.


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.

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

Caution

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

⚠️ Outside diff range comments (1)
sdk/src/host-config/canonicalize.ts (1)

888-937: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate stateless protocol pins against the advertised list.

The check excludes stateless pins. Therefore, this profile is accepted:

mcpProtocolVersion: "2026-07-28" with supportedProtocolVersions: ["2025-11-25"].

MCPClientManager then sends the 2026 pin while it advertises only 2025 support. Validate every concrete pin when an advertised list exists. Keep the stateful-only derivation behavior after that validation.

Add a canonicalization test for this conflicting 2026 pin.

🤖 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 `@sdk/src/host-config/canonicalize.ts` around lines 888 - 937, Update the
cross-field validation in canonicalization to validate every concrete
mcpProtocolVersion, including stateless versions, whenever
supportedProtocolVersions or initialize.supportedProtocolVersions is present;
throw the existing conflict error when the pin is absent from the advertised
list. Preserve the current stateful-only derivation behavior for missing
advertised lists, and add a canonicalization test covering a 2026 stateless pin
conflicting with a 2025 advertised version.
🤖 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.

Outside diff comments:
In `@sdk/src/host-config/canonicalize.ts`:
- Around line 888-937: Update the cross-field validation in canonicalization to
validate every concrete mcpProtocolVersion, including stateless versions,
whenever supportedProtocolVersions or initialize.supportedProtocolVersions is
present; throw the existing conflict error when the pin is absent from the
advertised list. Preserve the current stateful-only derivation behavior for
missing advertised lists, and add a canonicalization test covering a 2026
stateless pin conflicting with a 2025 advertised version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 62f7b958-05c9-425e-820c-2816c2a27af1

📥 Commits

Reviewing files that changed from the base of the PR and between 2d8d395 and 2b185fa.

⛔ Files ignored due to path filters (2)
  • sdk/src/host-compat/catalog.generated.ts is excluded by !**/*.generated.*
  • sdk/tests/__snapshots__/host-config-seed-host-template.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (22)
  • mcpjam-inspector/client/src/components/connection/ServerDetailModal.tsx
  • mcpjam-inspector/client/src/components/hosts/redesigned/focus/ProtocolTab.tsx
  • mcpjam-inspector/client/src/components/hosts/redesigned/focus/__tests__/ProtocolTab.versionDropdown.test.tsx
  • mcpjam-inspector/client/src/lib/__tests__/client-config-v2-mcp-profile.test.ts
  • mcpjam-inspector/client/src/lib/client-config-v2.ts
  • mcpjam-inspector/client/src/lib/host-config-field-schema.ts
  • sdk/src/host-config/canonicalize.ts
  • sdk/src/host-config/defaults.ts
  • sdk/src/host-config/host-connection.ts
  • sdk/src/host-config/host.ts
  • sdk/src/host-config/public-types.ts
  • sdk/src/host-config/sdk-evals-normalizer.ts
  • sdk/src/host-config/templates/seed-host-template.ts
  • sdk/src/host-config/types.ts
  • sdk/src/mcp-client-manager/MCPClientManager.ts
  • sdk/tests/MCPClientManager.auto-protocol-reconnect.test.ts
  • sdk/tests/host-compat-market-hosts.test.ts
  • sdk/tests/host-config-canonicalize.test.ts
  • sdk/tests/host-config-sdk-evals-normalizer.test.ts
  • sdk/tests/host-connection.test.ts
  • sdk/tests/host.test.ts
  • sdk/tests/support/dual-era-fixture.test.ts

@github-actions

Copy link
Copy Markdown
Contributor

Internal preview

Preview URL: https://mcp-inspector-pr-3828.up.railway.app
Deployed commit: 23fee1b
PR head commit: 2b185fa
Backend target: staging fallback.
Health: ✅ Convex reachable
Access is employee-only in non-production environments.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="sdk/tests/support/dual-era-fixture.test.ts">

<violation number="1" location="sdk/tests/support/dual-era-fixture.test.ts:58">
P3: The new `connectAutomatic()` helper duplicates `connectModern()` almost entirely — same handler/capture setup, same client identity, same `supportedProtocolVersions` list, and the same transport; only the `versionNegotiation` mode differs (and `connectLegacy()` shares the same skeleton too). Consider factoring the shared setup into a single parameterized helper that takes the negotiation options, so the fixture's client name/support-list/transport live in one place instead of three near-identical copies that can drift when the fixture evolves.</violation>
</file>

<file name="mcpjam-inspector/client/src/components/hosts/redesigned/focus/ProtocolTab.tsx">

<violation number="1" location="mcpjam-inspector/client/src/components/hosts/redesigned/focus/ProtocolTab.tsx:629">
P1: Editing a legacy host whose protocol data lives in the deprecated nested `initialize` envelope now drops that data from the frontend's connection path. `applyJsonToDraft` migrates by writing the sibling `clientInfo` / `supportedProtocolVersions` and forcing `initialize: undefined`, but the frontend resolver still reads only the nested path: `use-server-state.ts` (buildConnectDefaults) reads `mcpProfile?.initialize?.clientInfo` and `mcpProfile?.initialize?.supportedProtocolVersions`, and `resolveClientInfo` / `resolveSupportedProtocolVersions` in `client-config-v2.ts` do the same with no sibling fallback. So a user who opens the JSON editor and makes any edit to a legacy profile — with no change to the version/client fields — will have those fields cleared out of `mcpProfile` and, on the next connect, the accept-list and client identity silently stop being forwarded to the SDK. The reader side of this migration (`protocolToJson`) correctly prefers sibling-then-initialize, but the consumers that feed the actual connection were not updated to match, so the migrated sibling fields are never picked up. Consider updating the frontend resolvers/connect path to read `?? initialize?.*` (mirroring `protocolToJson`'s precedence) before committing the `initialize: undefined` migration, so migrated rows keep working.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

supportedProtocolVersions,
// Editing this section migrates the deprecated envelope to the sibling
// fields above; retaining both would recreate two sources of truth.
initialize: undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Editing a legacy host whose protocol data lives in the deprecated nested initialize envelope now drops that data from the frontend's connection path. applyJsonToDraft migrates by writing the sibling clientInfo / supportedProtocolVersions and forcing initialize: undefined, but the frontend resolver still reads only the nested path: use-server-state.ts (buildConnectDefaults) reads mcpProfile?.initialize?.clientInfo and mcpProfile?.initialize?.supportedProtocolVersions, and resolveClientInfo / resolveSupportedProtocolVersions in client-config-v2.ts do the same with no sibling fallback. So a user who opens the JSON editor and makes any edit to a legacy profile — with no change to the version/client fields — will have those fields cleared out of mcpProfile and, on the next connect, the accept-list and client identity silently stop being forwarded to the SDK. The reader side of this migration (protocolToJson) correctly prefers sibling-then-initialize, but the consumers that feed the actual connection were not updated to match, so the migrated sibling fields are never picked up. Consider updating the frontend resolvers/connect path to read ?? initialize?.* (mirroring protocolToJson's precedence) before committing the initialize: undefined migration, so migrated rows keep working.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mcpjam-inspector/client/src/components/hosts/redesigned/focus/ProtocolTab.tsx, line 629:

<comment>Editing a legacy host whose protocol data lives in the deprecated nested `initialize` envelope now drops that data from the frontend's connection path. `applyJsonToDraft` migrates by writing the sibling `clientInfo` / `supportedProtocolVersions` and forcing `initialize: undefined`, but the frontend resolver still reads only the nested path: `use-server-state.ts` (buildConnectDefaults) reads `mcpProfile?.initialize?.clientInfo` and `mcpProfile?.initialize?.supportedProtocolVersions`, and `resolveClientInfo` / `resolveSupportedProtocolVersions` in `client-config-v2.ts` do the same with no sibling fallback. So a user who opens the JSON editor and makes any edit to a legacy profile — with no change to the version/client fields — will have those fields cleared out of `mcpProfile` and, on the next connect, the accept-list and client identity silently stop being forwarded to the SDK. The reader side of this migration (`protocolToJson`) correctly prefers sibling-then-initialize, but the consumers that feed the actual connection were not updated to match, so the migrated sibling fields are never picked up. Consider updating the frontend resolvers/connect path to read `?? initialize?.*` (mirroring `protocolToJson`'s precedence) before committing the `initialize: undefined` migration, so migrated rows keep working.</comment>

<file context>
@@ -618,18 +620,13 @@ export function applyJsonToDraft(
+      supportedProtocolVersions,
+      // Editing this section migrates the deprecated envelope to the sibling
+      // fields above; retaining both would recreate two sources of truth.
+      initialize: undefined,
       mcpProtocolVersion,
       toolParamHeaderMirroring,
</file context>

{ name: "dual-era-fixture-test", version: "1.0.0" },
{
supportedProtocolVersions: ["2025-11-25", "2026-07-28"],
versionNegotiation: { mode: "auto" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new connectAutomatic() helper duplicates connectModern() almost entirely — same handler/capture setup, same client identity, same supportedProtocolVersions list, and the same transport; only the versionNegotiation mode differs (and connectLegacy() shares the same skeleton too). Consider factoring the shared setup into a single parameterized helper that takes the negotiation options, so the fixture's client name/support-list/transport live in one place instead of three near-identical copies that can drift when the fixture evolves.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk/tests/support/dual-era-fixture.test.ts, line 58:

<comment>The new `connectAutomatic()` helper duplicates `connectModern()` almost entirely — same handler/capture setup, same client identity, same `supportedProtocolVersions` list, and the same transport; only the `versionNegotiation` mode differs (and `connectLegacy()` shares the same skeleton too). Consider factoring the shared setup into a single parameterized helper that takes the negotiation options, so the fixture's client name/support-list/transport live in one place instead of three near-identical copies that can drift when the fixture evolves.</comment>

<file context>
@@ -33,7 +33,30 @@ async function connectModern() {
+    { name: "dual-era-fixture-test", version: "1.0.0" },
+    {
+      supportedProtocolVersions: ["2025-11-25", "2026-07-28"],
+      versionNegotiation: { mode: "auto" },
+    },
   );
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants