浏览器端LLM工具调用运行时

一个轻量级的浏览器原生运行时,用于处理LLM工具调用,完全符合Model Context Protocol (MCP) JSON-RPC 2.0标准,支持直接UI操作和实时交互。

  • by azmathmoosa
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 azmathmoosa/mcp-js

使用说明

🧠 mcp-js(可译为:“大脑(或思维)mcp-js”,但具体含义需根据上下文确定,因为“mcp-js”本身是一个技术或项目名称,没有直接的中文对应)

一个轻量级的、浏览器原生的运行时环境,用于处理大型语言模型(LLM)工具调用,具备完整功能 模型上下文协议(MCP) 符合JSON-RPC 2.0标准。

✨ 特点

  • 🌐(表示“互联网”或“世界”的符号) 浏览器原生的 - 完全在浏览器中运行,无需Node.js
  • 🔍(放大镜图标,常用于表示搜索、查看细节等含义) 智能解析 - 从文本、JSON或流式响应中提取工具调用
  • ✅ JSON Schema 验证 - 使用AJV内置参数验证
  • ⚡(闪电符号,常用于表示速度、能量或快速充电等概念) 异步执行 - 支持顺序执行和并行执行
  • 📝 事件系统 - 监听执行生命周期事件
  • 🛠️(扳手或工具的象征,无直接对应中文翻译,可理解为“工具”或“修理/维修”) 框架无关 - 兼容React、Vue、原生JavaScript或任何框架
  • 🎯(瞄准靶心) TypeScript 准备就绪 - 包含完整的类型定义
  • 📊(表格/数据图表) 内置日志记录 - 全面的调试和错误日志记录
  • 🌐 表示“地球,互联网,全球网络”的意思,常用来象征互联网或全球连接。 MCP协议 - 标准MCP客户端完全符合JSON-RPC 2.0规范
  • 📡(卫星天线或无线电信标) 与传输方式无关 - WebSocket、HTTP 或任何传输层
  • 🔧(扳手) 双API - 直接使用或通过MCP协议消息使用

🤔 为什么要在浏览器中使用MCP?

传统的AI交互需要 服务器往返(或服务器来回) 对于每一个操作——大型语言模型(LLM)与你的后端进行交互,后端随后更新前端。这会带来延迟、增加复杂性,并限制人工智能代理所能执行的操作。

mcp-js 改变了一切 通过将模型上下文协议直接引入浏览器:

🎯(目标/靶心) 直接用户界面操作

  • 无需服务器 - 大语言模型(LLMs)可以直接操控您应用程序的状态、文档对象模型(DOM)和用户界面(UI)组件
  • 实时互动 - 语音代理可以即时更新文档、移动元素、更改样式
  • 零延迟 - UI操作无需网络往返

🎮 表示游戏(Game)的符号 革命性的用例/应用场景

📝 文档编辑: “嘿,AI,把标题放大点,并在这里加个项目符号。”

mcp.register('update_document', ({elementId, changes}) => {
  document.getElementById(elementId).style.fontSize = changes.fontSize;
  // Direct DOM manipulation - no server required!
});

🎨 可视化构建器: “把这个框向右移动,并连接到另一个元素上。”

mcp.register('move_flowchart_node', ({nodeId, x, y}) => {
  const node = flowchart.getNode(nodeId);
  node.position = { x, y };
  flowchart.render(); // Instant visual feedback
});

🎪 互动体验: “将主题更改为暗模式,并突出显示该部分”

mcp.register('update_ui_theme', ({theme, highlightSelector}) => {
  document.body.className = theme;
  document.querySelector(highlightSelector).classList.add('highlight');
});

🚀 结果

  • 无缝AI代理 那种感觉就像原生于您的应用一样
  • 语音驱动的界面 用于复杂的视觉任务
  • 基于人工智能的创意工具 实时响应
  • 无障碍技术的突破 - 对任何用户界面元素进行语音控制

🚀 快速入门

安装

npm install @azmai/mcp-js

基本用法

import mcp from '@azmai/mcp-js';

// Enable debug logging
mcp.debug = true;

// Register a tool
mcp.register('add_numbers', ({x, y}) => x + y, {
  schema: {
    type: 'object',
    properties: {
      x: { type: 'number' },
      y: { type: 'number' }
    },
    required: ['x', 'y']
  },
  description: 'Add two numbers together'
});

// Parse LLM response
const llmResponse = '{"tool_call":{"tool":"add_numbers","args":{"x":5,"y":10}}}';
const toolCalls = mcp.parse(llmResponse);

