/* * Copyright (C) 2026 TomTom Navigation B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { describe, expect, it } from "vitest"; import { callerForLog, clientName, mintSessionId, parseSessionId, requestSession, } from "./clientSession"; const UUID = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; const encoded = (name: string) => Buffer.from(name).toString("base64url"); describe("clientName", () => { it("is the name as sent", () => { expect(clientName("openai-mcp (Codex)")).toBe("openai-mcp (Codex)"); expect(clientName("Anthropic/ClaudeAI")).toBe("Anthropic/ClaudeAI"); }); it("is unknown without a name, or with control characters in it", () => { expect(clientName(undefined)).toBe("unknown"); expect(clientName("")).toBe("unknown"); expect(clientName(" ")).toBe("unknown"); expect(clientName("bad\nclient")).toBe("unknown"); }); it("keeps the first 64 characters of a long name", () => { const name = clientName("x".repeat(100)); expect(name).toBe("x".repeat(64)); }); it("keeps an emoji that falls on the 64th character whole", () => { const name = clientName(`${"x".repeat(63)}\u{1F600}more`); expect(name).toBe(`${"x".repeat(63)}\u{1F600}`); }); }); describe("mintSessionId", () => { it("writes the apps token and the encoded client name before a fresh UUID", () => { const sessionId = mintSessionId({ apps: "text-only", clientName: "claude-code" }); expect(sessionId).toMatch(new RegExp(`^text-only\\.${encoded("claude-code")}\\.`)); expect(sessionId).toMatch(UUID); expect(mintSessionId({ apps: "text-only", clientName: "claude-code" })).not.toBe(sessionId); }); it("stays header-safe for a name with spaces, slashes and dots", () => { const sessionId = mintSessionId({ apps: "apps", clientName: "openai-mcp (Codex) v1.2/beta" }); expect(sessionId).toMatch(/^[\x21-\x7E]+$/); expect(sessionId.split(".")).toHaveLength(3); }); }); describe("parseSessionId", () => { it("reads back what was minted, the name exactly", () => { for (const client of [ "openai-mcp (Codex)", "Anthropic/ClaudeAI", "claude-code", "unknown", `${"x".repeat(63)}\u{1F600}`, ]) { expect(parseSessionId(mintSessionId({ apps: "apps", clientName: client }))).toEqual({ apps: "apps", clientName: client, }); } expect(parseSessionId(mintSessionId({ apps: "unknown", clientName: "mcp" }))).toEqual({ apps: "unknown", clientName: "mcp", }); }); it("is unknown on both without a session id", () => { expect(parseSessionId(undefined)).toEqual({ apps: "unknown", clientName: "unknown" }); expect(parseSessionId("")).toEqual({ apps: "unknown", clientName: "unknown" }); }); it("keeps a text-only session minted before the client token existed", () => { expect(parseSessionId("text-only-3f1c9a2e-0000-4000-8000-000000000000")).toEqual({ apps: "text-only", clientName: "unknown", }); }); it("is unknown for an apps token outside its closed set", () => { expect( parseSessionId(`widgets.${encoded("chatgpt")}.3f1c9a2e-0000-4000-8000-000000000000`) ).toEqual({ apps: "unknown", clientName: "chatgpt", }); }); it("is unknown for a client token that is not an encoded name", () => { expect(parseSessionId("apps.claude-ai.3f1c9a2e-0000-4000-8000-000000000000")).toEqual({ apps: "apps", clientName: "unknown", }); expect( parseSessionId(`apps.${encoded("bad\nclient")}.3f1c9a2e-0000-4000-8000-000000000000`) ).toEqual({ apps: "apps", clientName: "unknown", }); expect( parseSessionId(`apps.${encoded("x".repeat(65))}.3f1c9a2e-0000-4000-8000-000000000000`) ).toEqual({ apps: "apps", clientName: "unknown", }); expect(parseSessionId("3f1c9a2e-0000-4000-8000-000000000000")).toEqual({ apps: "unknown", clientName: "unknown", }); }); }); describe("requestSession", () => { const sessionId = mintSessionId({ apps: "apps", clientName: "openai-mcp" }); it("is the session its id carries, whatever the User-Agent", () => { expect(requestSession(sessionId, "something-else/2.0")).toEqual({ apps: "apps", clientName: "openai-mcp", }); }); it("keeps an id this server never minted unknown, whatever the User-Agent", () => { expect(requestSession("not-a-session", "openai-mcp/1.0.0")).toEqual({ apps: "unknown", clientName: "unknown", }); }); it("names a request without a session id by its raw User-Agent", () => { expect(requestSession(undefined, "openai-mcp/1.0.0")).toEqual({ apps: "unknown", clientName: "openai-mcp/1.0.0", }); }); it("is unknown for a request without a session id or a usable User-Agent", () => { expect(requestSession(undefined, undefined).clientName).toBe("unknown"); expect(requestSession(undefined, "bad\nagent").clientName).toBe("unknown"); }); }); describe("callerForLog", () => { const server = (client?: { name: string; version: string }) => ({ server: { getClientVersion: () => client } }) as unknown as McpServer; it("names a stdio client by its initialize", () => { expect(callerForLog(server({ name: "claude-ai", version: "1" }), {})).toEqual({ client: "claude-ai", user_agent: undefined, }); }); it("names a stdio client as its session id would", () => { expect(callerForLog(server({ name: " x".padEnd(100, "x"), version: "1" }), {}).client).toBe( "x".repeat(64) ); expect(callerForLog(server({ name: "bad\nclient", version: "1" }), {}).client).toBe("unknown"); }); it("names an HTTP client by its session id, with its User-Agent", () => { const headers = { "mcp-session-id": mintSessionId({ apps: "apps", clientName: "openai-mcp" }), "user-agent": "openai-mcp/1.0.0", }; expect(callerForLog(server(), { requestInfo: { headers } })).toEqual({ client: "openai-mcp", user_agent: "openai-mcp/1.0.0", }); }); it("names an HTTP call without a session id by its User-Agent", () => { const headers = { "user-agent": "openai-mcp/1.0.0" }; expect(callerForLog(server(), { requestInfo: { headers } }).client).toBe("openai-mcp/1.0.0"); }); it("is unknown for an HTTP call without a session id or a User-Agent", () => { expect(callerForLog(server(), { requestInfo: { headers: {} } }).client).toBe("unknown"); }); });