chore: merge tabs tool into one (#933)

This commit is contained in:
Pavel Feldman
2025-08-22 13:46:52 -07:00
committed by GitHub
parent fb28e99fa4
commit 2521a67b2f
3 changed files with 55 additions and 103 deletions

View File

@@ -627,39 +627,14 @@ http.createServer(async (req, res) => {
<!-- NOTE: This has been generated via update-readme.js --> <!-- NOTE: This has been generated via update-readme.js -->
- **browser_tab_close** - **browser_tabs**
- Title: Close a tab - Title: Manage tabs
- Description: Close a tab - Description: List, create, close, or select a browser tab.
- Parameters: - Parameters:
- `index` (number, optional): The index of the tab to close. Closes current tab if not provided. - `action` (string): Operation to perform
- `index` (number, optional): Tab index, used for close/select. If omitted for close, current tab is closed.
- Read-only: **false** - Read-only: **false**
<!-- NOTE: This has been generated via update-readme.js -->
- **browser_tab_list**
- Title: List tabs
- Description: List browser tabs
- Parameters: None
- Read-only: **true**
<!-- NOTE: This has been generated via update-readme.js -->
- **browser_tab_new**
- Title: Open a new tab
- Description: Open a new tab
- Parameters:
- `url` (string, optional): The URL to navigate to in the new tab. If not provided, the new tab will be blank.
- Read-only: **true**
<!-- NOTE: This has been generated via update-readme.js -->
- **browser_tab_select**
- Title: Select a tab
- Description: Select a tab by index
- Parameters:
- `index` (number): The index of the tab to select
- Read-only: **true**
</details> </details>
<details> <details>

View File

@@ -17,85 +17,48 @@
import { z } from 'zod'; import { z } from 'zod';
import { defineTool } from './tool.js'; import { defineTool } from './tool.js';
const listTabs = defineTool({ const browserTabs = defineTool({
capability: 'core-tabs', capability: 'core-tabs',
schema: { schema: {
name: 'browser_tab_list', name: 'browser_tabs',
title: 'List tabs', title: 'Manage tabs',
description: 'List browser tabs', description: 'List, create, close, or select a browser tab.',
inputSchema: z.object({}),
type: 'readOnly',
},
handle: async (context, params, response) => {
await context.ensureTab();
response.setIncludeTabs();
},
});
const selectTab = defineTool({
capability: 'core-tabs',
schema: {
name: 'browser_tab_select',
title: 'Select a tab',
description: 'Select a tab by index',
inputSchema: z.object({ inputSchema: z.object({
index: z.number().describe('The index of the tab to select'), action: z.enum(['list', 'new', 'close', 'select']).describe('Operation to perform'),
}), index: z.number().optional().describe('Tab index, used for close/select. If omitted for close, current tab is closed.'),
type: 'readOnly',
},
handle: async (context, params, response) => {
await context.selectTab(params.index);
response.setIncludeSnapshot();
},
});
const newTab = defineTool({
capability: 'core-tabs',
schema: {
name: 'browser_tab_new',
title: 'Open a new tab',
description: 'Open a new tab',
inputSchema: z.object({
url: z.string().optional().describe('The URL to navigate to in the new tab. If not provided, the new tab will be blank.'),
}),
type: 'readOnly',
},
handle: async (context, params, response) => {
const tab = await context.newTab();
if (params.url)
await tab.navigate(params.url);
response.setIncludeSnapshot();
},
});
const closeTab = defineTool({
capability: 'core-tabs',
schema: {
name: 'browser_tab_close',
title: 'Close a tab',
description: 'Close a tab',
inputSchema: z.object({
index: z.number().optional().describe('The index of the tab to close. Closes current tab if not provided.'),
}), }),
type: 'destructive', type: 'destructive',
}, },
handle: async (context, params, response) => { handle: async (context, params, response) => {
await context.closeTab(params.index); switch (params.action) {
response.setIncludeSnapshot(); case 'list': {
await context.ensureTab();
response.setIncludeTabs();
return;
}
case 'new': {
await context.newTab();
response.setIncludeTabs();
return;
}
case 'close': {
await context.closeTab(params.index);
response.setIncludeSnapshot();
return;
}
case 'select': {
if (!params.index)
throw new Error('Tab index is required');
await context.selectTab(params.index);
response.setIncludeSnapshot();
return;
}
}
}, },
}); });
export default [ export default [
listTabs, browserTabs,
newTab,
selectTab,
closeTab,
]; ];

View File

@@ -19,8 +19,14 @@ import { test, expect } from './fixtures.js';
import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
async function createTab(client: Client, title: string, body: string) { async function createTab(client: Client, title: string, body: string) {
await client.callTool({
name: 'browser_tabs',
arguments: {
action: 'new',
},
});
return await client.callTool({ return await client.callTool({
name: 'browser_tab_new', name: 'browser_navigate',
arguments: { arguments: {
url: `data:text/html,<title>${title}</title><body>${body}</body>`, url: `data:text/html,<title>${title}</title><body>${body}</body>`,
}, },
@@ -29,7 +35,10 @@ async function createTab(client: Client, title: string, body: string) {
test('list initial tabs', async ({ client }) => { test('list initial tabs', async ({ client }) => {
expect(await client.callTool({ expect(await client.callTool({
name: 'browser_tab_list', name: 'browser_tabs',
arguments: {
action: 'list',
},
})).toHaveResponse({ })).toHaveResponse({
tabs: `- 0: (current) [] (about:blank)`, tabs: `- 0: (current) [] (about:blank)`,
}); });
@@ -38,7 +47,10 @@ test('list initial tabs', async ({ client }) => {
test('list first tab', async ({ client }) => { test('list first tab', async ({ client }) => {
await createTab(client, 'Tab one', 'Body one'); await createTab(client, 'Tab one', 'Body one');
expect(await client.callTool({ expect(await client.callTool({
name: 'browser_tab_list', name: 'browser_tabs',
arguments: {
action: 'list',
},
})).toHaveResponse({ })).toHaveResponse({
tabs: `- 0: [] (about:blank) tabs: `- 0: [] (about:blank)
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`, - 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`,
@@ -75,8 +87,9 @@ test('select tab', async ({ client }) => {
await createTab(client, 'Tab two', 'Body two'); await createTab(client, 'Tab two', 'Body two');
expect(await client.callTool({ expect(await client.callTool({
name: 'browser_tab_select', name: 'browser_tabs',
arguments: { arguments: {
action: 'select',
index: 1, index: 1,
}, },
})).toHaveResponse({ })).toHaveResponse({
@@ -97,8 +110,9 @@ test('close tab', async ({ client }) => {
await createTab(client, 'Tab two', 'Body two'); await createTab(client, 'Tab two', 'Body two');
expect(await client.callTool({ expect(await client.callTool({
name: 'browser_tab_close', name: 'browser_tabs',
arguments: { arguments: {
action: 'close',
index: 2, index: 2,
}, },
})).toHaveResponse({ })).toHaveResponse({