Sign In

@mearl/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mearl/mcp-server - npm Package Compare versions

Comparing version
2.2.1
to
2.2.2
+11
-1
dist/configure.d.ts

@@ -8,5 +8,15 @@ #!/usr/bin/env node

* mearl-mcp-configure --extension-id=YOUR_ID # 直接指定自定义 Extension ID
* mearl-mcp-configure --yes # 配置所有已检测客户端,不询问
* mearl-mcp-configure --skip-mcp # 跳过 MCP 配置
* mearl-mcp-configure --get # 查看当前配置
*/
export {};
export interface ConfigureArgs {
extensionId?: string;
get: boolean;
globalInstall: boolean;
skipMcp: boolean;
yes: boolean;
}
export declare function parseConfigureArgs(args?: string[]): ConfigureArgs;
export declare function shouldUseNpx(globalInstall: boolean, detectNpxMode?: () => boolean): boolean;
export declare function runConfigure(argv?: string[]): Promise<void>;
+98
-53

@@ -8,2 +8,3 @@ #!/usr/bin/env node

* mearl-mcp-configure --extension-id=YOUR_ID # 直接指定自定义 Extension ID
* mearl-mcp-configure --yes # 配置所有已检测客户端,不询问
* mearl-mcp-configure --skip-mcp # 跳过 MCP 配置

@@ -14,3 +15,4 @@ * mearl-mcp-configure --get # 查看当前配置

