Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 7 additions & 12 deletions src/tools/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@

import {
successResponse,
errorResponse,
jsonResponse,
TOKEN_LIMITS,
truncateText,
previewExcerpt,
truncationFooter,
} from '../utils/response-helpers.js';
import { saveOutput } from '../utils/save-output.js';
import { defineModule, type ToolDefinition } from './module.js';
import { defineModule, defineToolHandler, type ToolDefinition } from './module.js';
import type { McpToolResponse } from '../types/common.js';

export const listConsoleMessagesTool = {
Expand Down Expand Up @@ -91,8 +90,8 @@ function formatMessageLine(msg: {
return `${time}${msg.level.toUpperCase()}${source}: ${msg.text}`;
}

export async function handleListConsoleMessages(args: unknown): Promise<McpToolResponse> {
try {
export const handleListConsoleMessages = defineToolHandler(
async (args: unknown): Promise<McpToolResponse> => {
const {
level,
limit,
Expand Down Expand Up @@ -286,24 +285,20 @@ export async function handleListConsoleMessages(args: unknown): Promise<McpToolR
}

return successResponse(output);
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleClearConsoleMessages(_args: unknown): Promise<McpToolResponse> {
try {
export const handleClearConsoleMessages = defineToolHandler(
async (_args: unknown): Promise<McpToolResponse> => {
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();

const count = (await firefox.getConsoleMessages()).length;
firefox.clearConsoleMessages();

return successResponse(`cleared ${count} messages`);
} catch (error) {
return errorResponse(error as Error);
}
}
);

export const module = defineModule({
name: 'console',
Expand Down
50 changes: 19 additions & 31 deletions src/tools/debugging.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { successResponse, errorResponse } from '../utils/response-helpers.js';
import { compareVersions } from '../utils/version.js';
import { remoteValueToNative } from '../utils/remote-value.js';
import { defineModule, type ToolDefinition } from './module.js';
import { defineModule, defineToolHandler, type ToolDefinition } from './module.js';
import type { McpToolResponse } from '../types/common.js';

const MIN_VERSION = '153';
Expand Down Expand Up @@ -117,20 +117,18 @@ export const getLogpointResultsTool = {
// Handlers
// ============================================================================

export async function handleEnableDebugger(_args: unknown): Promise<McpToolResponse> {
try {
export const handleEnableDebugger = defineToolHandler(
async (_args: unknown): Promise<McpToolResponse> => {
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
requireDebuggingSupport(firefox);
await firefox.sendBiDiCommand('moz:debugging.setDebuggerEnabled', { enabled: true });
return successResponse('Debugger enabled');
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleListScripts(_args: unknown): Promise<McpToolResponse> {
try {
export const handleListScripts = defineToolHandler(
async (_args: unknown): Promise<McpToolResponse> => {
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
requireDebuggingSupport(firefox);
Expand All @@ -143,13 +141,11 @@ export async function handleListScripts(_args: unknown): Promise<McpToolResponse
return successResponse('No scripts found');
}
return successResponse(scripts.join('\n'));
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleGetScriptSource(args: unknown): Promise<McpToolResponse> {
try {
export const handleGetScriptSource = defineToolHandler(
async (args: unknown): Promise<McpToolResponse> => {
const { scriptUrl } = args as { scriptUrl: string };
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
Expand All @@ -160,39 +156,33 @@ export async function handleGetScriptSource(args: unknown): Promise<McpToolRespo
scriptUrl,
});
return successResponse((result as { source: string }).source);
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleSetLogpoint(args: unknown): Promise<McpToolResponse> {
try {
export const handleSetLogpoint = defineToolHandler(
async (args: unknown): Promise<McpToolResponse> => {
const { url, line, expression } = args as { url: string; line: number; expression: string };
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
requireDebuggingSupport(firefox);
const logpointId = await firefox.setLogpoint(url, line, expression);
return successResponse(`Logpoint set (id: ${logpointId})`);
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleRemoveLogpoint(args: unknown): Promise<McpToolResponse> {
try {
export const handleRemoveLogpoint = defineToolHandler(
async (args: unknown): Promise<McpToolResponse> => {
const { logpoint } = args as { logpoint: string };
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
requireDebuggingSupport(firefox);
await firefox.removeLogpoint(logpoint);
return successResponse('Logpoint removed');
} catch (error) {
return errorResponse(error as Error);
}
}
);

export async function handleGetLogpointResults(args: unknown): Promise<McpToolResponse> {
try {
export const handleGetLogpointResults = defineToolHandler(
async (args: unknown): Promise<McpToolResponse> => {
const { logpoint } = args as { logpoint: string };
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
Expand All @@ -211,10 +201,8 @@ export async function handleGetLogpointResults(args: unknown): Promise<McpToolRe
return `[${i + 1}] ${JSON.stringify(remoteValueToNative(r.value))}`;
});
return successResponse(lines.join('\n'));
} catch (error) {
return errorResponse(error as Error);
}
}
);

export const module = defineModule({
name: 'debugging',
Expand Down
128 changes: 61 additions & 67 deletions src/tools/downloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { successResponse, errorResponse, jsonResponse } from '../utils/response-helpers.js';
import { defineModule, type ToolDefinition } from './module.js';
import { defineModule, defineToolHandler, type ToolDefinition } from './module.js';
import type { McpToolResponse } from '../types/common.js';

// Tool definitions
Expand Down Expand Up @@ -67,84 +67,78 @@ export const setDownloadBehaviorTool = {
} satisfies ToolDefinition;

// Tool handlers
export async function handleListDownloads(args: unknown): Promise<McpToolResponse> {
try {
const {
status,
urlContains,
limit = 50,
format = 'text',
} = (args ?? {}) as {
status?: string;
urlContains?: string;
limit?: number;
format?: string;
};
export const handleListDownloads = defineToolHandler(async function handleListDownloads(
args: unknown
): Promise<McpToolResponse> {
const {
status,
urlContains,
limit = 50,
format = 'text',
} = (args ?? {}) as {
status?: string;
urlContains?: string;
limit?: number;
format?: string;
};

const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
let downloads = firefox.getDownloads();

if (status) {
downloads = downloads.filter((d) => d.status === status);
}
if (urlContains) {
const needle = urlContains.toLowerCase();
downloads = downloads.filter((d) => (d.url || '').toLowerCase().includes(needle));
}

const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
let downloads = firefox.getDownloads();

if (status) {
downloads = downloads.filter((d) => d.status === status);
}
if (urlContains) {
const needle = urlContains.toLowerCase();
downloads = downloads.filter((d) => (d.url || '').toLowerCase().includes(needle));
}

downloads = downloads
.sort((a, b) => (b.startTimestamp || 0) - (a.startTimestamp || 0))
.slice(0, limit);

if (format === 'json') {
return jsonResponse(downloads);
}

if (downloads.length === 0) {
return successResponse('No downloads tracked.');
}

const lines = downloads.map((d) => {
const where = d.filepath ? ` -> ${d.filepath}` : '';
return `[${d.status}] ${d.suggestedFilename || d.url}${where}`;
});
return successResponse(lines.join('\n'));
} catch (error) {
return errorResponse(error instanceof Error ? error.message : String(error));
downloads = downloads
.sort((a, b) => (b.startTimestamp || 0) - (a.startTimestamp || 0))
.slice(0, limit);

if (format === 'json') {
return jsonResponse(downloads);
}

if (downloads.length === 0) {
return successResponse('No downloads tracked.');
}
}

export async function handleClearDownloads(): Promise<McpToolResponse> {
try {
const lines = downloads.map((d) => {
const where = d.filepath ? ` -> ${d.filepath}` : '';
return `[${d.status}] ${d.suggestedFilename || d.url}${where}`;
});
return successResponse(lines.join('\n'));
});

export const handleClearDownloads = defineToolHandler(
async function handleClearDownloads(): Promise<McpToolResponse> {
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
firefox.clearDownloads();
return successResponse('Downloads cleared.');
} catch (error) {
return errorResponse(error instanceof Error ? error.message : String(error));
}
}
);

export async function handleSetDownloadBehavior(args: unknown): Promise<McpToolResponse> {
try {
const { behavior } = (args ?? {}) as {
behavior?: 'allowed' | 'denied' | 'default';
};
export const handleSetDownloadBehavior = defineToolHandler(async function handleSetDownloadBehavior(
args: unknown
): Promise<McpToolResponse> {
const { behavior } = (args ?? {}) as {
behavior?: 'allowed' | 'denied' | 'default';
};

if (!behavior) {
return errorResponse('behavior is required');
}
if (!behavior) {
return errorResponse('behavior is required');
}

const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
await firefox.setDownloadBehavior(behavior);
const { getFirefox } = await import('../index.js');
const firefox = await getFirefox();
await firefox.setDownloadBehavior(behavior);

return successResponse(`Download behavior set to '${behavior}'.`);
} catch (error) {
return errorResponse(error instanceof Error ? error.message : String(error));
}
}
return successResponse(`Download behavior set to '${behavior}'.`);
});

export const module = defineModule({
name: 'downloads',
Expand Down
Loading