Skip to content
Closed
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
6 changes: 5 additions & 1 deletion shared/glean/mcp/src/remote-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ function buildTransport(
): StreamableHTTPClientTransport {
const parsedUrl = new URL(serverUrl);
const headers: Record<string, string> = {
"X-Glean-Internal-Service": "true",
// Note: do NOT add an "X-Glean-Internal-Service" header here. Glean's
// public edge (GCP LB) refuses any external request carrying that
// internal-service marker with HTTP 403 before auth, which breaks the
// plugin's OAuth flow entirely (gleanwork/claude-plugins installs, 3.4.x
// and 3.5.x, were blocked on every instance).
"X-Glean-Gateway-Request-Metadata": buildGatewayMetadataHeader(chatSessionId),
};

Expand Down
28 changes: 28 additions & 0 deletions shared/glean/mcp/tests/remote-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ describe("buildGatewayMetadataHeader", () => {
});
});

describe("transport request headers", () => {
// Regression: plugin builds up to 3.5.1 sent X-Glean-Internal-Service: true
// on every gateway request. Glean's public edge 403s any external request
// carrying that internal marker, before auth, breaking setup on every
// instance (401 OAuth challenge without it, 403 with it).
it("omits the internal-service marker and keeps gateway metadata", async () => {
let captured: Headers | undefined;
const handler = createMcpHandler(() => {
return new McpServer({ name: "test", version: "1.0.0" });
});
const client = await createRemoteClient("http://test.local/mcp", {
fetch: (url, init) => {
const request = new Request(url, init);
captured ??= request.headers;
return handler.fetch(request);
},
});
try {
expect(captured).toBeDefined();
expect(captured!.has("x-glean-internal-service")).toBe(false);
expect(captured!.get("x-glean-gateway-request-metadata")).not.toBeNull();
} finally {
await client.close();
await handler.close();
}
});
});

describe("remoteToolTimeoutMs", () => {
const KEY = "GLEAN_REMOTE_TOOL_TIMEOUT_MS";
const original = process.env[KEY];
Expand Down