// Execute tool calls
const results = await mcp.execute(toolCalls);
console.log(results);
// → [{ tool: 'add_numbers', result: 15, metadata: {...} }]

🌐 MCP协议支持

mcp-js 现在实现了完整的(功能/特性) 模型上下文协议(MCP) 规范符合JSON-RPC 2.0标准,允许标准的MCP客户端(如Claude)与您的工具进行通信。

快速MCP示例

import mcp from '@azmai/mcp-js';

// Register tools normally
mcp.register('calculate', ({op, a, b}) => {
  return op === 'add' ? a + b : a * b;
}, {
  schema: { /* JSON Schema */ },
  description: 'Basic calculator'
});

// Handle MCP messages
const response = await mcp.handleMCPMessage({
  jsonrpc: '2.0',
  method: 'tools/call',
  id: '1',
  params: { name: 'calculate', arguments: { op: 'add', a: 5, b: 3 } }
});
// → { jsonrpc: '2.0', id: '1', result: { output: 8, metadata: {...} } }

WebSocket 集成

// Browser WebSocket client
const ws = new WebSocket('ws://localhost:8080');

ws.onopen = async () => {
  // Initialize MCP session
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'initialize',
    id: '1',
    params: { clientInfo: { name: 'my-client', version: '1.0.0' } }
  }));
};

ws.onmessage = (event) => {
  const response = JSON.parse(event.data);
  console.log('MCP Response:', response);
};

支持的MCP方法

方法 描述 状态
initialize 初始化MCP会话 ✅
tools/list 列出可用工具 ✅
tools/call 执行工具 ✅
shutdown 优雅关闭 ✅
exit 终止连接 ✅

📚 API参考

核心方法

mcp.register(name, fn, options)

注册一个工具函数,可选地进行模式验证。

参数:

  • name (字符串) - 唯一工具名称
  • fn (功能) - 要执行的功能
  • options (对象) - 配置选项
    • schema (object) - 用于参数验证的JSON Schema
    • description (字符串) - 人类可读的描述

返回值: boolean - 成功状态

mcp.register('calculate_area', ({width, height}) => width * height, {
  schema: {
    type: 'object',
    properties: {
      width: { type: 'number', minimum: 0 },
      height: { type: 'number', minimum: 0 }
    },
    required: ['width', 'height']
  },
  description: 'Calculate rectangle area'
});

mcp.parse(llmResponse)

解析大型语言模型(LLM)的响应并提取工具调用。

参数:

  • llmResponse (string|object) - 需要解析的LLM响应

返回值: Array|null - 工具调用数组或空值

// Supports various formats
const calls1 = mcp.parse('{"tool_call":{"tool":"add","args":{"x":1,"y":2}}}');
const calls2 = mcp.parse({ tool_call: { tool: 'add', args: { x: 1, y: 2 } } });
const calls3 = mcp.parse('Use the calculator: {"tool_call":{"tool":"add","args":{"x":5,"y":3}}}');

mcp.execute(toolCalls, options)

执行工具调用时进行验证和错误处理。

参数:

  • toolCalls (数组) - 要执行的工具调用数组
  • options (对象) - 执行选项
    • parallel (boolean) - 并行执行(默认:false)
    • continueOnError (boolean) - 在错误时继续(默认:true)
    • maxConcurrency (数字) - 最大并行执行数(默认:5)

返回值: Promise<Array> - 结果数组

const results = await mcp.execute(toolCalls, {
  parallel: true,
  continueOnError: false,
  maxConcurrency: 3
});

实用方法

mcp.executeSingle(name, args)

直接通过名称执行单个工具。

const result = await mcp.executeSingle('add_numbers', { x: 10, y: 20 });
console.log(result); // → 30

mcp.listTools()

获取所有已注册工具的信息。

const tools = mcp.listTools();
console.log(tools);
// → [{ name: 'add_numbers', description: '...', schema: {...}, metadata: {...} }]

mcp.describeTools()

为大型语言模型(LLM)上下文生成可读性强的工具描述。

const descriptions = mcp.describeTools();
console.log(descriptions);
// → **add_numbers**: Add two numbers together
//   Parameters: x, y
//   Required: x, y

mcp.parseAndExecute(llmResponse, options)

一步完成解析和执行。

const results = await mcp.parseAndExecute(llmResponse, { parallel: true });

配置

mcp.debug

启用/禁用调试日志记录。

mcp.debug = true;  // Enable verbose logging
mcp.debug = false; // Disable debug logs

mcp.setStrict(enabled)

