apache/shardingsphere · error · MCPTransportSecurityException

Session attribution does not match this MCP session.

Error message

Session attribution does not match this MCP session.

What it means

Thrown by the HTTP transport security validator when the session-attribution identity resolved from the current request's headers does not equal the identity that was bound to the MCP session when it was created (subject, source, and attributes must all match). It protects session hijacking/pinning: a session id issued to one client cannot be reused by another origin, user, or token. The server returns HTTP 400 with category CATEGORY_SESSION_ATTRIBUTION_MISMATCH. Validation is skipped when the resolver is disabled or the session id is unknown, so this error only fires on an existing session whose header-derived attribution drifted.

Source

Thrown at mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ShardingSphereServerTransportSecurityValidator.java:69

        originHeaderConstraint.validate(getFirstHeaderValue(headers, "Origin"));
        String sessionId = getSessionId(headers);
        if (sessionId.isBlank() || !sessionManager.hasSession(sessionId)) {
            return;
        }
        validateSessionIdentity(headers, sessionId);
        protocolVersionHeaderConstraint.validate(getFirstHeaderValue(headers, HttpHeaders.PROTOCOL_VERSION));
    }
    
    private void validateSessionIdentity(final Map<String, List<String>> headers, final String sessionId) throws ServerTransportSecurityException {
        if (!sessionAttributionResolver.isEnabled()) {
            return;
        }
        Optional<MCPSessionIdentity> boundSessionIdentity = sessionManager.findSessionIdentity(sessionId);
        if (boundSessionIdentity.isEmpty()) {
            return;
        }
        if (!isSameAttribution(boundSessionIdentity.get(), sessionAttributionResolver.resolve(headers, sessionId))) {
            throw new MCPTransportSecurityException(400, "Session attribution does not match this MCP session.",
                    MCPTransportSecurityException.CATEGORY_SESSION_ATTRIBUTION_MISMATCH);
        }
    }
    
    private boolean isSameAttribution(final MCPSessionIdentity expected, final MCPSessionIdentity actual) {
        return expected.getSubject().equals(actual.getSubject()) && expected.getSource().equals(actual.getSource()) && expected.getAttributes().equals(actual.getAttributes());
    }
    
    private String getSessionId(final Map<String, List<String>> headers) {
        return headers.entrySet().stream()
                .filter(entry -> HttpHeaders.MCP_SESSION_ID.equalsIgnoreCase(entry.getKey()) && !entry.getValue().isEmpty()).findFirst()
                .map(optional -> Objects.toString(optional.getValue().getFirst(), "")).orElse("");
    }
    
    private String getFirstHeaderValue(final Map<String, List<String>> headers, final String headerName) {
        return headers.entrySet().stream()
                .filter(entry -> headerName.equalsIgnoreCase(entry.getKey()) && !entry.getValue().isEmpty()).findFirst().map(optional -> Objects.toString(optional.getValue().getFirst(), "").trim())
                .orElse("");

View on GitHub (pinned to e952770a21)

Solutions

  1. Start a new MCP session (re-run initialize) with the current identity headers and use the new session id for all subsequent calls.
  2. Verify the client sends exactly the same identity/attribution headers on every request of a session (check proxy or middleware that mutates them).
  3. If session pinning to identity is not wanted in your deployment, disable the session attribution resolver (sessionAttributionResolver.isEnabled() returns false skips the check).
  4. Check server logs for which of subject/source/attributes diverged and align the client's headers.

Example fix

// before: initialize with token A, then reuse session id with token B
initialize -> sessionId=S1 (Authorization: Bearer tokenA)
call tool with S1 (Authorization: Bearer tokenB)  // 400 attribution mismatch

// after: re-initialize whenever credentials change
initialize (Authorization: Bearer tokenB) -> sessionId=S2
call tool with S2 (Authorization: Bearer tokenB)
Defensive patterns

Strategy: validation

Validate before calling

// Re-initialize when identity inputs change, and pin headers per session
function identityFingerprint(headers) {
  return JSON.stringify([headers['authorization'], headers['x-forwarded-user'], headers['x-mcp-source']]);
}
const session = { id: null, fingerprint: null };
function ensureSession(initHeaders) {
  const fp = identityFingerprint(initHeaders);
  if (session.id === null || session.fingerprint !== fp) {
    session.id = mcp.initialize(initHeaders).sessionId;
    session.fingerprint = fp;
    session.headers = initHeaders; // reuse EXACT same headers afterwards
  }
  return session;
}

Try / catch

try {
  await mcp.request('tools/call', params, sessionHeaders);
} catch (e) {
  if (e.status === 400 && e.category === 'SESSION_ATTRIBUTION_MISMATCH') {
    session.id = null;               // force re-initialize with current identity
    return retryAfterReinitialize(params);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any MCP HTTP endpoint with a valid Mcp-Session-Id header while the attribution headers (e.g. authenticated subject, source, or attribute headers consumed by SessionAttributionResolver.resolve) differ from those present at Initialize time. Typical: replaying a session id from a different bearer token, a different proxy that rewrites identity headers, or a client that adds/removes an identity header between requests.

Common situations: Token rotation mid-session (new subject claim), a gateway that forwards different headers per request, multiple tabs/agents sharing a session id with different auth, or a changed issuer/attribute set after re-login while reusing an old session id.

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/e7349d9303f36c0f. Report an issue: GitHub.