import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { waitForSelectorSchema, waitForNavigationSchema, waitSchema } from '../schemas.js';
import { getPageForOperation } from '../tabs.js';
import { getDefaultTimeout } from '../browser.js';
import {
handleResult,
ok,
err,
selectorNotFound,
operationTimeout,
normalizeError,
} from '../errors.js';
import type { WaitUntilOption } from '../types.js';
/**
* Register waiting tools
*/
export function registerWaitingTools(server: McpServer): void {
// Wait for selector
server.tool(
'wait_for_selector',
'Wait for an element matching the selector to appear in the page',
waitForSelectorSchema.shape,
async ({ selector, visible, hidden, timeout, tabId }) => {
const pageResult = await getPageForOperation(tabId);
if (!pageResult.success) {
return handleResult(pageResult);
}
const page = pageResult.data;
const timeoutMs = timeout ?? getDefaultTimeout();
try {
const element = await page.waitForSelector(selector, {
timeout: timeoutMs,
visible: visible ?? false,
hidden: hidden ?? false,
});
if (!element) {
return handleResult(err(selectorNotFound(selector)));
}
return handleResult(ok({
found: true,
selector,
}));
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('waiting for selector')) {
return handleResult(err(selectorNotFound(selector)));
}
if (error.message.includes('timeout')) {
return handleResult(err(operationTimeout('wait_for_selector', timeoutMs)));
}
}
return handleResult(err(normalizeError(error)));
}
}
);
// Wait for navigation
server.tool(
'wait_for_navigation',
'Wait for the page to navigate to a new URL',
waitForNavigationSchema.shape,
async ({ waitUntil, timeout, tabId }) => {
const pageResult = await getPageForOperation(tabId);
if (!pageResult.success) {
return handleResult(pageResult);
}
const page = pageResult.data;
const timeoutMs = timeout ?? getDefaultTimeout();
try {
await page.waitForNavigation({
waitUntil: (waitUntil ?? 'load') as WaitUntilOption,
timeout: timeoutMs,
});
return handleResult(ok({
url: page.url(),
title: await page.title(),
}));
} catch (error) {
if (error instanceof Error && error.message.includes('timeout')) {
return handleResult(err(operationTimeout('wait_for_navigation', timeoutMs)));
}
return handleResult(err(normalizeError(error)));
}
}
);
// Wait for timeout
server.tool(
'wait',
'Wait for a specified number of milliseconds',
waitSchema.shape,
async ({ ms }) => {
await new Promise((resolve) => setTimeout(resolve, ms));
return handleResult(ok({ waited: ms }));
}
);
}