mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
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:
@@ -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({
|
||||
|
||||
@@ -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 })),
|
||||
);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user