Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/contributing/oauth-architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The OAuth implementation uses:
- **CORS proxy server** for metadata discovery and SSE handling
- **localStorage** for persistence
- **MCP SDK** for OAuth operations
- **Multi-protocol support** for 2025-03-26, 2025-06-18, and 2025-11-25 specifications
- **Multi-protocol support** for 2025-03-26, 2025-06-18, 2025-11-25, and 2026-07-28 specifications

The canonical home for the date-versioned OAuth debug state machines is now `sdk/src/oauth/state-machines/**`. The Inspector consumes those browser-safe SDK exports through `@mcpjam/sdk/browser` plus a small adapter in `client/src/lib/oauth/debug-state-machine-adapter.ts`.

Expand Down Expand Up @@ -218,7 +218,7 @@ graph LR

## Protocol Versions

The OAuth debugger supports three MCP OAuth protocol versions:
The OAuth debugger supports four MCP OAuth protocol versions:

### 2025-03-26 (Original)

Expand All @@ -235,13 +235,19 @@ The OAuth debugger supports three MCP OAuth protocol versions:
- PKCE recommended but not strictly enforced
- Registration: DCR (SHOULD) or pre-registered

### 2025-11-25 (Latest)
### 2025-11-25 (November)

- Client ID Metadata Documents (CIMD) support
- RFC8414 or OIDC discovery without root fallback
- PKCE strictly required and enforced
- Registration: CIMD (SHOULD), DCR, or pre-registered

### 2026-07-28 (Latest)

- Everything in 2025-11-25, plus:
- SEP-837: OIDC `application_type` sent on Dynamic Client Registration
- RFC9207 `iss` validation on the authorization callback, enforced as a hard block

## Key Components

