PrefectHQ/fastmcp · error · CIMDValidationError

CIMD client_id mismatch: document says '{doc.client_id}' but

Error message

CIMD client_id mismatch: document says '{doc.client_id}' but was fetched from '{client_id_url}'

What it means

The CIMD spec requires that the client_id field inside the metadata document exactly match the URL the document was fetched from (modulo trailing slash). fetch() compares them after rstrip('/') and raises CIMDValidationError on mismatch, preventing clients from claiming someone else's identity.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/cimd.py:418

            else:
                self._remove_cache_entry(client_id_url)
            return cached.doc

        now = time.time()
        policy = self._parse_cache_policy(response.headers, now)

        try:
            data = json.loads(response.content)
        except json.JSONDecodeError as e:
            raise CIMDValidationError(f"CIMD document is not valid JSON: {e}") from e

        try:
            doc = CIMDDocument.model_validate(data)
        except Exception as e:
            raise CIMDValidationError(f"Invalid CIMD document: {e}") from e

        if str(doc.client_id).rstrip("/") != client_id_url.rstrip("/"):
            raise CIMDValidationError(
                f"CIMD client_id mismatch: document says '{doc.client_id}' "
                f"but was fetched from '{client_id_url}'"
            )

        # Validate jwks_uri if present (SSRF check for JWKS endpoint)
        if doc.jwks_uri:
            jwks_uri_str = str(doc.jwks_uri)
            try:
                await validate_url(jwks_uri_str)
            except SSRFError as e:
                raise CIMDValidationError(
                    f"CIMD jwks_uri failed SSRF validation: {e}"
                ) from e

        logger.info(
            "CIMD document fetched and validated: %s (client_name=%s)",
            client_id_url,
            doc.client_name,

View on GitHub (pinned to 1f02114297)

Solutions

  1. Update the client_id field inside the hosted JSON document to exactly equal the URL where it is hosted
  2. Check scheme, host, port, and path — all must match (only trailing '/' is ignored)
  3. If the document was moved, update the embedded client_id and point clients at the new URL
  4. Avoid serving the document from a URL that redirects; serve it directly at the canonical client_id URL

Example fix

// document hosted at https://app.example.com/client.json
// before
{"client_id": "https://staging.example.com/client.json", ...}
// after
{"client_id": "https://app.example.com/client.json", ...}
Defensive patterns

Strategy: validation

Validate before calling

def client_id_matches(doc: dict, hosted_url: str) -> bool:
    return str(doc.get("client_id", "")).rstrip("/") == hosted_url.rstrip("/")

Try / catch

try:
    doc = await fetcher.get_client(client_id)
except CIMDValidationError as e:
    if "client_id mismatch" in str(e):
        raise InvalidClientError("document client_id must equal hosting URL") from e
    raise

Prevention

When it happens

Trigger: A CIMD document whose 'client_id' value differs from client_id_url — e.g. document hosted at https://a.example.com/client.json declaring client_id 'https://b.example.com/client.json', or http vs https mismatch, or trailing-path differences.

Common situations: Document copied from a staging environment to production without updating the embedded client_id; hosting moved to a new domain; trailing-slash or scheme (http/https) discrepancies; documents served from a redirect where fetch URL differs from canonical URL.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/4cc5426a79ac4c9e. Report an issue: GitHub.