feat: add console tool & support mcp stdio type

This commit is contained in:
hangerye
2025-06-22 21:45:17 +08:00
parent cb9a3665ec
commit d25a0fbecb
16 changed files with 610 additions and 89 deletions
+9 -3
View File
@@ -61,9 +61,11 @@ npm install -g mcp-chrome-bridge
pnpm
```bash
pnpm install -g mcp-chrome-bridge
pnpm install -g mcp-chrome-bridge --unsafe-perm
```
> Note: When using pnpm, the `--unsafe-perm` flag is required to ensure post-installation scripts run properly, which is necessary for registering the Native Messaging host.
3. **Load Chrome Extension**
- Open Chrome and go to `chrome://extensions/`
- Enable "Developer mode"
@@ -91,12 +93,14 @@ Add the following configuration to Claude Desktop's MCP configuration:
Complete tool list: [Complete Tool List](docs/TOOLS.md)
<details>
<summary><strong>📊 Browser Management (4 tools)</strong></summary>
<summary><strong>📊 Browser Management (6 tools)</strong></summary>
- `get_windows_and_tabs` - List all browser windows and tabs
- `chrome_navigate` - Navigate to URLs and control viewport
- `chrome_close_tabs` - Close specific tabs or windows
- `chrome_go_back_or_forward` - Browser navigation control
- `chrome_inject_script` - Inject content scripts into web pages
- `chrome_send_command_to_inject_script` - Send commands to injected content scripts
</details>
<details>
@@ -114,11 +118,12 @@ Complete tool list: [Complete Tool List](docs/TOOLS.md)
</details>
<details>
<summary><strong>🔍 Content Analysis (3 tools)</strong></summary>
<summary><strong>🔍 Content Analysis (4 tools)</strong></summary>
- `search_tabs_content` - AI-powered semantic search across browser tabs
- `chrome_get_web_content` - Extract HTML/text content from pages
- `chrome_get_interactive_elements` - Find clickable elements
- `chrome_console` - Capture and retrieve console output from browser tabs
</details>
<details>
@@ -148,6 +153,7 @@ Instruction: Help me summarize the current page content, then draw a diagram to
https://github.com/user-attachments/assets/fd17209b-303d-48db-9e5e-3717141df183
### After analyzing the content of the image, the LLM automatically controls Excalidraw to replicate the image
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)|[content-analize](prompt/content-analize.md)
Instruction: First, analyze the content of the image, and then replicate the image by combining the analysis with the content of the image.
+8 -4
View File
@@ -61,15 +61,17 @@ npm install -g mcp-chrome-bridge
pnpm
```bash
pnpm install -g mcp-chrome-bridge
pnpm install -g mcp-chrome-bridge --unsafe-perm
```
> 注意:使用 pnpm 安装时需要添加 `--unsafe-perm` 参数以确保安装后脚本能正常执行,这对于注册 Native Messaging 主机是必要的。
3. **加载 Chrome 扩展**
- 打开 Chrome 并访问 `chrome://extensions/`
- 启用"开发者模式"
- 点击"加载已解压的扩展程序",选择 `your/dowloaded/extension/folder`
- 点击插件图标打开插件,点击连接即可看到mcp的配置
<img width="475" alt="截屏2025-06-09 15 52 06" src="https://github.com/user-attachments/assets/241e57b8-c55f-41a4-9188-0367293dc5bc" />
<img width="475" alt="截屏2025-06-09 15 52 06" src="https://github.com/user-attachments/assets/241e57b8-c55f-41a4-9188-0367293dc5bc" />
### 在 Claude Desktop 中使用
@@ -91,7 +93,7 @@ pnpm install -g mcp-chrome-bridge
完整工具列表:[完整工具列表](docs/TOOLS_zh.md)
<details>
<summary><strong>📊 浏览器管理 (4个工具)</strong></summary>
<summary><strong>📊 浏览器管理 (6个工具)</strong></summary>
- `get_windows_and_tabs` - 列出所有浏览器窗口和标签页
- `chrome_navigate` - 导航到 URL 并控制视口
@@ -116,11 +118,12 @@ pnpm install -g mcp-chrome-bridge
</details>
<details>
<summary><strong>🔍 内容分析 (3个工具)</strong></summary>
<summary><strong>🔍 内容分析 (4个工具)</strong></summary>
- `search_tabs_content` - AI 驱动的浏览器标签页语义搜索
- `chrome_get_web_content` - 从页面提取 HTML/文本内容
- `chrome_get_interactive_elements` - 查找可点击元素
- `chrome_console` - 捕获和获取浏览器标签页的控制台输出
</details>
<details>
@@ -150,6 +153,7 @@ prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)
https://github.com/user-attachments/assets/f14f79a6-9390-4821-8296-06d020bcfc07
### ai先分析图片的内容元素,然后再自动控制excalidraw把图片模仿出来
prompt: [excalidraw-prompt](prompt/excalidraw-prompt.md)|[content-analize](prompt/content-analize.md)
指令:先看下图片是否能用excalidraw画出来,如果则列出所需的步骤和元素,然后画出来
@@ -0,0 +1,343 @@
import { createErrorResponse, ToolResult } from '@/common/tool-handler';
import { BaseBrowserToolExecutor } from '../base-browser';
import { TOOL_NAMES } from 'chrome-mcp-shared';
const DEBUGGER_PROTOCOL_VERSION = '1.3';
const DEFAULT_MAX_MESSAGES = 100;
interface ConsoleToolParams {
url?: string;
includeExceptions?: boolean;
maxMessages?: number;
}
interface ConsoleMessage {
timestamp: number;
level: string;
text: string;
args?: any[];
source?: string;
url?: string;
lineNumber?: number;
stackTrace?: any;
}
interface ConsoleException {
timestamp: number;
text: string;
url?: string;
lineNumber?: number;
columnNumber?: number;
stackTrace?: any;
}
interface ConsoleResult {
success: boolean;
message: string;
tabId: number;
tabUrl: string;
tabTitle: string;
captureStartTime: number;
captureEndTime: number;
totalDurationMs: number;
messages: ConsoleMessage[];
exceptions: ConsoleException[];
messageCount: number;
exceptionCount: number;
messageLimitReached: boolean;
}
/**
* Tool for capturing console output from browser tabs
*/
class ConsoleTool extends BaseBrowserToolExecutor {
name = TOOL_NAMES.BROWSER.CONSOLE;
async execute(args: ConsoleToolParams): Promise<ToolResult> {
const { url, includeExceptions = true, maxMessages = DEFAULT_MAX_MESSAGES } = args;
let targetTab: chrome.tabs.Tab;
try {
if (url) {
// Navigate to the specified URL
targetTab = await this.navigateToUrl(url);
} else {
// Use current active tab
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!activeTab?.id) {
return createErrorResponse('No active tab found and no URL provided.');
}
targetTab = activeTab;
}
if (!targetTab?.id) {
return createErrorResponse('Failed to identify target tab.');
}
const tabId = targetTab.id;
// Capture console messages (one-time capture)
const result = await this.captureConsoleMessages(tabId, {
includeExceptions,
maxMessages,
});
return {
content: [
{
type: 'text',
text: JSON.stringify(result),
},
],
isError: false,
};
} catch (error: any) {
console.error('ConsoleTool: Critical error during execute:', error);
return createErrorResponse(`Error in ConsoleTool: ${error.message || String(error)}`);
}
}
private async navigateToUrl(url: string): Promise<chrome.tabs.Tab> {
// Check if URL is already open
const existingTabs = await chrome.tabs.query({ url });
if (existingTabs.length > 0 && existingTabs[0]?.id) {
const tab = existingTabs[0];
// Activate the existing tab
await chrome.tabs.update(tab.id!, { active: true });
await chrome.windows.update(tab.windowId, { focused: true });
return tab;
} else {
// Create new tab with the URL
const newTab = await chrome.tabs.create({ url, active: true });
// Wait for tab to be ready
await this.waitForTabReady(newTab.id!);
return newTab;
}
}
private async waitForTabReady(tabId: number): Promise<void> {
return new Promise((resolve) => {
const checkTab = async () => {
try {
const tab = await chrome.tabs.get(tabId);
if (tab.status === 'complete') {
resolve();
} else {
setTimeout(checkTab, 100);
}
} catch (error) {
// Tab might be closed, resolve anyway
resolve();
}
};
checkTab();
});
}
private formatConsoleArgs(args: any[]): string {
if (!args || args.length === 0) return '';
return args
.map((arg) => {
if (arg.type === 'string') {
return arg.value || '';
} else if (arg.type === 'number') {
return String(arg.value || '');
} else if (arg.type === 'boolean') {
return String(arg.value || '');
} else if (arg.type === 'object') {
return arg.description || '[Object]';
} else if (arg.type === 'undefined') {
return 'undefined';
} else if (arg.type === 'function') {
return arg.description || '[Function]';
} else {
return arg.description || arg.value || String(arg);
}
})
.join(' ');
}
private async captureConsoleMessages(
tabId: number,
options: {
includeExceptions: boolean;
maxMessages: number;
},
): Promise<ConsoleResult> {
const { includeExceptions, maxMessages } = options;
const startTime = Date.now();
const messages: ConsoleMessage[] = [];
const exceptions: ConsoleException[] = [];
let limitReached = false;
try {
// Get tab information
const tab = await chrome.tabs.get(tabId);
// Check if debugger is already attached
const targets = await chrome.debugger.getTargets();
const existingTarget = targets.find(
(t) => t.tabId === tabId && t.attached && t.type === 'page',
);
if (existingTarget && !existingTarget.extensionId) {
throw new Error(
`Debugger is already attached to tab ${tabId} by another tool (e.g., DevTools).`,
);
}
// Attach debugger
try {
await chrome.debugger.attach({ tabId }, DEBUGGER_PROTOCOL_VERSION);
} catch (error: any) {
if (error.message?.includes('Cannot attach to the target with an attached client')) {
throw new Error(
`Debugger is already attached to tab ${tabId}. This might be DevTools or another extension.`,
);
}
throw error;
}
// Set up event listener to collect messages
const collectedMessages: any[] = [];
const collectedExceptions: any[] = [];
const eventListener = (source: chrome.debugger.Debuggee, method: string, params?: any) => {
if (source.tabId !== tabId) return;
if (method === 'Log.entryAdded' && params?.entry) {
collectedMessages.push(params.entry);
} else if (method === 'Runtime.consoleAPICalled' && params) {
// Convert Runtime.consoleAPICalled to Log.entryAdded format
const logEntry = {
timestamp: params.timestamp,
level: params.type || 'log',
text: this.formatConsoleArgs(params.args || []),
source: 'console-api',
url: params.stackTrace?.callFrames?.[0]?.url,
lineNumber: params.stackTrace?.callFrames?.[0]?.lineNumber,
stackTrace: params.stackTrace,
args: params.args,
};
collectedMessages.push(logEntry);
} else if (
method === 'Runtime.exceptionThrown' &&
includeExceptions &&
params?.exceptionDetails
) {
collectedExceptions.push(params.exceptionDetails);
}
};
chrome.debugger.onEvent.addListener(eventListener);
try {
// Enable Runtime domain first to capture console API calls and exceptions
await chrome.debugger.sendCommand({ tabId }, 'Runtime.enable');
// Also enable Log domain to capture other log entries
await chrome.debugger.sendCommand({ tabId }, 'Log.enable');
// Wait for all messages to be flushed
await new Promise((resolve) => setTimeout(resolve, 2000));
// Process collected messages
for (const entry of collectedMessages) {
if (messages.length >= maxMessages) {
limitReached = true;
break;
}
const message: ConsoleMessage = {
timestamp: entry.timestamp,
level: entry.level || 'log',
text: entry.text || '',
source: entry.source,
url: entry.url,
lineNumber: entry.lineNumber,
};
if (entry.stackTrace) {
message.stackTrace = entry.stackTrace;
}
if (entry.args && Array.isArray(entry.args)) {
message.args = entry.args;
}
messages.push(message);
}
// Process collected exceptions
for (const exceptionDetails of collectedExceptions) {
const exception: ConsoleException = {
timestamp: Date.now(),
text:
exceptionDetails.text ||
exceptionDetails.exception?.description ||
'Unknown exception',
url: exceptionDetails.url,
lineNumber: exceptionDetails.lineNumber,
columnNumber: exceptionDetails.columnNumber,
};
if (exceptionDetails.stackTrace) {
exception.stackTrace = exceptionDetails.stackTrace;
}
exceptions.push(exception);
}
} finally {
// Clean up
chrome.debugger.onEvent.removeListener(eventListener);
try {
await chrome.debugger.sendCommand({ tabId }, 'Runtime.disable');
} catch (e) {
console.warn(`ConsoleTool: Error disabling Runtime for tab ${tabId}:`, e);
}
try {
await chrome.debugger.sendCommand({ tabId }, 'Log.disable');
} catch (e) {
console.warn(`ConsoleTool: Error disabling Log for tab ${tabId}:`, e);
}
try {
await chrome.debugger.detach({ tabId });
} catch (e) {
console.warn(`ConsoleTool: Error detaching debugger for tab ${tabId}:`, e);
}
}
const endTime = Date.now();
// Sort messages by timestamp
messages.sort((a, b) => a.timestamp - b.timestamp);
exceptions.sort((a, b) => a.timestamp - b.timestamp);
return {
success: true,
message: `Console capture completed for tab ${tabId}. ${messages.length} messages, ${exceptions.length} exceptions captured.`,
tabId,
tabUrl: tab.url || '',
tabTitle: tab.title || '',
captureStartTime: startTime,
captureEndTime: endTime,
totalDurationMs: endTime - startTime,
messages,
exceptions,
messageCount: messages.length,
exceptionCount: exceptions.length,
messageLimitReached: limitReached,
};
} catch (error: any) {
console.error(`ConsoleTool: Error capturing console messages for tab ${tabId}:`, error);
throw error;
}
}
}
export const consoleTool = new ConsoleTool();
@@ -11,3 +11,4 @@ export { keyboardTool } from './keyboard';
export { historyTool } from './history';
export { bookmarkSearchTool, bookmarkAddTool, bookmarkDeleteTool } from './bookmark';
export { injectScriptTool, sendCommandToInjectScriptTool } from './inject-script';
export { consoleTool } from './console';
@@ -51,15 +51,4 @@ class WindowTool extends BaseBrowserToolExecutor {
}
}
interface TabContentResult {
tabId: number;
url: string;
title: string;
textContent?: string;
error?: string;
matchScore?: number;
semanticScore?: number;
matchedSnippets?: string[];
}
export const windowTool = new WindowTool();
+1 -1
View File
@@ -3,7 +3,7 @@
"description": "a chrome extension to use your own chrome as a mcp server",
"author": "hangye",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"type": "module",
"scripts": {
"dev": "wxt",
+5 -5
View File
@@ -1,10 +1,11 @@
{
"name": "mcp-chrome-bridge",
"version": "1.0.15",
"version": "1.0.23",
"description": "Chrome Native-Messaging host (Node)",
"main": "dist/index.js",
"bin": {
"mcp-chrome-bridge": "/dist/cli.js"
"mcp-chrome-bridge": "./dist/cli.js",
"mcp-chrome-stdio": "./dist/mcp/mcp-server-stdio.js"
},
"scripts": {
"dev": "nodemon --watch src --ext ts,js,json --ignore dist/ --exec \"npm run build && npm run register:dev\"",
@@ -35,13 +36,12 @@
"@fastify/cors": "^11.0.1",
"@modelcontextprotocol/sdk": "^1.11.0",
"chalk": "^5.4.1",
"chrome-mcp-shared": "workspace:*",
"commander": "^13.1.0",
"fastify": "^5.3.2",
"is-admin": "^4.0.0",
"pino": "^9.6.0",
"sudo-prompt": "^9.2.1",
"uuid": "^11.1.0",
"chrome-mcp-shared": "workspace:*"
"uuid": "^11.1.0"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
+38
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env node
import { program } from 'commander';
import * as fs from 'fs';
import * as path from 'path';
import {
tryRegisterUserLevelHost,
colorText,
@@ -95,6 +97,42 @@ program
}
});
// Update port in stdio-config.json
program
.command('update-port <port>')
.description('Update the port number in stdio-config.json')
.action(async (port: string) => {
try {
const portNumber = parseInt(port, 10);
if (isNaN(portNumber) || portNumber < 1 || portNumber > 65535) {
console.error(colorText('Error: Port must be a valid number between 1 and 65535', 'red'));
process.exit(1);
}
const configPath = path.join(__dirname, 'mcp', 'stdio-config.json');
if (!fs.existsSync(configPath)) {
console.error(colorText(`Error: Configuration file not found at ${configPath}`, 'red'));
process.exit(1);
}
const configData = fs.readFileSync(configPath, 'utf8');
const config = JSON.parse(configData);
const currentUrl = new URL(config.url);
currentUrl.port = portNumber.toString();
config.url = currentUrl.toString();
fs.writeFileSync(configPath, JSON.stringify(config, null, 4));
console.log(colorText(`✓ Port updated successfully to ${portNumber}`, 'green'));
console.log(colorText(`Updated URL: ${config.url}`, 'blue'));
} catch (error: any) {
console.error(colorText(`Failed to update port: ${error.message}`, 'red'));
process.exit(1);
}
});
program.parse(process.argv);
// If no command provided, show help
@@ -0,0 +1,114 @@
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import {
CallToolRequestSchema,
CallToolResult,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { TOOL_SCHEMAS } from 'chrome-mcp-shared';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import * as fs from 'fs';
import * as path from 'path';
let stdioMcpServer: Server | null = null;
let mcpClient: Client | null = null;
// Read configuration from stdio-config.json
const loadConfig = () => {
try {
const configPath = path.join(__dirname, 'stdio-config.json');
const configData = fs.readFileSync(configPath, 'utf8');
return JSON.parse(configData);
} catch (error) {
console.error('Failed to load stdio-config.json:', error);
throw new Error('Configuration file stdio-config.json not found or invalid');
}
};
export const getStdioMcpServer = () => {
if (stdioMcpServer) {
return stdioMcpServer;
}
stdioMcpServer = new Server(
{
name: 'StdioChromeMcpServer',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
},
);
setupTools(stdioMcpServer);
return stdioMcpServer;
};
export const ensureMcpClient = async () => {
try {
if (mcpClient) {
const pingResult = await mcpClient.ping();
if (pingResult) {
return mcpClient;
}
}
const config = loadConfig();
mcpClient = new Client({ name: 'Mcp Chrome Proxy', version: '1.0.0' }, { capabilities: {} });
const transport = new StreamableHTTPClientTransport(new URL(config.url), {});
await mcpClient.connect(transport);
return mcpClient;
} catch (error) {
mcpClient?.close();
mcpClient = null;
console.error('Failed to connect to MCP server:', error);
}
};
export const setupTools = (server: Server) => {
// List tools handler
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOL_SCHEMAS }));
// Call tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) =>
handleToolCall(request.params.name, request.params.arguments || {}),
);
};
const handleToolCall = async (name: string, args: any): Promise<CallToolResult> => {
try {
const client = await ensureMcpClient();
if (!client) {
throw new Error('Failed to connect to MCP server');
}
const result = await client.callTool({ name, arguments: args }, undefined, {
timeout: 2 * 6 * 1000, // Default timeout of 2 minute
});
return result as CallToolResult;
} catch (error: any) {
return {
content: [
{
type: 'text',
text: `Error calling tool: ${error.message}`,
},
],
isError: true,
};
}
};
async function main() {
const transport = new StdioServerTransport();
await getStdioMcpServer().connect(transport);
console.log('🚀 Chrome MCP Server running on type stdio');
}
main().catch((error) => {
console.error('Fatal error Chrome MCP Server main():', error);
process.exit(1);
});
@@ -0,0 +1,3 @@
{
"url": "http://127.0.0.1:12306/mcp"
}
+19 -16
View File
@@ -21,26 +21,29 @@ console.log('dist 和 dist/logs 目录已创建/确认存在');
console.log('编译TypeScript...');
execSync('tsc', { stdio: 'inherit' });
// 复制配置文件
console.log('复制配置文件...');
const configSourcePath = path.join(__dirname, '..', 'mcp', 'stdio-config.json');
const configDestPath = path.join(distDir, 'mcp', 'stdio-config.json');
try {
// 确保目标目录存在
fs.mkdirSync(path.dirname(configDestPath), { recursive: true });
if (fs.existsSync(configSourcePath)) {
fs.copyFileSync(configSourcePath, configDestPath);
console.log(`已将 stdio-config.json 复制到 ${configDestPath}`);
} else {
console.error(`错误: 配置文件未找到: ${configSourcePath}`);
}
} catch (error) {
console.error('复制配置文件时出错:', error);
}
// 复制package.json并更新其内容
console.log('准备package.json...');
const packageJson = require('../../package.json');
// 删除开发依赖和脚本
const distPackageJson = {
name: packageJson.name,
version: packageJson.version,
description: packageJson.description,
author: packageJson.author,
license: packageJson.license,
main: 'index.js',
dependencies: packageJson.dependencies,
scripts: {
start: 'node index.js',
},
};
fs.writeFileSync(path.join(distDir, 'package.json'), JSON.stringify(distPackageJson, null, 2));
// 创建安装说明
const readmeContent = `# ${packageJson.name}
+43 -39
View File
@@ -284,16 +284,6 @@ export async function tryRegisterUserLevelHost(): Promise<boolean> {
}
}
// 使用sudo-prompt提权
let sudoPrompt: any;
try {
sudoPrompt = require('sudo-prompt');
} catch (error) {
console.error('缺少sudo-prompt依赖,请先安装:npm install sudo-prompt');
console.error(error);
process.exit(1);
}
// 导入is-admin包(仅在Windows平台使用)
let isAdmin: () => boolean = () => false;
if (process.platform === 'win32') {
@@ -360,20 +350,31 @@ export async function registerWithElevatedPermissions(): Promise<void> {
throw error;
}
} else {
// 没有管理员权限,使用sudo-prompt提权
await new Promise((resolve, reject) => {
sudoPrompt.exec(command, { name: `${COMMAND_NAME} Installer` }, (error: Error) => {
if (error) {
console.error(
colorText(`Elevated permission installation failed: ${error.message}`, 'red'),
);
reject(error);
} else {
console.log(colorText('System-level manifest registration successful!', 'green'));
resolve(true);
}
});
});
// 没有管理员权限,打印手动操作提示
console.log(
colorText('⚠️ Administrator privileges required for system-level installation', 'yellow'),
);
console.log(
colorText(
'Please run one of the following commands with administrator privileges:',
'blue',
),
);
if (os.platform() === 'win32') {
console.log(colorText(' 1. Open Command Prompt as Administrator and run:', 'blue'));
console.log(colorText(` ${command}`, 'cyan'));
} else {
console.log(colorText(' 1. Run with sudo:', 'blue'));
console.log(colorText(` sudo ${command}`, 'cyan'));
}
console.log(
colorText(' 2. Or run the registration command with elevated privileges:', 'blue'),
);
console.log(colorText(` sudo ${COMMAND_NAME} register --system`, 'cyan'));
throw new Error('Administrator privileges required for system-level installation');
}
// 6. Windows特殊处理 - 设置系统级注册表
@@ -405,21 +406,24 @@ export async function registerWithElevatedPermissions(): Promise<void> {
throw error;
}
} else {
// 没有管理员权限,使用sudo-prompt提权
await new Promise<void>((resolve, reject) => {
sudoPrompt.exec(regCommand, { name: `${COMMAND_NAME} Installer` }, (error: Error) => {
if (error) {
console.error(
colorText(`Windows registry entry creation failed: ${error.message}`, 'red'),
);
console.error(colorText(`Command: ${regCommand}`, 'red'));
reject(error);
} else {
console.log(colorText('Windows registry entry created successfully!', 'green'));
resolve();
}
});
});
// 没有管理员权限,打印手动操作提示
console.log(
colorText(
'⚠️ Administrator privileges required for Windows registry modification',
'yellow',
),
);
console.log(colorText('Please run the following command as Administrator:', 'blue'));
console.log(colorText(` ${regCommand}`, 'cyan'));
console.log(colorText('Or run the registration command with elevated privileges:', 'blue'));
console.log(
colorText(
` Run Command Prompt as Administrator and execute: ${COMMAND_NAME} register --system`,
'cyan',
),
);
throw new Error('Administrator privileges required for Windows registry modification');
}
}
} catch (error: any) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "chrome-mcp-shared",
"version": "1.0.0",
"version": "1.0.1",
"author": "hangye",
"main": "dist/index.js",
"module": "./dist/index.mjs",
+25
View File
@@ -24,6 +24,7 @@ export const TOOL_NAMES = {
BOOKMARK_DELETE: 'chrome_bookmark_delete',
INJECT_SCRIPT: 'chrome_inject_script',
SEND_COMMAND_TO_INJECT_SCRIPT: 'chrome_send_command_to_inject_script',
CONSOLE: 'chrome_console',
},
};
@@ -509,4 +510,28 @@ export const TOOL_SCHEMAS: Tool[] = [
required: ['eventName'],
},
},
{
name: TOOL_NAMES.BROWSER.CONSOLE,
description:
'Capture and retrieve all console output from the current active browser tab/page. This captures console messages that existed before the tool was called.',
inputSchema: {
type: 'object',
properties: {
url: {
type: 'string',
description:
'URL to navigate to and capture console from. If not provided, uses the current active tab',
},
includeExceptions: {
type: 'boolean',
description: 'Include uncaught exceptions in the output (default: true)',
},
maxMessages: {
type: 'number',
description: 'Maximum number of console messages to capture (default: 100)',
},
},
required: [],
},
},
];
-9
View File
@@ -123,9 +123,6 @@ importers:
pino:
specifier: ^9.6.0
version: 9.7.0
sudo-prompt:
specifier: ^9.2.1
version: 9.2.1
uuid:
specifier: ^11.1.0
version: 11.1.0
@@ -4040,10 +4037,6 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
sudo-prompt@9.2.1:
resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
superagent@10.2.1:
resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==}
engines: {node: '>=14.18.0'}
@@ -8855,8 +8848,6 @@ snapshots:
pirates: 4.0.7
ts-interface-checker: 0.1.13
sudo-prompt@9.2.1: {}
superagent@10.2.1:
dependencies:
component-emitter: 1.3.1