Add Fortress MCP bridge with fortress_ tool prefix and workspace write dir
This commit is contained in:
48
src/agent/fortress-mcp.test.ts
Normal file
48
src/agent/fortress-mcp.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { mkdtemp, rm, stat } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createLogger } from "../log.js";
|
||||
import { _internals, createFortressMcpBridge } from "./fortress-mcp.js";
|
||||
import type { McpClientLike, SpawnSpec } from "./mcp-bridge.js";
|
||||
|
||||
const silentLogger = createLogger("error", () => {});
|
||||
const client: McpClientLike = {
|
||||
listTools: async () => ({ tools: [{ name: "read_page" }, { name: "fetch_protected_page" }] }),
|
||||
callTool: async () => ({ content: [] }),
|
||||
close: async () => {},
|
||||
};
|
||||
|
||||
describe("createFortressMcpBridge", () => {
|
||||
it("spawns the configured command with Fortress env, creates the write dir, and prefixes tools", async () => {
|
||||
const dir = await mkdtemp(path.join(os.tmpdir(), "fortress-"));
|
||||
try {
|
||||
const writeDir = path.join(dir, "ws", "fortress");
|
||||
const seen: SpawnSpec[] = [];
|
||||
const bridge = await createFortressMcpBridge({
|
||||
command: ["python", "-m", "tilion.mcp"],
|
||||
headless: true,
|
||||
writeDir,
|
||||
passthroughEnv: { TILION_PROXY: "http://p:1" },
|
||||
logger: silentLogger,
|
||||
clientFactory: async (s) => { seen.push(s); return client; },
|
||||
});
|
||||
expect(seen).toEqual([{
|
||||
command: "python",
|
||||
args: ["-m", "tilion.mcp"],
|
||||
env: { TILION_PROXY: "http://p:1", TILION_MCP_HEADLESS: "1", TILION_MCP_WRITE_DIR: writeDir },
|
||||
}]);
|
||||
expect((await stat(writeDir)).isDirectory()).toBe(true);
|
||||
expect(bridge.tools.map((t) => t.name)).toEqual(["fortress_read_page", "fortress_fetch_protected_page"]);
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("buildEnv maps headless=false to TILION_MCP_HEADLESS=0", () => {
|
||||
expect(_internals.buildEnv({ headless: false, writeDir: "/w", passthroughEnv: {} })).toEqual({
|
||||
TILION_MCP_HEADLESS: "0",
|
||||
TILION_MCP_WRITE_DIR: "/w",
|
||||
});
|
||||
});
|
||||
});
|
||||
43
src/agent/fortress-mcp.ts
Normal file
43
src/agent/fortress-mcp.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { mkdir } from "node:fs/promises";
|
||||
import type { Logger } from "../log.js";
|
||||
import { createMcpBridge, type McpBridge, type McpBridgeOptions } from "./mcp-bridge.js";
|
||||
|
||||
export interface FortressMcpOptions {
|
||||
// argv that starts the server, e.g. ["tilion-mcp"] or ["python", "-m", "tilion.mcp"].
|
||||
command: string[];
|
||||
headless: boolean;
|
||||
// Absolute dir inside the workspace; screenshots, saved pages, downloads and profiles land here.
|
||||
writeDir: string;
|
||||
// TILION_PROXY / TILION_REGION / CAPTCHA_API_KEY when set.
|
||||
passthroughEnv: Record<string, string>;
|
||||
logger: Logger;
|
||||
// Test seam, forwarded to createMcpBridge.
|
||||
clientFactory?: McpBridgeOptions["clientFactory"];
|
||||
}
|
||||
|
||||
export async function createFortressMcpBridge(opts: FortressMcpOptions): Promise<McpBridge> {
|
||||
const [command, ...args] = opts.command;
|
||||
if (!command) throw new Error("fortress command is empty");
|
||||
// tilion-mcp defaults to <tmpdir>/tilion-mcp, which the send tool can't reach.
|
||||
await mkdir(opts.writeDir, { recursive: true });
|
||||
return createMcpBridge({
|
||||
label: "fortress",
|
||||
command,
|
||||
args,
|
||||
env: buildEnv(opts),
|
||||
toolPrefix: "fortress_",
|
||||
logger: opts.logger,
|
||||
clientFactory: opts.clientFactory,
|
||||
});
|
||||
}
|
||||
|
||||
function buildEnv(opts: Pick<FortressMcpOptions, "headless" | "writeDir" | "passthroughEnv">): Record<string, string> {
|
||||
return {
|
||||
...opts.passthroughEnv,
|
||||
TILION_MCP_HEADLESS: opts.headless ? "1" : "0",
|
||||
TILION_MCP_WRITE_DIR: opts.writeDir,
|
||||
};
|
||||
}
|
||||
|
||||
// exported for tests
|
||||
export const _internals = { buildEnv };
|
||||
Reference in New Issue
Block a user