前言
Claude 桌面应用能够通过 Chrome 扩展实现浏览器自动化,这背后采用的是 Chrome 的 Native Messaging 机制。本文将深入剖析这一通信机制的技术实现,包括配置文件、通信协议和完整代码示例。
一、技术架构概览
1.1 核心组件
- Chrome 扩展:运行在浏览器中的前端组件
- Native Messaging Host:桌面应用提供的本地可执行程序
- 配置文件:连接两者的桥梁
1.2 通信流程图
┌─────────────────┐ ┌──────────────────────┐
│ Chrome 扩展 │ │ Claude 桌面应用 │
│ │ │ │
│ background.js │ │ chrome-native-host │
└────────┬────────┘ └──────────┬───────────┘
│ │
│ 1. 读取配置文件 │
│ NativeMessagingHosts/*.json │
├──────────────────────────────────>│
│ │
│ 2. connectNative() │
│ 建立连接 │
├──────────────────────────────────>│
│ │
│ 3. 建立 stdio 管道通信 │
│<──────────────────────────────────┤
│ │
│ 4. postMessage() │
│ 发送指令 │
├──────────────────────────────────>│
│ │
│ 5. 执行浏览器操作 │
│ (AppleScript/CDP) │
│ │
│ 6. 返回执行结果 │
│<──────────────────────────────────┤
│ │
二、Native Messaging Host 配置文件
2.1 配置文件位置
macOS:
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/
Windows:
HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\
Linux:
~/.config/google-chrome/NativeMessagingHosts/
2.2 配置文件内容
文件名:com.anthropic.claude_browser_extension.json
json
{
"name": "com.anthropic.claude_browser_extension",
"description": "Claude Desktop Browser Extension Host",
"path": "/Applications/Claude.app/Contents/Helpers/chrome-native-host",
"type": "stdio",
"allowed_origins": [
"chrome-extension://fcoeoabgfenejglbffodgkkbkcdhcgfn/"
]
}
2.3 关键字段解释
| 字段 | 说明 |
|---|---|
name |
Native Host 的唯一标识符 |
path |
可执行程序的绝对路径 |
type |
通信类型,固定为 "stdio" |
allowed_origins |
允许连接的扩展 ID 列表 |
三、Chrome 扩展端实现
3.1 建立连接
javascript
// background.js 或 service worker
let nativePort = null;
/**
* 连接到 Native Host
*/
function connectToNativeHost() {
// 使用配置文件中的 name 字段连接
nativePort = chrome.runtime.connectNative('com.anthropic.claude_browser_extension');
// 监听来自桌面应用的消息
nativePort.onMessage.addListener((message) => {
console.log("收到桌面应用消息:", message);
handleDesktopMessage(message);
});
// 监听连接断开事件
nativePort.onDisconnect.addListener(() => {
console.log("与桌面应用断开连接");
if (chrome.runtime.lastError) {
console.error("连接错误:", chrome.runtime.lastError.message);
}
nativePort = null;
// 可选:自动重连
setTimeout(connectToNativeHost, 5000);
});
console.log("已连接到 Claude 桌面应用");
}
/**
* 向桌面应用发送消息
*/
function sendToDesktop(data) {
if (nativePort) {
nativePort.postMessage(data);
} else {
console.error("未连接到桌面应用");
}
}
/**
* 处理来自桌面应用的消息
*/
function handleDesktopMessage(message) {
switch (message.type) {
case 'navigate':
chrome.tabs.update(message.tabId, { url: message.url });
break;
case 'execute_script':
chrome.tabs.executeScript(message.tabId, { code: message.code });
break;
case 'get_content':
chrome.tabs.sendMessage(message.tabId, { action: 'getContent' });
break;
default:
console.warn("未知消息类型:", message.type);
}
}
// 初始化连接
connectToNativeHost();
3.2 消息格式示例
发送到桌面应用:
javascript
sendToDesktop({
type: "browser_action",
action: "navigate",
url: "https://example.com",
tabId: 123
});
sendToDesktop({
type: "get_page_content",
tabId: 456
});
sendToDesktop({
type: "execute_script",
code: "document.title",
tabId: 789
});
接收来自桌面应用:
javascript
{
type: "response",
status: "success",
data: {
title: "Example Domain",
content: "..."
}
}
四、Native Host 端实现(Node.js 示例)
4.1 核心通信代码
javascript
#!/usr/bin/env node
// chrome-native-host
const fs = require('fs');
/**
* Native Messaging 协议实现
* 消息格式:[4字节长度(小端序)] + [JSON 数据]
*/
/**
* 读取消息
*/
function readMessage() {
return new Promise((resolve, reject) => {
// 读取前 4 字节的消息长度
const lengthBuffer = Buffer.alloc(4);
process.stdin.read(lengthBuffer);
if (!lengthBuffer) {
reject(new Error('无法读取消息长度'));
return;
}
const messageLength = lengthBuffer.readUInt32LE(0);
// 读取实际消息内容
const messageBuffer = Buffer.alloc(messageLength);
process.stdin.read(messageBuffer);
try {
const message = JSON.parse(messageBuffer.toString('utf8'));
resolve(message);
} catch (err) {
reject(err);
}
});
}
/**
* 发送消息
*/
function sendMessage(message) {
const buffer = Buffer.from(JSON.stringify(message), 'utf8');
const header = Buffer.alloc(4);
// 写入消息长度(小端序)
header.writeUInt32LE(buffer.length, 0);
// 先写入长度,再写入消息体
process.stdout.write(header);
process.stdout.write(buffer);
}
/**
* 处理浏览器操作
*/
async function handleBrowserAction(message) {
logToFile(`处理操作: ${message.action}`);
switch (message.action) {
case 'navigate':
// 使用 AppleScript 或其他方式控制 Chrome
await executeAppleScript(`
tell application "Google Chrome"
set URL of active tab of front window to "${message.url}"
end tell
`);
return { status: 'success', result: 'Navigation completed' };
case 'get_content':
// 执行 JavaScript 获取页面内容
const content = await executeJavaScriptInChrome(
message.tabId,
'document.body.innerText'
);
return { status: 'success', data: content };
default:
return { status: 'error', message: 'Unknown action' };
}
}
/**
* 执行 AppleScript (macOS)
*/
function executeAppleScript(script) {
const { execSync } = require('child_process');
return execSync(`osascript -e '${script}'`).toString();
}
/**
* 记录日志到文件(因为 stdout 被占用)
*/
function logToFile(message) {
fs.appendFileSync(
'/tmp/claude-native-host.log',
`${new Date().toISOString()} - ${message}\n`
);
}
/**
* 主循环
*/
async function main() {
logToFile('Native Host 已启动');
// 设置 stdin 为二进制模式
process.stdin.setEncoding(null);
try {
while (true) {
// 读取来自 Chrome 扩展的消息
const message = await readMessage();
logToFile(`收到消息: ${JSON.stringify(message)}`);
// 处理消息
let response;
if (message.type === 'browser_action') {
response = await handleBrowserAction(message);
} else if (message.type === 'ping') {
response = { type: 'pong', timestamp: Date.now() };
} else {
response = {
type: 'error',
message: 'Unknown message type'
};
}
// 发送响应
sendMessage(response);
}
} catch (err) {
logToFile(`错误: ${err.message}`);
process.exit(1);
}
}
// 启动
main();
4.2 Python 实现示例
python
#!/usr/bin/env python3
# chrome-native-host
import sys
import json
import struct
import logging
# 配置日志(不能使用 stdout)
logging.basicConfig(
filename='/tmp/claude-native-host.log',
level=logging.DEBUG,
format='%(asctime)s - %(message)s'
)
def read_message():
"""读取来自 Chrome 的消息"""
# 读取消息长度(4字节,小端序)
raw_length = sys.stdin.buffer.read(4)
if not raw_length:
sys.exit(0)
message_length = struct.unpack('=I', raw_length)[0]
# 读取消息内容
message_data = sys.stdin.buffer.read(message_length)
return json.loads(message_data.decode('utf-8'))
def send_message(message):
"""发送消息到 Chrome"""
encoded_message = json.dumps(message).encode('utf-8')
message_length = len(encoded_message)
# 写入长度(4字节,小端序)
sys.stdout.buffer.write(struct.pack('=I', message_length))
# 写入消息内容
sys.stdout.buffer.write(encoded_message)
sys.stdout.buffer.flush()
def handle_message(message):
"""处理消息"""
logging.info(f"收到消息: {message}")
if message.get('type') == 'browser_action':
action = message.get('action')
if action == 'navigate':
# 执行浏览器操作
return {
'type': 'response',
'status': 'success',
'data': {'result': 'Navigation completed'}
}
return {
'type': 'error',
'message': 'Unknown message type'
}
def main():
logging.info('Native Host 启动')
while True:
try:
message = read_message()
response = handle_message(message)
send_message(response)
except Exception as e:
logging.error(f'错误: {str(e)}')
sys.exit(1)
if __name__ == '__main__':
main()
五、完整的工作流程
5.1 安装和配置流程
-
安装 Claude 桌面应用
- 应用自动创建 Native Host 配置文件
- 可执行程序被放置在应用包内
-
安装 Chrome 扩展
- 从 Chrome Web Store 安装
- 扩展 ID:
fcoeoabgfenejglbffodgkkbkcdhcgfn
-
授权连接
- macOS: 在"系统设置 > 隐私与安全 > 自动化"中授权
- Windows: 通常自动授权
-
建立通信
- Chrome 读取配置文件
- 启动 Native Host 进程
- 通过 stdio 建立双向通信管道
5.2 消息交互示例
javascript
// 扩展发送:获取页面标题
sendToDesktop({
type: "execute_script",
code: "document.title",
tabId: 123
});
// 桌面应用响应
{
type: "response",
status: "success",
data: {
result: "Example Domain"
}
}
// 扩展发送:导航到新页面
sendToDesktop({
type: "browser_action",
action: "navigate",
url: "https://claude.ai",
tabId: 123
});
// 桌面应用响应
{
type: "response",
status: "success",
data: {
result: "Navigation completed",
finalUrl: "https://claude.ai"
}
}
六、关键技术要点
6.1 通信协议特点
- 二进制协议:消息长度用 4 字节小端序表示
- JSON 格式:消息体使用 JSON 编码
- 同步通信:请求-响应模式
- 本地执行:所有数据仅在本地传输
6.2 安全考虑
javascript
// 验证消息来源
nativePort.onMessage.addListener((message) => {
// 验证消息结构
if (!message.type || !message.data) {
console.error("非法消息格式");
return;
}
// 验证操作权限
if (message.type === 'execute_script') {
if (!isAllowedScript(message.code)) {
console.error("不允许执行的脚本");
return;
}
}
handleDesktopMessage(message);
});
function isAllowedScript(code) {
// 检查脚本是否安全
const dangerousPatterns = [
'eval(',
'Function(',
'localStorage.clear()',
'document.cookie'
];
return !dangerousPatterns.some(pattern =>
code.includes(pattern)
);
}
6.3 错误处理
javascript
// 扩展端
nativePort.onDisconnect.addListener(() => {
if (chrome.runtime.lastError) {
const error = chrome.runtime.lastError.message;
if (error.includes('Specified native messaging host not found')) {
console.error('Native Host 未安装或配置错误');
showInstallPrompt();
} else if (error.includes('Native host has exited')) {
console.error('Native Host 进程异常退出');
attemptReconnect();
}
}
});
function attemptReconnect() {
setTimeout(() => {
console.log('尝试重新连接...');
connectToNativeHost();
}, 3000);
}
七、调试技巧
7.1 查看配置文件
bash
# macOS
cat ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json
# Linux
cat ~/.config/google-chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json
7.2 检查 Native Host 进程
bash
# 查看运行中的 Native Host
ps aux | grep chrome-native-host
# 查看日志
tail -f /tmp/claude-native-host.log
7.3 Chrome 扩展调试
javascript
// 在 background.js 中添加详细日志
nativePort.onMessage.addListener((message) => {
console.log('[Native] 接收:', JSON.stringify(message, null, 2));
});
nativePort.postMessage = new Proxy(nativePort.postMessage, {
apply(target, thisArg, args) {
console.log('[Native] 发送:', JSON.stringify(args[0], null, 2));
return Reflect.apply(target, thisArg, args);
}
});
7.4 测试连接
javascript
// 测试 Native Host 连接
function testConnection() {
const testPort = chrome.runtime.connectNative(
'com.anthropic.claude_browser_extension'
);
testPort.onMessage.addListener((response) => {
console.log('连接测试成功:', response);
testPort.disconnect();
});
testPort.onDisconnect.addListener(() => {
if (chrome.runtime.lastError) {
console.error('连接测试失败:', chrome.runtime.lastError);
}
});
testPort.postMessage({ type: 'ping' });
}
testConnection();
八、常见问题与解决方案
8.1 连接失败
问题:扩展无法连接到 Native Host
排查步骤:
bash
# 1. 检查配置文件是否存在
ls -la ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/
# 2. 验证可执行文件权限
ls -la /Applications/Claude.app/Contents/Helpers/chrome-native-host
# 3. 手动测试 Native Host
echo '{"type":"ping"}' | /Applications/Claude.app/Contents/Helpers/chrome-native-host
8.2 多应用冲突
问题:同时安装 Claude Desktop 和 Claude Code 导致冲突
解决方案:
bash
# 临时禁用 Claude Desktop 的 Native Host
mv ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json \
~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json.backup
# 重启 Chrome
8.3 权限问题
macOS 权限设置:
系统设置 > 隐私与安全 > 自动化
勾选:Google Chrome.app
允许:Claude 控制 Chrome
九、性能优化建议
9.1 消息批处理
javascript
// 批量发送消息减少通信开销
class MessageBatcher {
constructor(interval = 100) {
this.queue = [];
this.interval = interval;
this.timer = null;
}
add(message) {
this.queue.push(message);
if (!this.timer) {
this.timer = setTimeout(() => {
this.flush();
}, this.interval);
}
}
flush() {
if (this.queue.length > 0) {
sendToDesktop({
type: 'batch',
messages: this.queue
});
this.queue = [];
}
this.timer = null;
}
}
const batcher = new MessageBatcher();
batcher.add({ type: 'action1' });
batcher.add({ type: 'action2' });
// 自动批量发送
9.2 连接池管理
javascript
class ConnectionPool {
constructor(maxConnections = 5) {
this.connections = [];
this.maxConnections = maxConnections;
}
getConnection() {
// 复用空闲连接
const idle = this.connections.find(c => !c.busy);
if (idle) {
idle.busy = true;
return idle;
}
// 创建新连接
if (this.connections.length < this.maxConnections) {
const conn = this.createConnection();
this.connections.push(conn);
return conn;
}
// 等待连接释放
return new Promise(resolve => {
this.waitQueue.push(resolve);
});
}
releaseConnection(conn) {
conn.busy = false;
if (this.waitQueue.length > 0) {
const resolve = this.waitQueue.shift();
resolve(conn);
}
}
}
十、总结
Claude 桌面应用与 Chrome 扩展的通信机制基于以下关键技术:
- Native Messaging 协议:Chrome 官方提供的本地应用通信标准
- 配置文件发现:通过 JSON 配置文件实现自动发现
- stdio 通信:使用标准输入输出进行二进制数据传输
- 本地执行:所有操作在本地完成,保护隐私安全
这种架构设计的优势:
- ✅ 安全性高:数据不经过云端
- ✅ 性能好:本地通信延迟低
- ✅ 可靠性强:不依赖网络
- ✅ 灵活性大:可实现复杂的浏览器自动化