Merge feat/cookie-storage: add cookie and storage management tools

This commit is contained in:
Ryan Malloy 2026-01-18 17:30:37 -07:00
commit d15521c474
2 changed files with 350 additions and 0 deletions

View File

@ -31,6 +31,7 @@ import pdf from './tools/pdf.js';
import sensors from './tools/sensors.js'; import sensors from './tools/sensors.js';
import requests from './tools/requests.js'; import requests from './tools/requests.js';
import snapshot from './tools/snapshot.js'; import snapshot from './tools/snapshot.js';
import storage from './tools/storage.js';
import tabs from './tools/tabs.js'; import tabs from './tools/tabs.js';
import screenshot from './tools/screenshot.js'; import screenshot from './tools/screenshot.js';
import themeManagement from './tools/themeManagement.js'; import themeManagement from './tools/themeManagement.js';
@ -62,6 +63,7 @@ export const allTools: Tool<any>[] = [
...screenshot, ...screenshot,
...sensors, ...sensors,
...snapshot, ...snapshot,
...storage,
...tabs, ...tabs,
...themeManagement, ...themeManagement,
...video, ...video,

348
src/tools/storage.ts Normal file
View File

@ -0,0 +1,348 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import { defineTool, defineTabTool } from './tool.js';
/**
* Get all cookies for the current browser context.
* Uses browserContext.cookies() to retrieve cookie data.
*/
const getCookies = defineTool({
capability: 'core',
schema: {
name: 'browser_get_cookies',
title: 'Get browser cookies',
description: 'List all cookies for the current browser context. Optionally filter by domain or URL.',
inputSchema: z.object({
urls: z.array(z.string()).optional().describe('Filter cookies by specific URLs. If not provided, returns all cookies.'),
}),
type: 'readOnly',
},
handle: async (context, params, response) => {
const browserContext = await context.existingBrowserContext();
if (!browserContext)
throw new Error('No browser context available. Navigate to a page first.');
const cookies = params.urls && params.urls.length > 0
? await browserContext.cookies(params.urls)
: await browserContext.cookies();
if (cookies.length === 0) {
response.addResult('No cookies found.');
return;
}
const result = ['### Browser Cookies', ''];
for (const cookie of cookies) {
result.push(`- **${cookie.name}**`);
result.push(` - Value: \`${cookie.value.substring(0, 100)}${cookie.value.length > 100 ? '...' : ''}\``);
result.push(` - Domain: ${cookie.domain}`);
result.push(` - Path: ${cookie.path}`);
result.push(` - Expires: ${cookie.expires === -1 ? 'Session' : new Date(cookie.expires * 1000).toISOString()}`);
result.push(` - HttpOnly: ${cookie.httpOnly}`);
result.push(` - Secure: ${cookie.secure}`);
result.push(` - SameSite: ${cookie.sameSite}`);
result.push('');
}
result.push(`**Total:** ${cookies.length} cookie(s)`);
response.addResult(result.join('\n'));
},
});
/**
* Set a cookie in the browser context.
* Uses browserContext.addCookies() to add cookie data.
*/
const setCookie = defineTool({
capability: 'core',
schema: {
name: 'browser_set_cookie',
title: 'Set a browser cookie',
description: 'Set a cookie with specified name, value, and optional attributes. Requires either url or domain+path.',
inputSchema: z.object({
name: z.string().describe('Cookie name'),
value: z.string().describe('Cookie value'),
url: z.string().optional().describe('URL to associate with the cookie. Either url or domain must be specified.'),
domain: z.string().optional().describe('Cookie domain. Either url or domain must be specified.'),
path: z.string().optional().describe('Cookie path (default: "/")'),
expires: z.number().optional().describe('Unix timestamp in seconds for cookie expiration. -1 for session cookie.'),
httpOnly: z.boolean().optional().describe('Whether the cookie is HTTP only (default: false)'),
secure: z.boolean().optional().describe('Whether the cookie is secure (default: false)'),
sameSite: z.enum(['Strict', 'Lax', 'None']).optional().describe('SameSite attribute (default: "Lax")'),
}),
type: 'destructive',
},
handle: async (context, params, response) => {
const browserContext = await context.existingBrowserContext();
if (!browserContext)
throw new Error('No browser context available. Navigate to a page first.');
if (!params.url && !params.domain)
throw new Error('Either "url" or "domain" must be specified.');
const cookie: {
name: string;
value: string;
url?: string;
domain?: string;
path?: string;
expires?: number;
httpOnly?: boolean;
secure?: boolean;
sameSite?: 'Strict' | 'Lax' | 'None';
} = {
name: params.name,
value: params.value,
};
if (params.url)
cookie.url = params.url;
if (params.domain)
cookie.domain = params.domain;
if (params.path)
cookie.path = params.path;
if (params.expires !== undefined)
cookie.expires = params.expires;
if (params.httpOnly !== undefined)
cookie.httpOnly = params.httpOnly;
if (params.secure !== undefined)
cookie.secure = params.secure;
if (params.sameSite)
cookie.sameSite = params.sameSite;
await browserContext.addCookies([cookie]);
const location = params.url || `${params.domain}${params.path || '/'}`;
response.addResult(`Cookie "${params.name}" set successfully for ${location}`);
},
});
/**
* Delete cookies from the browser context.
* Uses browserContext.clearCookies() with optional filters.
*/
const deleteCookies = defineTool({
capability: 'core',
schema: {
name: 'browser_delete_cookies',
title: 'Delete browser cookies',
description: 'Delete cookies by name, domain, or path. If no filters provided, clears all cookies.',
inputSchema: z.object({
name: z.string().optional().describe('Delete cookies with this name'),
domain: z.string().optional().describe('Delete cookies for this domain'),
path: z.string().optional().describe('Delete cookies with this path'),
}),
type: 'destructive',
},
handle: async (context, params, response) => {
const browserContext = await context.existingBrowserContext();
if (!browserContext)
throw new Error('No browser context available. Navigate to a page first.');
const hasFilters = params.name || params.domain || params.path;
if (hasFilters) {
// Build filter object for clearCookies
const filter: { name?: string; domain?: string; path?: string } = {};
if (params.name)
filter.name = params.name;
if (params.domain)
filter.domain = params.domain;
if (params.path)
filter.path = params.path;
await browserContext.clearCookies(filter);
const filterParts: string[] = [];
if (params.name)
filterParts.push(`name="${params.name}"`);
if (params.domain)
filterParts.push(`domain="${params.domain}"`);
if (params.path)
filterParts.push(`path="${params.path}"`);
response.addResult(`Cookies deleted matching: ${filterParts.join(', ')}`);
} else {
await browserContext.clearCookies();
response.addResult('All cookies have been cleared.');
}
},
});
/**
* Get localStorage or sessionStorage contents.
* Uses page.evaluate() to access web storage APIs.
*/
const getStorage = defineTabTool({
capability: 'core',
schema: {
name: 'browser_get_storage',
title: 'Get web storage contents',
description: 'Get all key-value pairs from localStorage or sessionStorage for the current page.',
inputSchema: z.object({
type: z.enum(['local', 'session']).describe('Storage type: "local" for localStorage, "session" for sessionStorage'),
key: z.string().optional().describe('Get a specific key value instead of all items'),
}),
type: 'readOnly',
},
handle: async (tab, params, response) => {
const storageType = params.type === 'local' ? 'localStorage' : 'sessionStorage';
if (params.key) {
// Get a specific key
const value = await tab.page.evaluate(
([type, key]) => {
const storage = type === 'local' ? localStorage : sessionStorage;
return storage.getItem(key);
},
[params.type, params.key] as const
);
if (value === null) {
response.addResult(`Key "${params.key}" not found in ${storageType}.`);
} else {
response.addResult(`**${storageType}["${params.key}"]:**\n\`\`\`\n${value}\n\`\`\``);
}
return;
}
// Get all items
const items = await tab.page.evaluate(
(type) => {
const storage = type === 'local' ? localStorage : sessionStorage;
const result: Record<string, string> = {};
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
if (key !== null)
result[key] = storage.getItem(key) || '';
}
return result;
},
params.type
);
const keys = Object.keys(items);
if (keys.length === 0) {
response.addResult(`${storageType} is empty.`);
return;
}
const result = [`### ${storageType} Contents`, ''];
for (const key of keys) {
const value = items[key];
const truncatedValue = value.length > 200 ? value.substring(0, 200) + '...' : value;
result.push(`- **${key}:**`);
result.push(` \`\`\`${truncatedValue}\`\`\``);
result.push('');
}
result.push(`**Total:** ${keys.length} item(s)`);
response.addResult(result.join('\n'));
},
});
/**
* Set a key-value pair in localStorage or sessionStorage.
* Uses page.evaluate() to access web storage APIs.
*/
const setStorage = defineTabTool({
capability: 'core',
schema: {
name: 'browser_set_storage',
title: 'Set web storage item',
description: 'Set a key-value pair in localStorage or sessionStorage for the current page.',
inputSchema: z.object({
type: z.enum(['local', 'session']).describe('Storage type: "local" for localStorage, "session" for sessionStorage'),
key: z.string().describe('Storage key'),
value: z.string().describe('Storage value (will be stored as string)'),
}),
type: 'destructive',
},
handle: async (tab, params, response) => {
const storageType = params.type === 'local' ? 'localStorage' : 'sessionStorage';
await tab.page.evaluate(
([type, key, value]) => {
const storage = type === 'local' ? localStorage : sessionStorage;
storage.setItem(key, value);
},
[params.type, params.key, params.value] as const
);
response.addResult(`Set ${storageType}["${params.key}"] successfully.`);
},
});
/**
* Clear localStorage, sessionStorage, or both.
* Uses page.evaluate() to access web storage APIs.
*/
const clearStorage = defineTabTool({
capability: 'core',
schema: {
name: 'browser_clear_storage',
title: 'Clear web storage',
description: 'Clear localStorage, sessionStorage, or both for the current page.',
inputSchema: z.object({
type: z.enum(['local', 'session', 'both']).describe('Storage type to clear: "local", "session", or "both"'),
}),
type: 'destructive',
},
handle: async (tab, params, response) => {
const clearLocal = params.type === 'local' || params.type === 'both';
const clearSession = params.type === 'session' || params.type === 'both';
await tab.page.evaluate(
([doLocal, doSession]) => {
if (doLocal)
localStorage.clear();
if (doSession)
sessionStorage.clear();
},
[clearLocal, clearSession] as const
);
if (params.type === 'both') {
response.addResult('Both localStorage and sessionStorage have been cleared.');
} else {
const storageType = params.type === 'local' ? 'localStorage' : 'sessionStorage';
response.addResult(`${storageType} has been cleared.`);
}
},
});
export default [
getCookies,
setCookie,
deleteCookies,
getStorage,
setStorage,
clearStorage,
];