启用/禁用严格模式进行验证。

mcp.setStrict(true);  // Throw errors on validation failures
mcp.setStrict(false); // Log errors but continue execution

活动

监听执行生命周期事件:

mcp.on('call', (data) => {
  console.log(`Executing: ${data.tool}`, data.args);
});

mcp.on('result', (data) => {
  console.log(`Success: ${data.tool} (${data.duration}ms)`, data.result);
});

mcp.on('error', (data) => {
  console.log(`Error: ${data.tool} - ${data.error}`);
});

mcp.on('tool_registered', (data) => {
  console.log(`Registered: ${data.name}`);
});

可用事件:

  • call - 工具执行开始
  • result - 工具执行成功完成
  • error - 工具执行失败
  • tool_registered - 新工具已注册
  • tool_unregistered - 工具已移除
  • registry_cleared - 所有工具已清点完毕

MCP协议方法

mcp.handleMCPMessage(message)

根据MCP规范处理传入的JSON-RPC 2.0消息。

const response = await mcp.handleMCPMessage({
  jsonrpc: '2.0',
  method: 'tools/list',
  id: '1'
});

mcp.setTransport(sendFn, receiveFn)

为MCP通信配置传输层。

mcp.setTransport(
  (message) => websocket.send(JSON.stringify(message)),
  (message) => console.log('Received:', message)
);

mcp.getMCPStatus()

获取MCP服务器的状态和功能。

const status = mcp.getMCPStatus();
// → { initialized: true, capabilities: {...}, toolCount: 5, ... }

流解析器

处理部分/流式响应:

const parser = mcp.createStreamingParser();

// Process chunks as they arrive
const chunk1 = '{"tool_call":{"tool":"add"';
const chunk2 = ',"args":{"x":1,"y":2}}}';

const calls1 = parser.addChunk(chunk1); // → null (incomplete)
const calls2 = parser.addChunk(chunk2); // → [{ tool: 'add', args: {x:1, y:2} }]

// Get all found calls
const allCalls = parser.getAllCalls();

统计

获取执行统计信息:

const stats = mcp.getStats();
console.log(stats);
// → {
//     toolCount: 5,
//     totalCalls: 42,
//     totalErrors: 3,
//     successRate: 92.86,
//     tools: [...]
//   }

🎯 使用示例

React 集成

import { useState, useEffect } from 'react';
import mcp from '@azmai/mcp-js';

function ToolExecutor() {
  const [result, setResult] = useState(null);
  
  useEffect(() => {
    // Register tools on component mount
    mcp.register('greet', ({name}) => `Hello, ${name}!`, {
      schema: {
        type: 'object',
        properties: { name: { type: 'string' } },
        required: ['name']
      }
    });
    
    // Listen to events
    const handleResult = (data) => setResult(data);
    mcp.on('result', handleResult);
    
    return () => mcp.off('result', handleResult);
  }, []);
  
  const executeTool = async () => {
    const calls = mcp.parse('{"tool_call":{"tool":"greet","args":{"name":"World"}}}');
    await mcp.execute(calls);
  };
  
  return (
    <div>
      <button onClick={executeTool}>Execute Tool</button>
      {result && <div>Result: {JSON.stringify(result)}</div>}
    </div>
  );
}

Vue 集成

<template>
  <div>
    <button @click="executeTool">Execute Tool</button>
    <div v-if="result">Result: {{ result }}</div>
  </div>
</template>

<script>
import mcp from '@azmai/mcp-js';

export default {
  data() {
    return { result: null };
  },
  
  mounted() {
    mcp.register('timestamp', () => new Date().toISOString(), {
      description: 'Get current timestamp'
    });
    
    mcp.on('result', (data) => {
      this.result = data.result;
    });
  },
  
  methods: {
    async executeTool() {
      await mcp.executeSingle('timestamp');
    }
  }
};
</script>

高级工具注册

// Async tool with complex validation
mcp.register('fetch_data', async ({url, options = {}}) => {
  const response = await fetch(url, options);
  return response.json();
}, {
  schema: {
    type: 'object',
    properties: {
      url: { 
        type: 'string', 
        format: 'uri',
        description: 'URL to fetch' 
      },
      options: {
        type: 'object',
        properties: {
          method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE'] },
          headers: { type: 'object' }
        },
        default: {}
      }
    },
    required: ['url']
  },
  description: 'Fetch data from a URL'
});