### 1. OAuthFlowLogger
Expand Down Expand Up @@ -272,7 +278,7 @@ Step metadata is defined in `sdk/src/oauth/state-machines/shared/step-metadata.t
Modal dialog for configuring OAuth test profiles:

- Server name and URL configuration
- Protocol version selection (2025-03-26, 2025-06-18, 2025-11-25)
- Protocol version selection (2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28)
- Registration strategy selection (CIMD, DCR, Pre-registered)
- Advanced settings accordion for optional configuration
- Custom headers management with add/remove functionality
Expand Down
4 changes: 2 additions & 2 deletions docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ MCPJam is the development platform for **MCP servers, MCP apps, and ChatGPT apps
[First MCP App →](/guides/first-mcp-app) · [First ChatGPT App in React →](/guides/first-chatgpt-app-react)
</Accordion>
<Accordion title="Validate OAuth conformance across protocol versions" icon="shield-check">
Step through every stage of the MCP authorization flow against protocol versions 03-26, 06-18, 11-25, and 2026-07-28 (Draft), with DCR, pre-registration, and CIMD coverage.
Step through every stage of the MCP authorization flow against protocol versions 03-26, 06-18, 11-25, and 2026-07-28 (Latest), with DCR, pre-registration, and CIMD coverage.

[Guided OAuth →](/inspector/guided-oauth) · [CLI OAuth conformance →](/cli/oauth-conformance)
</Accordion>
Expand All @@ -93,7 +93,7 @@ MCPJam is the development platform for **MCP servers, MCP apps, and ChatGPT apps
| Capability | Description |
| --- | --- |
| Playground | IDE-style workspace combining chat, manual tool calls, widget rendering, Chat/Trace/Raw views, and multi-model and multi-host compare. OpenAI Apps SDK and MCP App UIs, text tools, Chrome DevTools-style widget emulator. [Read more](/inspector/playground) |
| OAuth Debugger | Guided MCP OAuth conformance checks: protocol versions 03-26, 06-18, 11-25, 2026-07-28 (Draft); DCR, client pre-registration, CIMD. [Read more](/inspector/guided-oauth) |
| OAuth Debugger | Guided MCP OAuth conformance checks: protocol versions 03-26, 06-18, 11-25, 2026-07-28 (Latest); DCR, client pre-registration, CIMD. [Read more](/inspector/guided-oauth) |
| MCP Server Debugging | Manually run tools, resources, templates, and elicitation; full JSON-RPC logs. |
| Skills | Skills in the Playground; local filesystem only. [Read more](/inspector/skills) |
| Projects | Shared server groups with real-time team sync. [Read more](/inspector/projects) |
Expand Down
4 changes: 2 additions & 2 deletions docs/inspector/guided-oauth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The OAuth Debugger provides a visual, step-by-step interface for testing and deb
The OAuth Debugger includes:

- **Visual Step-by-Step Guide** - Interactive flow guide with detailed explanations for each OAuth step
- **Multi-Protocol Support** - Test against OAuth spec versions 03-26, 06-18, 11-25, and 2026-07-28 (Draft)
- **Multi-Protocol Support** - Test against OAuth spec versions 03-26, 06-18, 11-25, and 2026-07-28 (Latest)
- **Full Registration Methods** - Support for client pre-registration, Dynamic Client Registration (DCR), and Client ID Metadata Documents (CIMD)
- **Network Inspection** - View all HTTP requests and responses with headers and body content
- **Educational Context** - Built-in teachable moments and tips for common OAuth issues
Expand Down Expand Up @@ -59,7 +59,7 @@ The setting is stored per server and applies to both the OAuth Debugger and the

When the authorization server returns an `iss` parameter on the callback that does not match the issuer recorded from its metadata, Inspector's behavior depends on the protocol version selected for the flow:

- **2026-07-28 (Draft)** — The flow is blocked before the authorization code is redeemed. The error names both the recorded issuer and the one returned on the callback so you can compare them directly.
- **2026-07-28 (Latest)** — The flow is blocked before the authorization code is redeemed. The error names both the recorded issuer and the one returned on the callback so you can compare them directly.
- **2025-11-25 and earlier** — The flow continues and a warning toast is shown naming both issuers. These protocol versions do not mention `iss` or RFC 9207 issuer validation, so blocking would enforce a rule the selected version does not contain.

In both cases the mismatch is recorded on the OAuth trace at the `received_authorization_code` step, visible in the **Last OAuth Trace** panel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { useState } from "react";
import { describe, expect, it, vi, beforeEach } from "vitest";
import { protocolVersionLabel } from "@mcpjam/sdk/browser";
import { AuthenticationSection } from "../shared/AuthenticationSection";
import { fetchOAuthClientSecret } from "@/lib/apis/hosted-oauth-client-secret-api";

Expand Down Expand Up @@ -803,7 +804,7 @@ describe("AuthenticationSection", () => {
screen.getByRole("button", { name: /advanced settings/i })
);

it("offers the 2026-07-28 (Draft) option in the Protocol dropdown", () => {
it("offers the 2026-07-28 option in the Protocol dropdown", () => {
render(
<AuthenticationSection
{...protocolBaseProps}
Expand All @@ -812,8 +813,10 @@ describe("AuthenticationSection", () => {
);
openAdvanced();
// Radix Select renders the selected item's label in the trigger; the
// 2026 draft option resolving to a label proves it is in PROTOCOL_OPTIONS.
expect(screen.getByText("2026-07-28 (Draft)")).toBeInTheDocument();
// 2026 option resolving to a label proves it is in PROTOCOL_OPTIONS.
expect(
screen.getByText(protocolVersionLabel("2026-07-28"))
).toBeInTheDocument();
});

it("keeps Auto visible when the wire pin is 2026-07-28", () => {
Expand All @@ -827,7 +830,7 @@ describe("AuthenticationSection", () => {
openAdvanced();
expect(screen.getByText("Auto")).toBeInTheDocument();
expect(
screen.queryByText("2026-07-28 (Draft)")
screen.queryByText(protocolVersionLabel("2026-07-28"))
).not.toBeInTheDocument();
});

Expand All @@ -842,7 +845,7 @@ describe("AuthenticationSection", () => {
openAdvanced();
expect(screen.getByText("Auto")).toBeInTheDocument();
expect(
screen.queryByText("2026-07-28 (Draft)")
screen.queryByText(protocolVersionLabel("2026-07-28"))
).not.toBeInTheDocument();
});

Expand All @@ -861,7 +864,7 @@ describe("AuthenticationSection", () => {
openAdvanced();
expect(screen.getByText("Auto")).toBeInTheDocument();
expect(
screen.queryByText("2025-11-25 (Latest)")
screen.queryByText(protocolVersionLabel("2025-11-25"))
).not.toBeInTheDocument();
});

Expand Down Expand Up @@ -890,7 +893,7 @@ describe("AuthenticationSection", () => {
openAdvanced();
expect(screen.getByText("2025-06-18")).toBeInTheDocument();
expect(
screen.queryByText("2026-07-28 (Draft)")
screen.queryByText(protocolVersionLabel("2026-07-28"))
).not.toBeInTheDocument();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
SelectValue,
} from "@mcpjam/design-system/select";
import {
protocolVersionLabel,
readXaaEnterprisePolicy,
resolveAuthorizationPlan,
type McpProtocolVersion,
Expand All @@ -29,6 +30,7 @@ import type { RegistrationMode, XaaClientAuthMethod } from "@/shared/xaa.js";
import type { ConfidentialCimdCapabilityStatus } from "@/hooks/use-confidential-cimd-capability";
import {
resolveEffectiveOauthProtocolMode,
SERVER_FORM_OAUTH_PROTOCOL_MODES,
type ServerFormAuthType,
type ServerFormOAuthProtocolMode,
} from "@/shared/types.js";
Expand Down Expand Up @@ -122,15 +124,21 @@ interface AuthenticationSectionProps {
xaaDcrStatus?: "registered" | "registering" | "uncertain";
}

/**
* "Auto" first, then every era newest-first.
* `SERVER_FORM_OAUTH_PROTOCOL_MODES` is oldest-first, and the labels come
* from the SDK's `protocolVersionLabel`, so adding an era moves the "Latest"
* marker here without this file changing.
*/
const PROTOCOL_OPTIONS: Array<{
value: ServerFormOAuthProtocolMode;
label: string;
}> = [
{ value: "auto", label: "Auto" },
{ value: "2026-07-28", label: "2026-07-28 (Draft)" },
{ value: "2025-11-25", label: "2025-11-25 (Latest)" },
{ value: "2025-06-18", label: "2025-06-18" },
{ value: "2025-03-26", label: "2025-03-26 (Legacy)" },
...[...SERVER_FORM_OAUTH_PROTOCOL_MODES].reverse().map((version) => ({
value: version,
label: protocolVersionLabel(version),
})),
];

// Options come from the shared registration-vocabulary label module, so the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
isKnownProtocolVersion,
isStatelessProtocolVersion,
MCP_PROTOCOL_VERSIONS,
protocolVersionLabel,
readTasksPolicy,
readXaaEnterprisePolicy,
setTasksPolicy,
Expand Down Expand Up @@ -90,24 +91,11 @@ const TASKS_POLICY_VALUE: Partial<Record<TasksPolicy, TasksPolicyChoice>> = {
};

/**
* Decorate the two newest revisions with their product labels:
*
* - **Latest** = newest known revision. Derived from
* `MCP_PROTOCOL_VERSIONS` so the marker walks forward automatically.
* - **November** = the 2025-11-25 revision.
*
* `MCP_PROTOCOL_VERSIONS` is ordered oldest-first; the dropdown lists
* newest-first.
* newest-first. Labels come from the SDK's `protocolVersionLabel` so this
* picker, the Connect page, and the OAuth debugger agree on which revision
* is Latest.
*/
const LATEST_PROTOCOL_VERSION: McpProtocolVersion | undefined =
MCP_PROTOCOL_VERSIONS[MCP_PROTOCOL_VERSIONS.length - 1];

function protocolVersionLabel(version: McpProtocolVersion): string {
if (version === LATEST_PROTOCOL_VERSION) return `Latest (${version})`;
if (version === "2025-11-25") return `November (${version})`;
return version;
}

const HOST_PROTOCOL_OPTIONS: Array<{
value: HostProtocolDropdownValue;
label: string;
Expand Down
36 changes: 24 additions & 12 deletions mcpjam-inspector/client/src/components/oauth/OAuthProfileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { Label } from "@mcpjam/design-system/label";
import {
getDefaultRegistrationStrategy,
getSupportedRegistrationStrategies,
MCP_PROTOCOL_VERSIONS,
protocolVersionLabel,
type OAuthProtocolVersion,
} from "@mcpjam/sdk/browser";
import type {
Expand Down Expand Up @@ -60,6 +62,19 @@ interface OAuthProfileModalProps {
}) => void | Promise<void>;
}

/**
* Every revision the debugger can drive a flow against, oldest-first, with
* the labels the host protocol picker and the Connect page use. Derived so a
* new revision cannot leave this dropdown claiming an older one is Latest.
*/
const PROTOCOL_OPTIONS: Array<{
value: OAuthProtocolVersion;
label: string;
}> = MCP_PROTOCOL_VERSIONS.map((version) => ({
value: version,
label: protocolVersionLabel(version),
}));

interface HeaderRow {
id: string;
key: string;
Expand Down Expand Up @@ -391,18 +406,15 @@ export function OAuthProfileModal({
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="2025-03-26" className="text-xs">
2025-03-26
</SelectItem>
<SelectItem value="2025-06-18" className="text-xs">
2025-06-18
</SelectItem>
<SelectItem value="2025-11-25" className="text-xs">
2025-11-25 (Latest)
</SelectItem>
<SelectItem value="2026-07-28" className="text-xs">
2026-07-28 (Draft)
</SelectItem>
{PROTOCOL_OPTIONS.map((option) => (
<SelectItem
key={option.value}
value={option.value}
className="text-xs"
>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
Expand Down
Loading
Loading