{"_id":"@azmai/mcp-js","name":"@azmai/mcp-js","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@azmai/mcp-js","version":"0.1.0","description":"Browser-native MCP runtime for handling LLM tool calls","main":"src/index.js","type":"module","scripts":{"test":"node test.js","dev":"vite","build":"vite build","preview":"vite preview"},"keywords":["mcp","llm","tool-calls","browser","runtime","javascript"],"author":{"name":"Azmath Moosa","email":"azmathmoosa@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/azmathmoosa/mcp-js.git"},"bugs":{"url":"https://github.com/azmathmoosa/mcp-js/issues"},"homepage":"https://github.com/azmathmoosa/mcp-js#readme","license":"MIT","dependencies":{"ajv":"^8.12.0"},"exports":{".":"./src/index.js"},"engines":{"node":">=14.0.0"},"devDependencies":{"vite":"^7.1.10"},"_id":"@azmai/mcp-js@0.1.0","gitHead":"074531ac9b6e63725ba765b92e3922fdbf64582c","_nodeVersion":"22.19.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-A9JE8kuQOGX3ZLsmSn7YggzDW+K6QqQ81hiHrToEaNnJHP+FGkJXCbrw1KlGhxiuLEo9iN1pdK2XC/jAoONZzA==","shasum":"ddb6f640fad4a5fa3eb62d272736b679b21887fb","tarball":"https://registry.npmjs.org/@azmai/mcp-js/-/mcp-js-0.1.0.tgz","fileCount":9,"unpackedSize":79188,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCeuSJx6wUn3gnbr1ZVU3paM8086PveYYWl6u8ZWGnq1gIhAP7b74q9OOTW9x6TmE+p8/aq5o75Ko8u7v4tKWS8k4Ow"}]},"_npmUser":{"name":"azmathmoosa","email":"azmathmoosa@gmail.com"},"directories":{},"maintainers":[{"name":"azmathmoosa","email":"azmathmoosa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-js_0.1.0_1760762585216_0.22678974981465627"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-18T04:43:05.121Z","0.1.0":"2025-10-18T04:43:05.407Z","modified":"2025-10-18T04:43:06.073Z"},"maintainers":[{"name":"azmathmoosa","email":"azmathmoosa@gmail.com"}],"description":"Browser-native MCP runtime for handling LLM tool calls","homepage":"https://github.com/azmathmoosa/mcp-js#readme","keywords":["mcp","llm","tool-calls","browser","runtime","javascript"],"repository":{"type":"git","url":"git+https://github.com/azmathmoosa/mcp-js.git"},"author":{"name":"Azmath Moosa","email":"azmathmoosa@gmail.com"},"bugs":{"url":"https://github.com/azmathmoosa/mcp-js/issues"},"license":"MIT","readme":"# 🧠 mcp-js\n\nA lightweight, browser-native runtime for handling LLM tool calls with full **Model Context Protocol (MCP)** JSON-RPC 2.0 compliance.\n\n[![npm version](https://badge.fury.io/js/@azmai%2Fmcp-js.svg)](https://www.npmjs.com/package/@azmai/mcp-js)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## ✨ Features\n\n- 🌐 **Browser-native** - Works entirely in the browser, no Node.js required\n- 🔍 **Smart parsing** - Extracts tool calls from text, JSON, or streaming responses\n- ✅ **JSON Schema validation** - Built-in argument validation using AJV\n- ⚡ **Async execution** - Supports both sequential and parallel execution\n- 📝 **Event system** - Listen to execution lifecycle events\n- 🛠️ **Framework agnostic** - Works with React, Vue, vanilla JS, or any framework\n- 🎯 **TypeScript ready** - Full type definitions included\n- 📊 **Built-in logging** - Comprehensive debug and error logging\n- 🌐 **MCP Protocol** - Full JSON-RPC 2.0 compliance for standard MCP clients\n- 📡 **Transport agnostic** - WebSocket, HTTP, or any transport layer\n- 🔧 **Dual API** - Use directly or via MCP protocol messages\n\n## 🤔 Why MCP in the Browser?\n\nTraditional AI interactions require a **server roundtrip** for every action - LLMs talk to your backend, which then updates the frontend. This creates latency, complexity, and limits what AI agents can do.\n\n**mcp-js changes everything** by bringing the Model Context Protocol directly to the browser:\n\n### 🎯 **Direct UI Manipulation**\n- **No server needed** - LLMs can directly manipulate your app's state, DOM, and UI components\n- **Real-time interaction** - Voice agents can instantly update documents, move elements, change styles\n- **Zero latency** - No network roundtrips for UI operations\n\n### 🎮 **Revolutionary Use Cases**\n\n**📝 Document Editing**: *\"Hey AI, make the title bigger and add a bullet point here\"*\n```js\nmcp.register('update_document', ({elementId, changes}) => {\n  document.getElementById(elementId).style.fontSize = changes.fontSize;\n  // Direct DOM manipulation - no server required!\n});\n```\n\n**🎨 Visual Builders**: *\"Move this box to the right and connect it to the other element\"*\n```js\nmcp.register('move_flowchart_node', ({nodeId, x, y}) => {\n  const node = flowchart.getNode(nodeId);\n  node.position = { x, y };\n  flowchart.render(); // Instant visual feedback\n});\n```\n\n**🎪 Interactive Experiences**: *\"Change the theme to dark mode and highlight that section\"*\n```js\nmcp.register('update_ui_theme', ({theme, highlightSelector}) => {\n  document.body.className = theme;\n  document.querySelector(highlightSelector).classList.add('highlight');\n});\n```\n\n### 🚀 **The Result**\n- **Seamless AI agents** that feel native to your app\n- **Voice-driven interfaces** for complex visual tasks  \n- **AI-powered creativity tools** that respond in real-time\n- **Accessibility breakthroughs** - voice control for any UI element\n\n## 🚀 Quick Start\n\n### Installation\n\n```bash\nnpm install @azmai/mcp-js\n```\n\n### Basic Usage\n\n```js\nimport mcp from '@azmai/mcp-js';\n\n// Enable debug logging\nmcp.debug = true;\n\n// Register a tool\nmcp.register('add_numbers', ({x, y}) => x + y, {\n  schema: {\n    type: 'object',\n    properties: {\n      x: { type: 'number' },\n      y: { type: 'number' }\n    },\n    required: ['x', 'y']\n  },\n  description: 'Add two numbers together'\n});\n\n// Parse LLM response\nconst llmResponse = '{\"tool_call\":{\"tool\":\"add_numbers\",\"args\":{\"x\":5,\"y\":10}}}';\nconst toolCalls = mcp.parse(llmResponse);\n\n// Execute tool calls\nconst results = await mcp.execute(toolCalls);\nconsole.log(results);\n// → [{ tool: 'add_numbers', result: 15, metadata: {...} }]\n```\n\n## 🌐 MCP Protocol Support\n\nmcp-js now implements the full **Model Context Protocol (MCP)** specification with JSON-RPC 2.0 compliance, allowing standard MCP clients (like Claude) to communicate with your tools.\n\n### Quick MCP Example\n\n```js\nimport mcp from '@azmai/mcp-js';\n\n// Register tools normally\nmcp.register('calculate', ({op, a, b}) => {\n  return op === 'add' ? a + b : a * b;\n}, {\n  schema: { /* JSON Schema */ },\n  description: 'Basic calculator'\n});\n\n// Handle MCP messages\nconst response = await mcp.handleMCPMessage({\n  jsonrpc: '2.0',\n  method: 'tools/call',\n  id: '1',\n  params: { name: 'calculate', arguments: { op: 'add', a: 5, b: 3 } }\n});\n// → { jsonrpc: '2.0', id: '1', result: { output: 8, metadata: {...} } }\n```\n\n### WebSocket Integration\n\n```js\n// Browser WebSocket client\nconst ws = new WebSocket('ws://localhost:8080');\n\nws.onopen = async () => {\n  // Initialize MCP session\n  ws.send(JSON.stringify({\n    jsonrpc: '2.0',\n    method: 'initialize',\n    id: '1',\n    params: { clientInfo: { name: 'my-client', version: '1.0.0' } }\n  }));\n};\n\nws.onmessage = (event) => {\n  const response = JSON.parse(event.data);\n  console.log('MCP Response:', response);\n};\n```\n\n### Supported MCP Methods\n\n| Method | Description | Status |\n|--------|-------------|--------|\n| `initialize` | Initialize MCP session | ✅ |\n| `tools/list` | List available tools | ✅ |\n| `tools/call` | Execute a tool | ✅ |\n| `shutdown` | Graceful shutdown | ✅ |\n| `exit` | Terminate connection | ✅ |\n\n## 📚 API Reference\n\n### Core Methods\n\n#### `mcp.register(name, fn, options)`\n\nRegister a tool function with optional schema validation.\n\n**Parameters:**\n- `name` (string) - Unique tool name\n- `fn` (Function) - Function to execute\n- `options` (object) - Configuration options\n  - `schema` (object) - JSON Schema for argument validation\n  - `description` (string) - Human-readable description\n\n**Returns:** `boolean` - Success status\n\n```js\nmcp.register('calculate_area', ({width, height}) => width * height, {\n  schema: {\n    type: 'object',\n    properties: {\n      width: { type: 'number', minimum: 0 },\n      height: { type: 'number', minimum: 0 }\n    },\n    required: ['width', 'height']\n  },\n  description: 'Calculate rectangle area'\n});\n```\n\n#### `mcp.parse(llmResponse)`\n\nParse LLM response and extract tool calls.\n\n**Parameters:**\n- `llmResponse` (string|object) - LLM response to parse\n\n**Returns:** `Array|null` - Array of tool calls or null\n\n```js\n// Supports various formats\nconst calls1 = mcp.parse('{\"tool_call\":{\"tool\":\"add\",\"args\":{\"x\":1,\"y\":2}}}');\nconst calls2 = mcp.parse({ tool_call: { tool: 'add', args: { x: 1, y: 2 } } });\nconst calls3 = mcp.parse('Use the calculator: {\"tool_call\":{\"tool\":\"add\",\"args\":{\"x\":5,\"y\":3}}}');\n```\n\n#### `mcp.execute(toolCalls, options)`\n\nExecute tool calls with validation and error handling.\n\n**Parameters:**\n- `toolCalls` (Array) - Array of tool calls to execute\n- `options` (object) - Execution options\n  - `parallel` (boolean) - Execute in parallel (default: false)\n  - `continueOnError` (boolean) - Continue on errors (default: true)\n  - `maxConcurrency` (number) - Max parallel executions (default: 5)\n\n**Returns:** `Promise<Array>` - Array of results\n\n```js\nconst results = await mcp.execute(toolCalls, {\n  parallel: true,\n  continueOnError: false,\n  maxConcurrency: 3\n});\n```\n\n### Utility Methods\n\n#### `mcp.executeSingle(name, args)`\n\nExecute a single tool directly by name.\n\n```js\nconst result = await mcp.executeSingle('add_numbers', { x: 10, y: 20 });\nconsole.log(result); // → 30\n```\n\n#### `mcp.listTools()`\n\nGet information about all registered tools.\n\n```js\nconst tools = mcp.listTools();\nconsole.log(tools);\n// → [{ name: 'add_numbers', description: '...', schema: {...}, metadata: {...} }]\n```\n\n#### `mcp.describeTools()`\n\nGenerate human-readable tool descriptions for LLM context.\n\n```js\nconst descriptions = mcp.describeTools();\nconsole.log(descriptions);\n// → **add_numbers**: Add two numbers together\n//   Parameters: x, y\n//   Required: x, y\n```\n\n#### `mcp.parseAndExecute(llmResponse, options)`\n\nParse and execute in one step.\n\n```js\nconst results = await mcp.parseAndExecute(llmResponse, { parallel: true });\n```\n\n### Configuration\n\n#### `mcp.debug`\n\nEnable/disable debug logging.\n\n```js\nmcp.debug = true;  // Enable verbose logging\nmcp.debug = false; // Disable debug logs\n```\n\n#### `mcp.setStrict(enabled)`\n\nEnable/disable strict mode for validation.\n\n```js\nmcp.setStrict(true);  // Throw errors on validation failures\nmcp.setStrict(false); // Log errors but continue execution\n```\n\n### Events\n\nListen to execution lifecycle events:\n\n```js\nmcp.on('call', (data) => {\n  console.log(`Executing: ${data.tool}`, data.args);\n});\n\nmcp.on('result', (data) => {\n  console.log(`Success: ${data.tool} (${data.duration}ms)`, data.result);\n});\n\nmcp.on('error', (data) => {\n  console.log(`Error: ${data.tool} - ${data.error}`);\n});\n\nmcp.on('tool_registered', (data) => {\n  console.log(`Registered: ${data.name}`);\n});\n```\n\nAvailable events:\n- `call` - Tool execution started\n- `result` - Tool execution completed successfully\n- `error` - Tool execution failed\n- `tool_registered` - New tool registered\n- `tool_unregistered` - Tool removed\n- `registry_cleared` - All tools cleared\n\n### MCP Protocol Methods\n\n#### `mcp.handleMCPMessage(message)`\n\nHandle incoming JSON-RPC 2.0 messages according to MCP specification.\n\n```js\nconst response = await mcp.handleMCPMessage({\n  jsonrpc: '2.0',\n  method: 'tools/list',\n  id: '1'\n});\n```\n\n#### `mcp.setTransport(sendFn, receiveFn)`\n\nConfigure transport layer for MCP communication.\n\n```js\nmcp.setTransport(\n  (message) => websocket.send(JSON.stringify(message)),\n  (message) => console.log('Received:', message)\n);\n```\n\n#### `mcp.getMCPStatus()`\n\nGet MCP server status and capabilities.\n\n```js\nconst status = mcp.getMCPStatus();\n// → { initialized: true, capabilities: {...}, toolCount: 5, ... }\n```\n\n### Streaming Parser\n\nHandle partial/streaming responses:\n\n```js\nconst parser = mcp.createStreamingParser();\n\n// Process chunks as they arrive\nconst chunk1 = '{\"tool_call\":{\"tool\":\"add\"';\nconst chunk2 = ',\"args\":{\"x\":1,\"y\":2}}}';\n\nconst calls1 = parser.addChunk(chunk1); // → null (incomplete)\nconst calls2 = parser.addChunk(chunk2); // → [{ tool: 'add', args: {x:1, y:2} }]\n\n// Get all found calls\nconst allCalls = parser.getAllCalls();\n```\n\n### Statistics\n\nGet execution statistics:\n\n```js\nconst stats = mcp.getStats();\nconsole.log(stats);\n// → {\n//     toolCount: 5,\n//     totalCalls: 42,\n//     totalErrors: 3,\n//     successRate: 92.86,\n//     tools: [...]\n//   }\n```\n\n## 🎯 Usage Examples\n\n### React Integration\n\n```jsx\nimport { useState, useEffect } from 'react';\nimport mcp from '@azmai/mcp-js';\n\nfunction ToolExecutor() {\n  const [result, setResult] = useState(null);\n  \n  useEffect(() => {\n    // Register tools on component mount\n    mcp.register('greet', ({name}) => `Hello, ${name}!`, {\n      schema: {\n        type: 'object',\n        properties: { name: { type: 'string' } },\n        required: ['name']\n      }\n    });\n    \n    // Listen to events\n    const handleResult = (data) => setResult(data);\n    mcp.on('result', handleResult);\n    \n    return () => mcp.off('result', handleResult);\n  }, []);\n  \n  const executeTool = async () => {\n    const calls = mcp.parse('{\"tool_call\":{\"tool\":\"greet\",\"args\":{\"name\":\"World\"}}}');\n    await mcp.execute(calls);\n  };\n  \n  return (\n    <div>\n      <button onClick={executeTool}>Execute Tool</button>\n      {result && <div>Result: {JSON.stringify(result)}</div>}\n    </div>\n  );\n}\n```\n\n### Vue Integration\n\n```vue\n<template>\n  <div>\n    <button @click=\"executeTool\">Execute Tool</button>\n    <div v-if=\"result\">Result: {{ result }}</div>\n  </div>\n</template>\n\n<script>\nimport mcp from '@azmai/mcp-js';\n\nexport default {\n  data() {\n    return { result: null };\n  },\n  \n  mounted() {\n    mcp.register('timestamp', () => new Date().toISOString(), {\n      description: 'Get current timestamp'\n    });\n    \n    mcp.on('result', (data) => {\n      this.result = data.result;\n    });\n  },\n  \n  methods: {\n    async executeTool() {\n      await mcp.executeSingle('timestamp');\n    }\n  }\n};\n</script>\n```\n\n### Advanced Tool Registration\n\n```js\n// Async tool with complex validation\nmcp.register('fetch_data', async ({url, options = {}}) => {\n  const response = await fetch(url, options);\n  return response.json();\n}, {\n  schema: {\n    type: 'object',\n    properties: {\n      url: { \n        type: 'string', \n        format: 'uri',\n        description: 'URL to fetch' \n      },\n      options: {\n        type: 'object',\n        properties: {\n          method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE'] },\n          headers: { type: 'object' }\n        },\n        default: {}\n      }\n    },\n    required: ['url']\n  },\n  description: 'Fetch data from a URL'\n});\n\n// Tool with error handling\nmcp.register('safe_divide', ({x, y}) => {\n  if (y === 0) throw new Error('Division by zero');\n  return x / y;\n}, {\n  schema: {\n    type: 'object',\n    properties: {\n      x: { type: 'number' },\n      y: { type: 'number', not: { const: 0 } }\n    },\n    required: ['x', 'y']\n  },\n  description: 'Safely divide two numbers'\n});\n```\n\n## 🔧 Advanced Features\n\n### Custom Validation\n\n```js\nimport { schemaValidator } from '@azmai/mcp-js';\n\n// Add custom format validator\nschemaValidator.ajv.addFormat('email', /^[^@]+@[^@]+\\.[^@]+$/);\n\nmcp.register('send_email', ({to, subject, body}) => {\n  // Send email logic\n}, {\n  schema: {\n    type: 'object',\n    properties: {\n      to: { type: 'string', format: 'email' },\n      subject: { type: 'string' },\n      body: { type: 'string' }\n    },\n    required: ['to', 'subject', 'body']\n  }\n});\n```\n\n### Middleware Pattern\n\n```js\n// Create custom execution wrapper\nconst originalExecute = mcp.execute.bind(mcp);\n\nmcp.execute = async function(toolCalls, options = {}) {\n  console.log('Pre-execution hook');\n  \n  try {\n    const results = await originalExecute(toolCalls, options);\n    console.log('Post-execution hook');\n    return results;\n  } catch (error) {\n    console.log('Error hook:', error);\n    throw error;\n  }\n};\n```\n\n## 🧪 Testing\n\n```js\n// Mock tools for testing\nmcp.register('mock_tool', (args) => ({ mocked: true, args }), {\n  schema: { type: 'object' }\n});\n\n// Test parsing\nconst testResponse = '{\"tool_call\":{\"tool\":\"mock_tool\",\"args\":{\"test\":true}}}';\nconst calls = mcp.parse(testResponse);\nassert(calls.length === 1);\nassert(calls[0].tool === 'mock_tool');\n\n// Test execution\nconst results = await mcp.execute(calls);\nassert(results[0].result.mocked === true);\n```\n\n## 🎪 Demo\n\nOpen `examples/demo.html` in your browser to see an interactive demonstration of all features.\n\n## 🚀 MCP Integration Examples\n\n### Node.js WebSocket Server\n\n```js\nimport { WebSocketServer } from 'ws';\nimport mcp from '@azmai/mcp-js';\n\n// Register your tools\nmcp.register('greet', ({name}) => `Hello, ${name}!`, {\n  schema: {\n    type: 'object',\n    properties: { name: { type: 'string' } },\n    required: ['name']\n  }\n});\n\nconst wss = new WebSocketServer({ port: 8080 });\n\nwss.on('connection', (ws) => {\n  console.log('MCP client connected');\n  \n  mcp.setTransport((message) => ws.send(JSON.stringify(message)));\n  \n  ws.on('message', async (data) => {\n    const message = JSON.parse(data.toString());\n    const response = await mcp.handleMCPMessage(message);\n    ws.send(JSON.stringify(response));\n  });\n});\n```\n\n### Express HTTP Server\n\n```js\nimport express from 'express';\nimport mcp from '@azmai/mcp-js';\n\nconst app = express();\napp.use(express.json());\n\napp.post('/mcp', async (req, res) => {\n  const response = await mcp.handleMCPMessage(req.body);\n  res.json(response);\n});\n\napp.listen(3000);\n```\n\n### Browser WebSocket Client\n\n```js\nclass MCPClient {\n  constructor(url) {\n    this.ws = new WebSocket(url);\n    this.requestId = 1;\n    this.pending = new Map();\n    \n    this.ws.onmessage = (event) => {\n      const response = JSON.parse(event.data);\n      const resolve = this.pending.get(response.id);\n      if (resolve) {\n        this.pending.delete(response.id);\n        resolve(response);\n      }\n    };\n  }\n  \n  async call(method, params = {}) {\n    const id = String(this.requestId++);\n    \n    return new Promise((resolve) => {\n      this.pending.set(id, resolve);\n      this.ws.send(JSON.stringify({\n        jsonrpc: '2.0',\n        method,\n        params,\n        id\n      }));\n    });\n  }\n  \n  async initialize() {\n    return this.call('initialize', {\n      clientInfo: { name: 'browser-client', version: '1.0.0' }\n    });\n  }\n  \n  async listTools() {\n    return this.call('tools/list');\n  }\n  \n  async callTool(name, args) {\n    return this.call('tools/call', { name, arguments: args });\n  }\n}\n\n// Usage\nconst client = new MCPClient('ws://localhost:8080');\nawait client.initialize();\nconst tools = await client.listTools();\nconst result = await client.callTool('greet', { name: 'World' });\n```\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🔗 Links\n\n- [npm package](https://www.npmjs.com/package/@azmai/mcp-js)\n- [GitHub repository](https://github.com/azmathmoosa/mcp-js)\n- [Documentation](https://github.com/azmathmoosa/mcp-js#readme)\n- [Issues](https://github.com/azmathmoosa/mcp-js/issues)\n\n## 🙏 Acknowledgments\n\n- [AJV](https://ajv.js.org/) for JSON Schema validation\n- The LLM community for inspiration and feedback\n\n---\n\nMade with ❤️ for the browser-first future of AI applications.","readmeFilename":"README.md","_rev":"1-38c5bd7029bfe5d368bc3e334cfb1203"}