// Tool with error handling
mcp.register('safe_divide', ({x, y}) => {
  if (y === 0) throw new Error('Division by zero');
  return x / y;
}, {
  schema: {
    type: 'object',
    properties: {
      x: { type: 'number' },
      y: { type: 'number', not: { const: 0 } }
    },
    required: ['x', 'y']
  },
  description: 'Safely divide two numbers'
});

🔧 高级功能

自定义验证

import { schemaValidator } from '@azmai/mcp-js';

// Add custom format validator
schemaValidator.ajv.addFormat('email', /^[^@]+@[^@]+\.[^@]+$/);

mcp.register('send_email', ({to, subject, body}) => {
  // Send email logic
}, {
  schema: {
    type: 'object',
    properties: {
      to: { type: 'string', format: 'email' },
      subject: { type: 'string' },
      body: { type: 'string' }
    },
    required: ['to', 'subject', 'body']
  }
});

中间件模式

// Create custom execution wrapper
const originalExecute = mcp.execute.bind(mcp);

mcp.execute = async function(toolCalls, options = {}) {
  console.log('Pre-execution hook');
  
  try {
    const results = await originalExecute(toolCalls, options);
    console.log('Post-execution hook');
    return results;
  } catch (error) {
    console.log('Error hook:', error);
    throw error;
  }
};

🧪 测试

// Mock tools for testing
mcp.register('mock_tool', (args) => ({ mocked: true, args }), {
  schema: { type: 'object' }
});

// Test parsing
const testResponse = '{"tool_call":{"tool":"mock_tool","args":{"test":true}}}';
const calls = mcp.parse(testResponse);
assert(calls.length === 1);
assert(calls[0].tool === 'mock_tool');

// Test execution
const results = await mcp.execute(calls);
assert(results[0].result.mocked === true);

🎪 试演/演示

开放 examples/demo.html 在您的浏览器中查看所有功能的交互式演示。

🚀 MCP 集成示例

Node.js WebSocket 服务器

import { WebSocketServer } from 'ws';
import mcp from '@azmai/mcp-js';

// Register your tools
mcp.register('greet', ({name}) => `Hello, ${name}!`, {
  schema: {
    type: 'object',
    properties: { name: { type: 'string' } },
    required: ['name']
  }
});

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('MCP client connected');
  
  mcp.setTransport((message) => ws.send(JSON.stringify(message)));
  
  ws.on('message', async (data) => {
    const message = JSON.parse(data.toString());
    const response = await mcp.handleMCPMessage(message);
    ws.send(JSON.stringify(response));
  });
});

Express HTTP 服务器

import express from 'express';
import mcp from '@azmai/mcp-js';

const app = express();
app.use(express.json());

app.post('/mcp', async (req, res) => {
  const response = await mcp.handleMCPMessage(req.body);
  res.json(response);
});

app.listen(3000);

浏览器WebSocket客户端

class MCPClient {
  constructor(url) {
    this.ws = new WebSocket(url);
    this.requestId = 1;
    this.pending = new Map();
    
    this.ws.onmessage = (event) => {
      const response = JSON.parse(event.data);
      const resolve = this.pending.get(response.id);
      if (resolve) {
        this.pending.delete(response.id);
        resolve(response);
      }
    };
  }
  
  async call(method, params = {}) {
    const id = String(this.requestId++);
    
    return new Promise((resolve) => {
      this.pending.set(id, resolve);
      this.ws.send(JSON.stringify({
        jsonrpc: '2.0',
        method,
        params,
        id
      }));
    });
  }
  
  async initialize() {
    return this.call('initialize', {
      clientInfo: { name: 'browser-client', version: '1.0.0' }
    });
  }
  
  async listTools() {
    return this.call('tools/list');
  }
  
  async callTool(name, args) {
    return this.call('tools/call', { name, arguments: args });
  }
}

// Usage
const client = new MCPClient('ws://localhost:8080');
await client.initialize();
const tools = await client.listTools();
const result = await client.callTool('greet', { name: 'World' });

🤝 贡献(或:参与贡献)

  1. 为仓库创建分支(或“克隆仓库”)
  2. 创建你的特性分支(git checkout -b feature/amazing-feature)
  3. 提交您的更改(git commit -m 'Add amazing feature')
  4. 推送到分支(git push origin feature/amazing-feature)
  5. 提交一个拉取请求

📄 许可证

这个项目遵循MIT许可证授权——详见 许可证 文件中有详细信息。

🔗 链接

🙏 致谢

  • AJV 用于JSON Schema验证
  • 寻求大型语言模型(LLM)社区的灵感和反馈

以❤️之心打造,迎接AI应用以浏览器为先的未来。