test(agents): make Gemini MCP smoke local

This commit is contained in:
Peter Steinberger
2026-04-25 21:45:50 +01:00
parent 17a213f080
commit 9d2254be06
2 changed files with 45 additions and 10 deletions

View File

@@ -174,7 +174,7 @@ OPENCLAW_LIVE_TEST=1 \
This does not ask Gemini to generate a response. It writes the same system
settings OpenClaw gives Gemini, then runs `gemini --debug mcp list` to prove a
saved `transport: "streamable-http"` server is normalized to Gemini's HTTP MCP
shape and can connect.
shape and can connect to a local streamable-HTTP MCP server.
Docker recipe:

View File

@@ -1,5 +1,8 @@
import { execFile } from "node:child_process";
import http from "node:http";
import { promisify } from "node:util";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { describe, expect, it } from "vitest";
import { isLiveTestEnabled } from "../live-test-helpers.js";
import { prepareCliBundleMcpConfig } from "./bundle-mcp.js";
@@ -17,6 +20,42 @@ async function canRunGemini(command: string): Promise<boolean> {
}
}
async function startLocalStreamableHttpMcpServer(): Promise<{
url: string;
close: () => Promise<void>;
}> {
const mcpServer = new McpServer({ name: "openclaw-gemini-live-probe", version: "1.0.0" });
mcpServer.tool("openclaw_live_probe", "OpenClaw Gemini MCP live probe", async () => ({
content: [{ type: "text", text: "ok" }],
}));
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await mcpServer.connect(transport);
const httpServer = http.createServer(async (req, res) => {
if (!req.url?.startsWith("/mcp")) {
res.writeHead(404).end();
return;
}
await transport.handleRequest(req, res);
});
await new Promise<void>((resolve) => {
httpServer.listen(0, "127.0.0.1", resolve);
});
const address = httpServer.address();
const port = typeof address === "object" && address ? address.port : 0;
return {
url: `http://127.0.0.1:${port}/mcp`,
close: async () => {
await transport.close().catch(() => undefined);
await new Promise<void>((resolve, reject) =>
httpServer.close((error) => (error ? reject(error) : resolve())),
);
},
};
}
describeLive("Gemini CLI MCP settings smoke", () => {
it("connects to an OpenClaw-configured streamable-http server", async () => {
const geminiCommand = process.env.OPENCLAW_LIVE_GEMINI_COMMAND ?? "gemini";
@@ -25,10 +64,7 @@ describeLive("Gemini CLI MCP settings smoke", () => {
return;
}
const inheritedEnv =
typeof process.env.CONTEXT7_API_KEY === "string" && process.env.CONTEXT7_API_KEY
? { CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY }
: undefined;
const probeServer = await startLocalStreamableHttpMcpServer();
const prepared = await prepareCliBundleMcpConfig({
enabled: true,
mode: "gemini-system-settings",
@@ -41,15 +77,13 @@ describeLive("Gemini CLI MCP settings smoke", () => {
plugins: { enabled: false },
mcp: {
servers: {
context7: {
openclawLiveProbe: {
transport: "streamable-http",
url: "https://mcp.context7.com/mcp",
...(inheritedEnv ? { headers: { Authorization: "Bearer ${CONTEXT7_API_KEY}" } } : {}),
url: probeServer.url,
},
},
},
},
env: inheritedEnv,
});
try {
@@ -62,11 +96,12 @@ describeLive("Gemini CLI MCP settings smoke", () => {
maxBuffer: 1024 * 1024,
});
const output = `${result.stdout}\n${result.stderr}`;
expect(output).toContain("context7");
expect(output).toContain("openclawLiveProbe");
expect(output).toMatch(/\(http\)|type:\s*http|http/i);
expect(output).not.toContain("transport");
} finally {
await prepared.cleanup?.();
await probeServer.close();
}
}, 60_000);
});