import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import path, { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { HOST_NAME, DEFAULT_EXTENSION_ID } from './constants.js';

@@ -24,7 +26,8 @@ import prompts from 'prompts';

// 解析命令行参数
function parseArgs() {
const args = process.argv.slice(2);
export function parseConfigureArgs(args = process.argv.slice(2)) {
let extensionId;
let skipMcp = false;
let get = false;
let globalInstall = false;
let yes = false;
for (const arg of args) {

@@ -43,4 +46,10 @@ if (arg.startsWith('--extension-id=')) {

}
else if (arg === '--global-install') {
globalInstall = true;
}
else if (arg === '--yes' || arg === '-y' || arg === '--non-interactive') {
yes = true;
}
}
return { extensionId, skipMcp, get };
return { extensionId, skipMcp, get, globalInstall, yes };
}

@@ -205,6 +214,7 @@ // 替换或追加 TOML 中的 mcp_servers.xxx 节

}
async function main() {
const parseResult = parseArgs();
const { skipMcp, get } = parseResult;
const extensionId = parseResult.extensionId;
export function shouldUseNpx(globalInstall, detectNpxMode = isNpxMode) {
return !globalInstall && detectNpxMode();
}
export async function runConfigure(argv = process.argv.slice(2)) {
const { extensionId, get, globalInstall, skipMcp, yes } = parseConfigureArgs(argv);
// 如果是查看配置,显示后退出

@@ -217,6 +227,9 @@ if (get) {

// 检测 npx 模式
const useNpx = isNpxMode();
const useNpx = shouldUseNpx(globalInstall);
if (useNpx) {
log('检测到 npx 模式,将生成 npx 配置');
}
else if (globalInstall) {
log('使用全局 mearl-mcp-server 命令生成配置');
}
// 配置 Native Messaging Host

@@ -226,3 +239,3 @@ console.log('\n📦 配置 Chrome Native Messaging Host...');

// the same time (they share the same Unix socket and handlers).
await installNativeHost({
const nativeHostInstalled = await installNativeHost({
mode: 'both',

@@ -232,2 +245,5 @@ extensionId: extensionId || process.env.MEARL_EXTENSION_ID || DEFAULT_EXTENSION_ID,

});
if (!nativeHostInstalled) {
throw new Error('Native Messaging Host 配置失败');
}
console.log('✅ 配置完成\n');

@@ -239,49 +255,72 @@ // 配置 MCP 客户端

else {
const response = await prompts({
type: 'confirm',
name: 'setupMcp',
message: '配置 MCP 客户端?',
initial: true,
// 列出全部支持的客户端,已检测到安装的默认预选
const allClients = Object.entries(MCP_CLIENTS).map(([key, client]) => {
const selected = isClientInstalled(key);
return {
title: selected ? `${client.name} ✓` : client.name,
value: key,
selected,
};
});
if (response.setupMcp) {
// 列出全部支持的客户端,已检测到安装的默认预选
const allClients = Object.entries(MCP_CLIENTS).map(([key, client]) => ({
title: isClientInstalled(key) ? `${client.name} ✓` : client.name,
value: key,
selected: isClientInstalled(key),
}));
const detectedNames = allClients.filter(c => c.selected).map(c => c.title);
if (detectedNames.length > 0) {
console.log(`\n已检测到: ${detectedNames.join(', ')}\n`);
const detectedClients = allClients.filter(client => client.selected);
const detectedNames = detectedClients.map(client => client.title);
let selectedClients;
if (yes) {
if (detectedClients.length === 0) {
throw new Error('未检测到可配置的 MCP 客户端;请交互式运行 mearl-mcp-configure 进行选择');
}
else {
console.log('\n未检测到已安装的客户端,可手动选择\n');
selectedClients = detectedClients.map(client => client.value);
console.log(`\n将配置已检测到的客户端: ${detectedNames.join(', ')}\n`);
}
else {
const response = await prompts({
type: 'confirm',
name: 'setupMcp',
message: '配置 MCP 客户端?',
initial: true,
});
if (response.setupMcp === undefined) {
throw new Error('MCP 客户端配置已取消');
}
const clientResponse = await prompts({
type: 'multiselect',
name: 'clients',
message: '选择要配置的客户端:',
choices: allClients,
instructions: false,
hint: '空格选择,回车确认',
});
if (clientResponse.clients && clientResponse.clients.length > 0) {
console.log('');
for (const clientKey of clientResponse.clients) {
const client = MCP_CLIENTS[clientKey];
if (updateMCPConfig(client.configPath, client, useNpx)) {
console.log(`✅ ${client.name}`);
}
else {
console.log(`❌ ${client.name} 配置失败`);
}
if (response.setupMcp) {
if (detectedNames.length > 0) {
console.log(`\n已检测到: ${detectedNames.join(', ')}\n`);
}
console.log('');
else {
console.log('\n未检测到已安装的客户端,可手动选择\n');
}
const clientResponse = await prompts({
type: 'multiselect',
name: 'clients',
message: '选择要配置的客户端:',
choices: allClients,
instructions: false,
hint: '空格选择,回车确认',
});
selectedClients = clientResponse.clients;
if (!selectedClients || selectedClients.length === 0) {
throw new Error('未选择要配置的 MCP 客户端');
}
}
else {
console.log('已取消\n');
console.log('跳过 MCP 客户端配置\n');
}
}
else {
console.log('跳过 MCP 客户端配置\n');
if (selectedClients && selectedClients.length > 0) {
console.log('');
let configured = true;
for (const clientKey of selectedClients) {
const client = MCP_CLIENTS[clientKey];
if (updateMCPConfig(client.configPath, client, useNpx)) {
console.log(`✅ ${client.name}`);
}
else {
configured = false;
console.log(`❌ ${client.name} 配置失败`);
}
}
console.log('');
if (!configured) {
throw new Error('部分 MCP 客户端配置失败');
}
}

@@ -295,5 +334,11 @@ }

}
main().catch(error => {
console.error('Configuration failed:', error);
process.exit(1);
});
const currentFile = fileURLToPath(import.meta.url);
if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) {
try {
await runConfigure();
}
catch (error) {
console.error('Configuration failed:', error);
process.exitCode = 1;
}
}

@@ -9,2 +9,29 @@ #!/usr/bin/env node

*/
export {};
import { existsSync } from 'fs';
import { MCPClientConfig } from './mcp-clients.js';
import { uninstallNativeHost } from '@mearl/native-host';
export type MCPConfigCleanupResult = 'removed' | 'not-found' | 'failed';
export declare function cleanMCPConfig(configPath: string, clientConfig: MCPClientConfig): MCPConfigCleanupResult;
export interface MCPUninstallDependencies {
cleanConfig?: typeof cleanMCPConfig;
clients?: Record<string, MCPClientConfig>;
exists?: typeof existsSync;
globalInstallationDetected?: () => boolean;
log?: (message: string) => void;
logError?: (message: string) => void;
prompt?: (options: {
type: 'multiselect';
name: 'clients';
message: string;
choices: Array<{
title: string;
value: string;
}>;
instructions: boolean;
hint: string;
}) => Promise<{
clients?: string[];
}>;
removeNativeHost?: typeof uninstallNativeHost;
}
export declare function runUninstall(argv?: string[], dependencies?: MCPUninstallDependencies): Promise<boolean>;

@@ -9,25 +9,10 @@ #!/usr/bin/env node

*/
import { unlinkSync, existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { homedir, platform } from 'os';
import { execSync } from 'child_process';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { platform } from 'os';
import { execFileSync } from 'child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import prompts from 'prompts';
import { HOST_NAME } from './constants.js';
import { MCP_CLIENTS } from './mcp-clients.js';
// 获取 Native Messaging Host 配置目录
function getNativeHostDir() {
const os = platform();
if (os === 'darwin') {
return join(homedir(), 'Library/Application Support/Google/Chrome/NativeMessagingHosts');
}
else if (os === 'linux') {
return join(homedir(), '.config/google-chrome/NativeMessagingHosts');
}
else if (os === 'win32') {
throw new Error('Windows 需要手动删除注册表项,请参考 README.md');
}
else {
throw new Error(`不支持的平台: ${os}`);
}
}
import { uninstallNativeHost } from '@mearl/native-host';
// 检查是否全局安装

@@ -37,3 +22,3 @@ function isGloballyInstalled() {

const cmd = platform() === 'win32' ? 'where' : 'which';
execSync(`${cmd} mearl-mcp-server`, { encoding: 'utf-8', stdio: 'pipe' });
execFileSync(cmd, ['mearl-mcp-server'], { encoding: 'utf-8', stdio: 'pipe' });
return true;

@@ -45,9 +30,30 @@ }

}
function removeTomlMCPSection(content) {
const lines = content.split('\n');
const sectionStart = lines.findIndex(line => line.trim() === '[mcp_servers.mearl]');
if (sectionStart === -1)
return null;
let sectionEnd = lines.length;
for (let index = sectionStart + 1; index < lines.length; index += 1) {
if (lines[index].trim().startsWith('[')) {
sectionEnd = index;
break;
}
}
return [...lines.slice(0, sectionStart), ...lines.slice(sectionEnd)].join('\n');
}
// 清理 MCP 配置文件中的 mearl
function cleanMCPConfig(configPath, clientConfig) {
export function cleanMCPConfig(configPath, clientConfig) {
try {
if (!existsSync(configPath)) {
return false;
return 'not-found';
}
const content = readFileSync(configPath, 'utf-8');
if (clientConfig.useTomlFormat) {
const updated = removeTomlMCPSection(content);
if (updated === null)
return 'not-found';
writeFileSync(configPath, updated);
return 'removed';
}
const config = JSON.parse(content);

@@ -71,48 +77,48 @@ const useServersFormat = clientConfig.useServersFormat;

if (!hasConfig) {
return false;
return 'not-found';
}
// 写回文件
writeFileSync(configPath, JSON.stringify(config, null, 2));
return true;
return 'removed';
}
catch (error) {
console.error(` ⚠️ 清理配置失败: ${error instanceof Error ? error.message : String(error)}`);
return false;
return 'failed';
}
}
// 卸载主函数
async function uninstall() {
console.log('🗑️ Mearl MCP 卸载\n');
export async function runUninstall(argv = process.argv.slice(2), dependencies = {}) {
const log = dependencies.log ?? (message => console.log(message));
const logError = dependencies.logError ?? (message => console.error(message));
const removeNativeHost = dependencies.removeNativeHost ?? uninstallNativeHost;
const detectGlobalInstallation = dependencies.globalInstallationDetected ?? isGloballyInstalled;
const fileExists = dependencies.exists ?? existsSync;
const clients = dependencies.clients ?? MCP_CLIENTS;
const prompt = dependencies.prompt ?? prompts;
const cleanConfig = dependencies.cleanConfig ?? cleanMCPConfig;
const managedBySetup = argv.includes('--managed-by-setup');
const yes = argv.includes('--yes') || argv.includes('-y');
log('🗑️ Mearl MCP 卸载\n');
try {
let completed = true;
// 1. 清理 Native Messaging Host 配置
console.log('清理 Native Messaging Host...');
const hostDir = getNativeHostDir();
const manifestPath = join(hostDir, `${HOST_NAME}.json`);
const wrapperPath = join(hostDir, `${HOST_NAME}-launcher.sh`);
let hasDeleted = false;
if (existsSync(manifestPath)) {
unlinkSync(manifestPath);
console.log(`✅ 删除配置文件`);
hasDeleted = true;
}
if (existsSync(wrapperPath)) {
unlinkSync(wrapperPath);
hasDeleted = true;
}
if (!hasDeleted) {
console.log('未找到配置文件');
}
log('清理 Native Messaging Host...');
const nativeHostRemoved = await removeNativeHost({ mode: 'mcp', silent: true });
log(nativeHostRemoved ? '✅ Native Messaging Host 已清理' : '⚠️ Native Messaging Host 清理失败');
completed = nativeHostRemoved;
// 2. 检查全局安装
console.log('\n检查 npm 全局安装...');
if (isGloballyInstalled()) {
console.log('⚠️ 请手动卸载: npm uninstall -g @mearl/mcp-server');
if (!managedBySetup) {
log('\n检查 npm 全局安装...');
if (detectGlobalInstallation()) {
log('⚠️ 请手动卸载: npm uninstall -g @mearl/mcp-server');
}
else {
log('✅ 未检测到全局安装');
}
}
else {
console.log('✅ 未检测到全局安装');
}
// 3. 清理 MCP 客户端配置
console.log('\n清理 MCP 客户端配置...');
log('\n清理 MCP 客户端配置...');
// 检测哪些客户端有配置文件
const availableClients = Object.entries(MCP_CLIENTS)
.filter(([_, client]) => existsSync(client.configPath))
const availableClients = Object.entries(clients)
.filter(([_, client]) => fileExists(client.configPath))
.map(([key, client]) => ({

@@ -123,21 +129,27 @@ title: client.name,

if (availableClients.length === 0) {
console.log('未找到 MCP 配置');
log('未找到 MCP 配置');
}
else {
const response = await prompts({
type: 'multiselect',
name: 'clients',
message: '选择要清理的客户端:',
choices: availableClients,
instructions: false,
hint: '空格选择,回车确认',
});
if (response.clients && response.clients.length > 0) {
for (const clientKey of response.clients) {
const client = MCP_CLIENTS[clientKey];
if (cleanMCPConfig(client.configPath, client)) {
console.log(`✅ 已清理 ${client.name}`);
const selectedClients = yes
? availableClients.map(client => client.value)
: (await prompt({
type: 'multiselect',
name: 'clients',
message: '选择要清理的客户端:',
choices: availableClients,
instructions: false,
hint: '空格选择,回车确认',
})).clients;
if (selectedClients && selectedClients.length > 0) {
for (const clientKey of selectedClients) {
const client = clients[clientKey];
const result = cleanConfig(client.configPath, client);
if (result === 'removed') {
log(`✅ 已清理 ${client.name}`);
}
else if (result === 'not-found') {
log(`未找到 ${client.name} 配置`);
}
else {
console.log(`未找到 ${client.name} 配置`);
completed = false;
}

@@ -147,12 +159,21 @@ }

else {
console.log('已取消');
log('已取消');
}
}
console.log('\n✅ 卸载完成!');
if (completed) {
log('\n✅ 卸载完成!');
}
else {
logError('\n❌ 卸载未完整完成,请检查上述错误');
}
return completed;
}
catch (error) {
console.error('\n❌ 卸载失败:', error instanceof Error ? error.message : String(error));
process.exit(1);
logError(`\n❌ 卸载失败: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
uninstall();
const currentFile = fileURLToPath(import.meta.url);
if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) {
process.exitCode = (await runUninstall()) ? 0 : 1;
}
{
"name": "@mearl/mcp-server",
"version": "2.2.1",
"version": "2.2.2",
"description": "MCP Server for Mearl - enables AI to interact with Chrome browser requests and logs",

@@ -33,4 +33,4 @@ "mcpName": "io.github.F-loat/mearl",

"prompts": "^2.4.2",
"@mearl/client": "2.2.1",
"@mearl/native-host": "2.2.1"
"@mearl/client": "2.2.2",
"@mearl/native-host": "2.2.2"
},

@@ -47,4 +47,5 @@ "devDependencies": {

"setup": "pnpm build && npm link",
"test": "vitest run",
"typecheck": "tsc --noEmit"
}
}