fix(browser-tools): restore MCP-compatible outputs

Browser tools were registering union output schemas that the MCP SDK could not validate at call time. Use raw object output shapes and cover the real tools/call boundary.
This commit is contained in:
Mohamed Boudra
2026-07-02 22:14:26 +02:00
parent 441b935b73
commit 742d47fe71
5 changed files with 191 additions and 198 deletions

View File

@@ -1,4 +1,6 @@
import { execFileSync } from "node:child_process";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { describe, expect, it, vi } from "vitest";
import { realpathSync, rmSync } from "node:fs";
import { access, mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
@@ -13,6 +15,7 @@ import { AgentManager, type ManagedAgent } from "./agent-manager.js";
import { AgentStorage, type StoredAgentRecord } from "./agent-storage.js";
import { createTestAgentClients } from "../test-utils/fake-agent-client.js";
import type { AgentMode, AgentProvider, ProviderSnapshotEntry } from "./agent-sdk-types.js";
import type { ProviderSnapshotManager } from "./provider-snapshot-manager.js";
import { createProviderSnapshotManagerStub } from "../test-utils/session-stubs.js";
import {
AgentListItemPayloadSchema,
@@ -36,6 +39,8 @@ import { WorkspaceGitServiceImpl } from "../workspace-git-service.js";
import type { GitHubService } from "../../services/github-service.js";
import type { TerminalManager } from "../../terminal/terminal-manager.js";
import { PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels";
import type { BrowserToolsBroker, BrowserToolsExecuteInput } from "../browser-tools/broker.js";
import type { BrowserToolsResponsePayload } from "../browser-tools/errors.js";
const REPO_CWD = resolvePath("/tmp/repo");
const TARGET_CWD = resolvePath("/tmp/target");
@@ -487,6 +492,77 @@ function createGitHubServiceStub(): GitHubService {
};
}
class FakeBrowserToolsBroker {
public readonly calls: BrowserToolsExecuteInput[] = [];
public constructor(private readonly response: BrowserToolsResponsePayload) {}
public async execute(input: BrowserToolsExecuteInput): Promise<BrowserToolsResponsePayload> {
this.calls.push(input);
return this.response;
}
}
class BoundaryAgentManagerFake {
private readonly agent = createManagedAgent({
id: "agent-1",
cwd: REPO_CWD,
workspaceId: BROWSER_WORKSPACE_ID,
});
public getAgent(agentId: string): ManagedAgent | null {
return agentId === this.agent.id ? this.agent : null;
}
public listAgents(): ManagedAgent[] {
return [];
}
}
class BoundaryAgentStorageFake {
public async list(): Promise<StoredAgentRecord[]> {
return [];
}
}
class BoundaryProviderSnapshotManagerFake {
public listRegisteredProviderIds(): AgentProvider[] {
return [];
}
public hasProvider(): boolean {
return false;
}
public getProviderLabel(provider: AgentProvider): string {
return provider;
}
public async listProviders(): Promise<ProviderSnapshotEntry[]> {
return [];
}
public async getProvider(): Promise<ProviderSnapshotEntry> {
throw new Error("Provider catalog is not used by this boundary test");
}
public async listModels(): Promise<[]> {
return [];
}
public async listModes(): Promise<[]> {
return [];
}
}
async function connectInMemoryMcpClient(server: Awaited<ReturnType<typeof createAgentMcpServer>>) {
const client = new Client({ name: "paseo-test-client", version: "0.0.0" });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await server.connect(serverTransport);
await client.connect(clientTransport);
return client;
}
function createStoredSchedule(input: CreateScheduleInput): StoredSchedule {
const now = "2026-04-11T00:00:00.000Z";
return {
@@ -582,6 +658,74 @@ function createPaseoWorktreeForMcpTest(options: {
describe("browser MCP tools", () => {
const logger = createTestLogger();
it("calls registered tools through the MCP SDK with listed output schemas", async () => {
const agentManager = new BoundaryAgentManagerFake();
const agentStorage = new BoundaryAgentStorageFake();
const broker = new FakeBrowserToolsBroker({
requestId: "req-browser-tabs",
ok: true,
result: { command: "list_tabs", tabs: [] },
});
const server = await createAgentMcpServer({
agentManager: agentManager as AgentManager,
agentStorage: agentStorage as AgentStorage,
providerSnapshotManager:
new BoundaryProviderSnapshotManagerFake() as unknown as ProviderSnapshotManager,
browserToolsBroker: broker as BrowserToolsBroker,
callerAgentId: "agent-1",
logger,
});
const client = await connectInMemoryMcpClient(server);
try {
const browserResult = await client.callTool({
name: "browser_list_tabs",
arguments: {},
});
const listAgentsResult = await client.callTool({
name: "list_agents",
arguments: {},
});
expect(broker.calls).toEqual([
{
agentId: "agent-1",
cwd: REPO_CWD,
workspaceId: BROWSER_WORKSPACE_ID,
command: { command: "list_tabs", args: {} },
},
]);
expect(browserResult.isError).not.toBe(true);
expect(browserResult.structuredContent).toEqual({
ok: true,
result: { command: "list_tabs", tabs: [] },
context: { agentId: "agent-1", cwd: REPO_CWD, workspaceId: BROWSER_WORKSPACE_ID },
});
expect(listAgentsResult.isError).not.toBe(true);
expect(listAgentsResult.structuredContent).toEqual({
agents: [],
});
const listedTools = await client.listTools();
const toolsByName = new Map(listedTools.tools.map((tool) => [tool.name, tool]));
const registeredTools = Reflect.get(server, "_registeredTools") as Record<
string,
RegisteredMcpTool
>;
for (const [name, tool] of Object.entries(registeredTools)) {
if (tool.outputSchema !== undefined) {
expect(toolsByName.get(name)?.outputSchema, `${name} outputSchema`).toEqual(
expect.objectContaining({ type: "object" }),
);
}
}
} finally {
await client.close();
await server.close();
}
});
it("keeps browser tools registered when browser tools are disabled", async () => {
const { agentManager, agentStorage, spies } = createTestDeps();
spies.agentManager.getAgent.mockReturnValue({

View File

@@ -5,7 +5,6 @@ import type {
ServerNotification,
ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { createPaseoToolCatalog, type PaseoToolHostDependencies } from "./tools/paseo-tools.js";
import type { PaseoToolResult } from "./tools/types.js";
@@ -14,39 +13,6 @@ export type AgentMcpServerOptions = PaseoToolHostDependencies;
type McpToolContext = RequestHandlerExtra<ServerRequest, ServerNotification>;
function isZodSchema(value: unknown): value is z.ZodType {
return (
typeof value === "object" &&
value !== null &&
typeof (value as { safeParseAsync?: unknown }).safeParseAsync === "function"
);
}
function relaxMcpOutputSchema(outputSchema: unknown): unknown {
if (!outputSchema) {
return outputSchema;
}
if (isZodSchema(outputSchema)) {
return outputSchema instanceof z.ZodObject ? outputSchema.passthrough() : outputSchema;
}
return z.object(outputSchema as z.ZodRawShape).passthrough();
}
function relaxMcpToolOutputSchema<TConfig extends { outputSchema?: unknown }>(
config: TConfig,
): TConfig {
if (config.outputSchema === undefined) {
return config;
}
return {
...config,
outputSchema: relaxMcpOutputSchema(config.outputSchema),
} as TConfig;
}
function formatStructuredContentForModel(structuredContent: unknown): string {
if (
!structuredContent ||
@@ -115,12 +81,12 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
for (const tool of catalog.tools.values()) {
server.registerTool(
tool.name,
relaxMcpToolOutputSchema({
{
title: tool.title,
description: tool.description,
inputSchema: tool.inputSchema,
outputSchema: tool.outputSchema,
}),
},
async (args: unknown, context?: McpToolContext) =>
toMcpToolResult(await catalog.executeTool(tool.name, args, { signal: context?.signal })),
);

View File

@@ -14,7 +14,7 @@ export interface PaseoToolConfig {
title?: string;
description?: string;
inputSchema?: z.ZodRawShape | z.ZodType;
outputSchema?: z.ZodRawShape | z.ZodType;
outputSchema?: z.ZodRawShape;
}
export interface PaseoToolDefinition extends PaseoToolConfig {

View File

@@ -68,10 +68,6 @@ class BrowserToolHarness {
return this.get(name).handler(parsed, {});
}
public outputParses(name: string, output: unknown) {
return schemaFor(this.get(name).config.outputSchema).safeParse(output);
}
private get(name: string): RegisteredTool {
const tool = this.tools.get(name);
if (!tool) {
@@ -739,53 +735,6 @@ describe("registerBrowserTools", () => {
});
});
test("new tab declares the shape it returns", async () => {
const harness = new BrowserToolHarness();
harness.broker.setResponse(newTabPayload());
const response = await harness.execute("browser_new_tab", {});
expect(harness.outputParses("browser_new_tab", response.structuredContent)).toEqual({
success: true,
data: response.structuredContent,
});
});
test("list tabs declares the shape it returns", async () => {
const harness = new BrowserToolHarness();
const response = await harness.execute("browser_list_tabs", {});
expect(harness.outputParses("browser_list_tabs", response.structuredContent)).toEqual({
success: true,
data: response.structuredContent,
});
});
test("snapshot declares the shape it returns", async () => {
const harness = new BrowserToolHarness();
harness.broker.setResponse(snapshotPayload());
const response = await harness.execute("browser_snapshot", { browserId: BROWSER_ID });
expect(harness.outputParses("browser_snapshot", response.structuredContent)).toEqual({
success: true,
data: response.structuredContent,
});
});
test("screenshot declares the shape it returns", async () => {
const harness = new BrowserToolHarness();
harness.broker.setResponse(screenshotPayload());
const response = await harness.execute("browser_screenshot", { browserId: BROWSER_ID });
expect(harness.outputParses("browser_screenshot", response.structuredContent)).toEqual({
success: true,
data: response.structuredContent,
});
});
test("snapshot rejects calls without a browser id", () => {
const harness = new BrowserToolHarness();

View File

@@ -1,36 +1,5 @@
import { z } from "zod";
import {
BrowserAutomationBackResultSchema,
BrowserAutomationBrowserIdSchema,
BrowserAutomationCheckResultSchema,
BrowserAutomationClearResultSchema,
BrowserAutomationClickResultSchema,
BrowserAutomationDownloadResultSchema,
BrowserAutomationDragResultSchema,
BrowserAutomationEnvironmentResultSchema,
BrowserAutomationErrorSchema,
BrowserAutomationFillResultSchema,
BrowserAutomationFocusResultSchema,
BrowserAutomationForwardResultSchema,
BrowserAutomationFullPageScreenshotResultSchema,
BrowserAutomationHoverResultSchema,
BrowserAutomationKeypressResultSchema,
BrowserAutomationListTabsResultSchema,
BrowserAutomationLogsResultSchema,
BrowserAutomationNavigateResultSchema,
BrowserAutomationNewTabResultSchema,
BrowserAutomationPageInfoResultSchema,
BrowserAutomationPdfResultSchema,
BrowserAutomationReloadResultSchema,
BrowserAutomationScreenshotResultSchema,
BrowserAutomationSelectResultSchema,
BrowserAutomationSetBackgroundResultSchema,
BrowserAutomationSnapshotResultSchema,
BrowserAutomationStorageResultSchema,
BrowserAutomationTypeResultSchema,
BrowserAutomationUploadResultSchema,
BrowserAutomationWaitResultSchema,
} from "@getpaseo/protocol/browser-automation/rpc-schemas";
import { BrowserAutomationBrowserIdSchema } from "@getpaseo/protocol/browser-automation/rpc-schemas";
import type { BrowserToolsBroker } from "./broker.js";
import type { BrowserToolsResponsePayload } from "./errors.js";
import type {
@@ -66,29 +35,25 @@ const WORKSPACE_CONTEXT_MESSAGE =
const URL_WHITESPACE_PATTERN = /\s/;
const NON_HTTP_EXPLICIT_SCHEME_PATTERN = /^(?!https?:\/\/)[a-z][a-z0-9+.-]*:\/\//i;
const BrowserToolContextSchema = z.object({
agentId: z.string().optional(),
cwd: z.string().optional(),
workspaceId: z.string().optional(),
browserId: z.string().optional(),
});
const BrowserToolErrorOutputSchema = z.object({
ok: z.literal(false),
error: BrowserAutomationErrorSchema,
context: BrowserToolContextSchema,
});
function browserToolOutputSchema<ResultSchema extends z.ZodType>(resultSchema: ResultSchema) {
return z.discriminatedUnion("ok", [
z.object({
ok: z.literal(true),
result: resultSchema,
context: BrowserToolContextSchema,
}),
BrowserToolErrorOutputSchema,
]);
}
const BrowserToolOutputSchema = {
ok: z.boolean(),
result: z.unknown().optional(),
error: z
.object({
code: z.string(),
message: z.string(),
retryable: z.boolean(),
})
.optional(),
context: z
.object({
agentId: z.string().optional(),
cwd: z.string().optional(),
workspaceId: z.string().optional(),
browserId: z.string().optional(),
})
.optional(),
};
const BrowserHttpUrlInputSchema = z
.string()
@@ -116,37 +81,6 @@ const BrowserWaitInputSchema = z
message: "browser_wait requires exactly one of text or url",
});
const BrowserToolOutputs = {
listTabs: browserToolOutputSchema(BrowserAutomationListTabsResultSchema),
newTab: browserToolOutputSchema(BrowserAutomationNewTabResultSchema),
pageInfo: browserToolOutputSchema(BrowserAutomationPageInfoResultSchema),
snapshot: browserToolOutputSchema(BrowserAutomationSnapshotResultSchema),
click: browserToolOutputSchema(BrowserAutomationClickResultSchema),
fill: browserToolOutputSchema(BrowserAutomationFillResultSchema),
wait: browserToolOutputSchema(BrowserAutomationWaitResultSchema),
type: browserToolOutputSchema(BrowserAutomationTypeResultSchema),
keypress: browserToolOutputSchema(BrowserAutomationKeypressResultSchema),
navigate: browserToolOutputSchema(BrowserAutomationNavigateResultSchema),
back: browserToolOutputSchema(BrowserAutomationBackResultSchema),
forward: browserToolOutputSchema(BrowserAutomationForwardResultSchema),
reload: browserToolOutputSchema(BrowserAutomationReloadResultSchema),
screenshot: browserToolOutputSchema(BrowserAutomationScreenshotResultSchema),
fullPageScreenshot: browserToolOutputSchema(BrowserAutomationFullPageScreenshotResultSchema),
pdf: browserToolOutputSchema(BrowserAutomationPdfResultSchema),
download: browserToolOutputSchema(BrowserAutomationDownloadResultSchema),
upload: browserToolOutputSchema(BrowserAutomationUploadResultSchema),
focus: browserToolOutputSchema(BrowserAutomationFocusResultSchema),
clear: browserToolOutputSchema(BrowserAutomationClearResultSchema),
check: browserToolOutputSchema(BrowserAutomationCheckResultSchema),
select: browserToolOutputSchema(BrowserAutomationSelectResultSchema),
hover: browserToolOutputSchema(BrowserAutomationHoverResultSchema),
drag: browserToolOutputSchema(BrowserAutomationDragResultSchema),
logs: browserToolOutputSchema(BrowserAutomationLogsResultSchema),
storage: browserToolOutputSchema(BrowserAutomationStorageResultSchema),
environment: browserToolOutputSchema(BrowserAutomationEnvironmentResultSchema),
setBackground: browserToolOutputSchema(BrowserAutomationSetBackgroundResultSchema),
} as const;
export function registerBrowserTools(options: RegisterBrowserToolsOptions): void {
options.registerTool(
"browser_list_tabs",
@@ -155,7 +89,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"List open Paseo desktop browser tabs for this agent's workspace. Use returned browserId values with tab-scoped tools.",
inputSchema: {},
outputSchema: BrowserToolOutputs.listTabs,
outputSchema: BrowserToolOutputSchema,
},
async () => {
const context = resolveBrowserToolContext(options);
@@ -185,7 +119,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
inputSchema: {
url: BrowserHttpUrlInputSchema.optional(),
},
outputSchema: BrowserToolOutputs.newTab,
outputSchema: BrowserToolOutputSchema,
},
async ({ url }) => {
const context = resolveBrowserToolContext(options);
@@ -215,7 +149,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
inputSchema: {
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.pageInfo,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -244,7 +178,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
inputSchema: {
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.snapshot,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -274,7 +208,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
ref: BrowserRefInputSchema,
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.click,
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -306,7 +240,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
value: z.string(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.fill,
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, value, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -335,7 +269,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"Wait until a Paseo desktop browser tab contains text or reaches a URL fragment. Use browserId from browser_new_tab or browser_list_tabs; waits up to 5s by default on the desktop side.",
inputSchema: BrowserWaitInputSchema,
outputSchema: BrowserToolOutputs.wait,
outputSchema: BrowserToolOutputSchema,
},
async ({ text, url, timeoutMs, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -370,7 +304,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
ref: BrowserRefInputSchema.optional(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.type,
outputSchema: BrowserToolOutputSchema,
},
async ({ text, ref, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -403,7 +337,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
ref: BrowserRefInputSchema.optional(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.keypress,
outputSchema: BrowserToolOutputSchema,
},
async ({ key, ref, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -432,7 +366,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"Navigate a Paseo desktop browser tab to a URL. Use browserId from browser_new_tab or browser_list_tabs; pass an http(s) URL or a scheme-less host URL, which is treated as http.",
inputSchema: { url: BrowserHttpUrlInputSchema, browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs.navigate,
outputSchema: BrowserToolOutputSchema,
},
async ({ url, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -482,7 +416,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
title: toolConfig.title,
description: toolConfig.description,
inputSchema: { browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs[toolConfig.command],
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -510,7 +444,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"Capture a PNG screenshot of a Paseo desktop browser tab. Use browserId from browser_new_tab or browser_list_tabs.",
inputSchema: { browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs.screenshot,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -537,7 +471,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"Capture a full-page PNG screenshot of a Paseo desktop browser tab. Use browserId from browser_new_tab or browser_list_tabs.",
inputSchema: { browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs.fullPageScreenshot,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -568,7 +502,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
landscape: z.boolean().optional(),
printBackground: z.boolean().default(true),
},
outputSchema: BrowserToolOutputs.pdf,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId, landscape, printBackground }) => {
const context = resolveBrowserToolContext(options);
@@ -601,7 +535,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
fileName: z.string().min(1).optional(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.download,
outputSchema: BrowserToolOutputSchema,
},
async ({ url, fileName, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -634,7 +568,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
filePaths: z.array(z.string().min(1)).min(1),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.upload,
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, filePaths, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -685,7 +619,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
title: toolConfig.title,
description: toolConfig.description,
inputSchema: { ref: BrowserRefInputSchema, browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs[toolConfig.command],
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -718,7 +652,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
checked: z.boolean().default(true),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.check,
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, checked, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -751,7 +685,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
value: z.string(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.select,
outputSchema: BrowserToolOutputSchema,
},
async ({ ref, value, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -784,7 +718,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
targetRef: BrowserRefInputSchema,
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.drag,
outputSchema: BrowserToolOutputSchema,
},
async ({ sourceRef, targetRef, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -816,7 +750,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
maxEntries: z.number().int().positive().max(200).optional(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.logs,
outputSchema: BrowserToolOutputSchema,
},
async ({ maxEntries, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -844,7 +778,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
description:
"Read cookies plus localStorage and sessionStorage for a Paseo desktop browser tab. Use browserId from browser_new_tab or browser_list_tabs.",
inputSchema: { browserId: BrowserAutomationBrowserIdSchema },
outputSchema: BrowserToolOutputs.storage,
outputSchema: BrowserToolOutputSchema,
},
async ({ browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -887,7 +821,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
.optional(),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.environment,
outputSchema: BrowserToolOutputSchema,
},
async ({ viewport, geolocation, browserId }) => {
const context = resolveBrowserToolContext(options);
@@ -919,7 +853,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void
color: z.string().min(1),
browserId: BrowserAutomationBrowserIdSchema,
},
outputSchema: BrowserToolOutputs.setBackground,
outputSchema: BrowserToolOutputSchema,
},
async ({ color, browserId }) => {
const context = resolveBrowserToolContext(options);