Skip to main content
Glama

Chrome MCP Server

postinstall.ts9.05 kB
#!/usr/bin/env node import fs from 'fs'; import os from 'os'; import path from 'path'; import { COMMAND_NAME } from './constant'; import { colorText, tryRegisterUserLevelHost } from './utils'; // Check if this script is run directly const isDirectRun = require.main === module; // Detect global installation for both npm and pnpm function detectGlobalInstall(): boolean { // npm uses npm_config_global if (process.env.npm_config_global === 'true') { return true; } // pnpm detection methods // Method 1: Check if PNPM_HOME is set and current path contains it if (process.env.PNPM_HOME && __dirname.includes(process.env.PNPM_HOME)) { return true; } // Method 2: Check if we're in a global pnpm directory structure // pnpm global packages are typically installed in ~/.local/share/pnpm/global/5/node_modules // Windows: %APPDATA%\pnpm\global\5\node_modules const globalPnpmPatterns = process.platform === 'win32' ? ['\\pnpm\\global\\', '\\pnpm-global\\', '\\AppData\\Roaming\\pnpm\\'] : ['/pnpm/global/', '/.local/share/pnpm/', '/pnpm-global/']; if (globalPnpmPatterns.some((pattern) => __dirname.includes(pattern))) { return true; } // Method 3: Check npm_config_prefix for pnpm if (process.env.npm_config_prefix && __dirname.includes(process.env.npm_config_prefix)) { return true; } // Method 4: Windows-specific global installation paths if (process.platform === 'win32') { const windowsGlobalPatterns = [ '\\npm\\node_modules\\', '\\AppData\\Roaming\\npm\\node_modules\\', '\\Program Files\\nodejs\\node_modules\\', '\\nodejs\\node_modules\\', ]; if (windowsGlobalPatterns.some((pattern) => __dirname.includes(pattern))) { return true; } } return false; } const isGlobalInstall = detectGlobalInstall(); /** * Write Node.js path for run_host scripts to avoid fragile relative paths */ async function writeNodePath(): Promise<void> { try { const nodePath = process.execPath; const nodePathFile = path.join(__dirname, '..', 'node_path.txt'); console.log(colorText(`Writing Node.js path: ${nodePath}`, 'blue')); fs.writeFileSync(nodePathFile, nodePath, 'utf8'); console.log(colorText('✓ Node.js path written for run_host scripts', 'green')); } catch (error: any) { console.warn(colorText(`⚠️ Failed to write Node.js path: ${error.message}`, 'yellow')); } } /** * 确保执行权限(无论是否为全局安装) */ async function ensureExecutionPermissions(): Promise<void> { if (process.platform === 'win32') { // Windows 平台处理 await ensureWindowsFilePermissions(); return; } // Unix/Linux 平台处理 const filesToCheck = [ path.join(__dirname, '..', 'index.js'), path.join(__dirname, '..', 'run_host.sh'), path.join(__dirname, '..', 'cli.js'), ]; for (const filePath of filesToCheck) { if (fs.existsSync(filePath)) { try { fs.chmodSync(filePath, '755'); console.log( colorText(`✓ Set execution permissions for ${path.basename(filePath)}`, 'green'), ); } catch (err: any) { console.warn( colorText( `⚠️ Unable to set execution permissions for ${path.basename(filePath)}: ${err.message}`, 'yellow', ), ); } } else { console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow')); } } } /** * Windows 平台文件权限处理 */ async function ensureWindowsFilePermissions(): Promise<void> { const filesToCheck = [ path.join(__dirname, '..', 'index.js'), path.join(__dirname, '..', 'run_host.bat'), path.join(__dirname, '..', 'cli.js'), ]; for (const filePath of filesToCheck) { if (fs.existsSync(filePath)) { try { // 检查文件是否为只读,如果是则移除只读属性 const stats = fs.statSync(filePath); if (!(stats.mode & parseInt('200', 8))) { // 检查写权限 // 尝试移除只读属性 fs.chmodSync(filePath, stats.mode | parseInt('200', 8)); console.log( colorText(`✓ Removed read-only attribute from ${path.basename(filePath)}`, 'green'), ); } // 验证文件可读性 fs.accessSync(filePath, fs.constants.R_OK); console.log( colorText(`✓ Verified file accessibility for ${path.basename(filePath)}`, 'green'), ); } catch (err: any) { console.warn( colorText( `⚠️ Unable to verify file permissions for ${path.basename(filePath)}: ${err.message}`, 'yellow', ), ); } } else { console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow')); } } } async function tryRegisterNativeHost(): Promise<void> { try { console.log(colorText('Attempting to register Chrome Native Messaging host...', 'blue')); // Always ensure execution permissions, regardless of installation type await ensureExecutionPermissions(); if (isGlobalInstall) { // First try user-level installation (no elevated permissions required) const userLevelSuccess = await tryRegisterUserLevelHost(); if (!userLevelSuccess) { // User-level installation failed, suggest using register command console.log( colorText( 'User-level installation failed, system-level installation may be needed', 'yellow', ), ); console.log( colorText('Please run the following command for system-level installation:', 'blue'), ); console.log(` ${COMMAND_NAME} register --system`); printManualInstructions(); } } else { // Local installation mode, don't attempt automatic registration console.log( colorText('Local installation detected, skipping automatic registration', 'yellow'), ); printManualInstructions(); } } catch (error) { console.log( colorText( `注册过程中出现错误: ${error instanceof Error ? error.message : String(error)}`, 'red', ), ); printManualInstructions(); } } /** * 打印手动安装指南 */ function printManualInstructions(): void { console.log('\n' + colorText('===== Manual Registration Guide =====', 'blue')); console.log(colorText('1. Try user-level installation (recommended):', 'yellow')); if (isGlobalInstall) { console.log(` ${COMMAND_NAME} register`); } else { console.log(` npx ${COMMAND_NAME} register`); } console.log( colorText('\n2. If user-level installation fails, try system-level installation:', 'yellow'), ); console.log(colorText(' Use --system parameter (auto-elevate permissions):', 'yellow')); if (isGlobalInstall) { console.log(` ${COMMAND_NAME} register --system`); } else { console.log(` npx ${COMMAND_NAME} register --system`); } console.log(colorText('\n Or use administrator privileges directly:', 'yellow')); if (os.platform() === 'win32') { console.log( colorText( ' Please run Command Prompt or PowerShell as administrator and execute:', 'yellow', ), ); if (isGlobalInstall) { console.log(` ${COMMAND_NAME} register`); } else { console.log(` npx ${COMMAND_NAME} register`); } } else { console.log(colorText(' Please run the following command in terminal:', 'yellow')); if (isGlobalInstall) { console.log(` sudo ${COMMAND_NAME} register`); } else { console.log(` sudo npx ${COMMAND_NAME} register`); } } console.log( '\n' + colorText( 'Ensure Chrome extension is installed and refresh the extension to connect to local service.', 'blue', ), ); } /** * 主函数 */ async function main(): Promise<void> { console.log(colorText(`Installing ${COMMAND_NAME}...`, 'green')); // Debug information console.log(colorText('Installation environment debug info:', 'blue')); console.log(` __dirname: ${__dirname}`); console.log(` npm_config_global: ${process.env.npm_config_global}`); console.log(` PNPM_HOME: ${process.env.PNPM_HOME}`); console.log(` npm_config_prefix: ${process.env.npm_config_prefix}`); console.log(` isGlobalInstall: ${isGlobalInstall}`); // Always ensure execution permissions first await ensureExecutionPermissions(); // Write Node.js path for run_host scripts to use await writeNodePath(); // If global installation, try automatic registration if (isGlobalInstall) { await tryRegisterNativeHost(); } else { console.log(colorText('Local installation detected', 'yellow')); printManualInstructions(); } } // Only execute main function when running this script directly if (isDirectRun) { main().catch((error) => { console.error( colorText( `Installation script error: ${error instanceof Error ? error.message : String(error)}`, 'red', ), ); }); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/haithemobeidi/mcp-chrome'

If you have feedback or need assistance with the MCP directory API, please join our Discord server