Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.agentium.in/llms.txt

Use this file to discover all available pages before exploring further.

MCP Auth Conformance (2026-07-28 spec)

The Model Context Protocol 2026-07-28 spec tightens authorization in two specific ways:
  1. RFC 9207 — iss parameter validation. Mitigates “OAuth mix-up” attacks where a malicious AS swaps tokens between two ASes the client is talking to.
  2. OpenID Connect Dynamic Client Registration with application_type. Lets the client tell the AS whether it’s a native app or a web app at registration time.
Agentium ships small helpers for both, exposed from @agentium/core/mcp/auth-validation.ts. They’re framework-agnostic — use them in any MCP client implementation, not just Agentium’s.

RFC 9207 — validateAuthorizationResponse

When the AS redirects back to your callback URL with ?code=...&state=..., it should also include an iss parameter (the AS’s own URL identifier). If it doesn’t match the AS you actually sent the user to, abort.

Why

Mix-up attack flow (simplified):
  1. Client supports two ASes: bank.example and attacker.example.
  2. User starts a sign-in with bank.example.
  3. Attacker convinces user to also click “sign in with attacker.example”.
  4. Attacker’s AS sends a callback that looks like it came from bank.
  5. Client exchanges the code against bank.example (wrong AS!).
  6. Bank issues a token for attacker’s account back to the attacker.
iss blocks step 4 — the callback must declare which AS sent it, and the client checks the value matches the AS the user actually picked.

Helper

import { validateAuthorizationResponse } from "@agentium/core";

const callbackUrl = new URL(req.url, `https://${req.headers.host}`);
// e.g. https://app.example.com/callback?code=abc&state=xyz&iss=https%3A%2F%2Fmy-as.example.com

try {
  validateAuthorizationResponse(callbackUrl, "https://my-as.example.com");
  // Safe to exchange code for tokens.
} catch (err) {
  if (err instanceof AuthValidationError) {
    res.status(400).end(err.message);
    return;
  }
  throw err;
}

AuthValidationError

Thrown when:
  • iss is missing from the callback query.
  • iss is present but doesn’t match the expected issuer (string equality on URL.origin + URL.pathname).
class AuthValidationError extends Error {
  readonly name = "AuthValidationError";
  readonly code: "missing_iss" | "iss_mismatch";
}

When this matters most

Agentium clients that talk to multiple MCP authorization servers (e.g. a research agent that connects to GitHub MCP + Slack MCP + an internal MCP). For single-AS deployments the attack vector is weak but the check is cheap, so the default behavior in the spec is “always validate.”

OIDC Dynamic Client Registration — validateApplicationType

OIDC Dynamic Client Registration lets a client app register itself with the AS at runtime. The spec requires the client to declare application_type:
  • "native" — the client runs on a device (desktop, mobile, embedded). Custom URI schemes (myapp://callback) are allowed.
  • "web" — the client runs at an HTTPS URL. Redirect URIs must be HTTPS, no custom schemes.
Agents that act as MCP clients should pick the right type or the AS may reject the registration / token request.

Helper

import { validateApplicationType } from "@agentium/core";

validateApplicationType("web",    "https://app.example.com/callback");  // ✅
validateApplicationType("native", "myapp://callback");                   // ✅
validateApplicationType("web",    "http://app.example.com/callback");   // ✕ "web clients must use https"
validateApplicationType("web",    "myapp://callback");                   // ✕ "web clients cannot use custom schemes"
validateApplicationType("native", "https://app.example.com/callback");  // ✅ but warn — native should usually use loopback or custom scheme
Throws AuthValidationError on policy violations.

Wiring into your MCP client

The MCP TypeScript SDK exposes an OAuthClient interface that you implement. Sketch:
import {
  validateAuthorizationResponse,
  validateApplicationType,
  AuthValidationError,
} from "@agentium/core";

class MyMCPOAuthClient implements OAuthClient {
  async register(metadata: AuthorizationServerMetadata) {
    validateApplicationType("native", this.redirectUri);
    return await this.dynamicRegister({
      application_type: "native",
      redirect_uris: [this.redirectUri],
      // ...
    });
  }

  async handleCallback(callbackUrl: URL) {
    try {
      validateAuthorizationResponse(callbackUrl, this.issuerUrl);
    } catch (err) {
      if (err instanceof AuthValidationError) {
        throw new Error(`Refusing token exchange: ${err.message}`);
      }
      throw err;
    }
    // proceed to exchange code for tokens
  }
}

Other 2026-07-28 changes (FYI)

The spec includes more than just iss validation. Other notable changes:
  • Mandatory PKCE for all clients (even confidential ones). Agentium’s MCP SDK enables PKCE by default.
  • Resource Indicators (RFC 8707) — clients should pass the MCP server URL as a resource parameter so the AS knows which protected resource the token is for. The MCP SDK adds this automatically when you set resource: ... on the auth request.
  • JWT access tokens with audience checks — when accepting tokens at an MCP server, verify the aud claim matches the server’s URL.
These are implemented in the underlying MCP SDK, not in auth-validation.ts. The helpers here cover the two specific call sites where you (the client integrator) need to make a decision: callback validation and registration.

Best practices

  • Pin known ASes by URL. Don’t accept arbitrary issuer URLs at runtime.
  • Log every AuthValidationError. A spike usually means an attack attempt or a misconfigured AS.
  • For desktop apps, use the loopback flow (http://127.0.0.1:<random_port>/callback). It’s safer than custom schemes (which can be hijacked by other apps).
  • Use refresh tokens with short access-token TTLs (5–15 minutes). Limits the blast radius of any token compromise.

See also