{"_id":"@emqx-ai/mcp-mqtt-sdk","_rev":"6-bfdf912ca6472a858eb5ec45c73e9a81","name":"@emqx-ai/mcp-mqtt-sdk","dist-tags":{"latest":"0.2.7"},"versions":{"0.1.0":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.1.0","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"author":{"name":"EMQX Team"},"license":"Apache-2.0","_id":"@emqx-ai/mcp-mqtt-sdk@0.1.0","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"dist":{"shasum":"2a9641fd5c04a19aed99839e619ff147cca81ca0","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.1.0.tgz","fileCount":35,"integrity":"sha512-amt7C3gr0Wl+MOzBKsFZjMsHYe8DswazHkvdK/jH7DfGLPLmmZB7Zsz9dNppE7/zMjCE2wwRRrsYm9cChd6cpA==","signatures":[{"sig":"MEQCIFG48BmX/DQCoY3NxJ8FVJTblfvQNRCU23rbWV+kHVHiAiAyfHJk4MVXiO8sof+EiSYyrToihxMF+weaRDdEB0tSRQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":159285},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"}},"gitHead":"b74d5fd24c30f41d712fb2c3a9d9a7dbd403e6f0","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.prod.json","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsc --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"emqx","email":"yusf@emqx.io"},"_npmVersion":"9.6.7","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","directories":{},"_nodeVersion":"18.17.1","dependencies":{"zod":"^3.22.4","mqtt":"^5.3.4","nanoid":"^5.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","ts-jest":"^29.1.1","typescript":"^5.3.3","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-mqtt-sdk_0.1.0_1757924036613_0.03753088328194476","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.2.0","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"author":{"name":"EMQX Team"},"license":"Apache-2.0","_id":"@emqx-ai/mcp-mqtt-sdk@0.2.0","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"dist":{"shasum":"46188f61c4d0d9f0abfaa37cf123e806f9f3a4d1","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.2.0.tgz","fileCount":35,"integrity":"sha512-yNLrDujiz/WkNXneen7NOinoCwH5snZppyEiaHMa0A6UNOQIsqv5SVYzlKMW5iI/lQDF5Ql6BrnmURR8Ge1IRg==","signatures":[{"sig":"MEUCIBiV/loTzuetLAD+4g3cpKOzEY3w9NKtHywvNwNHSeAaAiEAjgkla5gGnJY4XSMwMUcTxmYI8nsmwFINL+cZkAzIAjY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":157098},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"}},"gitHead":"577459d03d814e94dd96beb128a2e2bcfa6f969d","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.prod.json","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsc --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"emqx","email":"yusf@emqx.io"},"_npmVersion":"9.6.7","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","directories":{},"_nodeVersion":"18.17.1","dependencies":{"zod":"^3.22.4","mqtt":"^5.3.4","nanoid":"^5.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","ts-jest":"^29.1.1","typescript":"^5.3.3","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-mqtt-sdk_0.2.0_1757927632880_0.6540961602667408","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.2.1","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"author":{"name":"EMQX Team"},"license":"Apache-2.0","_id":"@emqx-ai/mcp-mqtt-sdk@0.2.1","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"dist":{"shasum":"c095ceb86cb286498676a2d0b9e5f9dbcc2cccda","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.2.1.tgz","fileCount":35,"integrity":"sha512-IRa/8nT2Hf11kyWECywy8lAVdOWrsdGFOUG8L9l5BpTDVZMRrvbJpsvst5Wtnu/XwvPsm6+wBC4EvSShrei5zA==","signatures":[{"sig":"MEUCIDaE0nSHHprM5y3hyrco4V6qUlNRjx0WssqXWbZ33/2QAiEAyHoJZZBXhqBGPCqolbs5d3m+FoZ+KvuC7LH6iR9Q9qg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":158305},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"}},"gitHead":"457f866ee263dc5ac62247009435a4d260dc241f","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.prod.json","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsc --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"emqx","email":"yusf@emqx.io"},"_npmVersion":"9.6.7","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","directories":{},"_nodeVersion":"18.17.1","dependencies":{"zod":"^3.22.4","mqtt":"^5.3.4","nanoid":"^5.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","ts-jest":"^29.1.1","typescript":"^5.3.3","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-mqtt-sdk_0.2.1_1757929316197_0.6386982106680938","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.2.2","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"author":{"name":"EMQX Team"},"license":"Apache-2.0","_id":"@emqx-ai/mcp-mqtt-sdk@0.2.2","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"dist":{"shasum":"3735d45c5a6cd5a9e8dec017df6dceba98725b90","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.2.2.tgz","fileCount":35,"integrity":"sha512-X5lFQ3PiZr9xPCknpmQnvJiBaDFVCG2MzXy19FDE5D4JvjaOijsAFwBH6ybTVPeDcKqwc9Wpg4zOw93tD8R2bg==","signatures":[{"sig":"MEUCIAt+I7ew6tDOgsQD6YHNYm+HX/8Prm3zVFbNxUhm3MB9AiEAlYNanEhpezu1TqJwfZ5Lx2y+nQ+CbFmuAnsl9VPqMeo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":158270},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"}},"gitHead":"ce1ad5a77066255707090e47e5c78295c1d1ac8b","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.prod.json","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsc --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"emqx","email":"yusf@emqx.io"},"_npmVersion":"9.6.7","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","directories":{},"_nodeVersion":"18.17.1","dependencies":{"zod":"^3.22.4","mqtt":"^5.3.4","nanoid":"^5.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","ts-jest":"^29.1.1","typescript":"^5.3.3","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-mqtt-sdk_0.2.2_1757950579081_0.17312259547693243","host":"s3://npm-registry-packages-npm-production"}},"0.2.6":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.2.6","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"author":{"name":"EMQX Team"},"license":"Apache-2.0","_id":"@emqx-ai/mcp-mqtt-sdk@0.2.6","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"homepage":"https://github.com/emqx/mcp-typescript-sdk#readme","bugs":{"url":"https://github.com/emqx/mcp-typescript-sdk/issues"},"dist":{"shasum":"580cd3eaba24cbc24cc78d0327f9a6b96006dd0a","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.2.6.tgz","fileCount":35,"integrity":"sha512-CafOcnP+iBmHmAYW2UpP33F6Z6KoB4pKkkp1bEFlEh0UrFaWfdrjVmIq3/Wfc22KH+UY3iTj05954tQh/wDqug==","signatures":[{"sig":"MEUCIGKOnwAeNjBJ3JGeSsD2zoGGhhQdYHnNcAf9Gk4Q2gllAiEA+k+zevYgAmLnfU43TEbLcUTfGqN9hv7Y8Mqa46KC3kU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emqx-ai%2fmcp-mqtt-sdk@0.2.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":158589},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js"},"./server":{"types":"./dist/server/index.d.ts","import":"./dist/server/index.js"}},"gitHead":"92d5d27ba3acea61853240a06a907e8cf40eec34","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.prod.json","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsc --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a5c6847-fea0-4e1d-820c-4f28b2e73f87"}},"repository":{"url":"git+https://github.com/emqx/mcp-typescript-sdk.git","type":"git"},"_npmVersion":"11.7.0","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","directories":{},"_nodeVersion":"22.21.1","dependencies":{"zod":"^3.22.4","mqtt":"^5.3.4","nanoid":"^5.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","ts-jest":"^29.1.1","typescript":"^5.3.3","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-mqtt-sdk_0.2.6_1768746312686_0.5018018115345371","host":"s3://npm-registry-packages-npm-production"}},"0.2.7":{"name":"@emqx-ai/mcp-mqtt-sdk","version":"0.2.7","description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","type":"module","main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./client":{"import":"./dist/client/index.js","types":"./dist/client/index.d.ts"},"./server":{"import":"./dist/server/index.js","types":"./dist/server/index.d.ts"}},"scripts":{"build":"tsc -p tsconfig.prod.json","build:watch":"tsc --watch","test":"jest","test:watch":"jest --watch","lint":"eslint .","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"publishConfig":{"access":"public"},"keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"repository":{"type":"git","url":"git+https://github.com/emqx/mcp-typescript-sdk.git"},"author":{"name":"EMQX Team"},"license":"Apache-2.0","engines":{"node":">=18"},"dependencies":{"mqtt":"^5.3.4","zod":"^3.22.4","nanoid":"^5.0.4"},"devDependencies":{"@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0","eslint":"^8.56.0","jest":"^29.7.0","ts-jest":"^29.1.1","typescript":"^5.3.3"},"gitHead":"d76783839383b2dab035406440644c8e2df66f7b","_id":"@emqx-ai/mcp-mqtt-sdk@0.2.7","bugs":{"url":"https://github.com/emqx/mcp-typescript-sdk/issues"},"homepage":"https://github.com/emqx/mcp-typescript-sdk#readme","_nodeVersion":"22.22.0","_npmVersion":"11.10.1","dist":{"integrity":"sha512-iEE332dn08fykKJygJaAvH0OYCD4iPnrVyKwUSaO8lcaSlHYep8kwurqnAKzp+pGKuQHmJiHSPDIaHjA1a1raw==","shasum":"b8c94afa40da287eb7d5c111756e21be93a5a825","tarball":"https://registry.npmjs.org/@emqx-ai/mcp-mqtt-sdk/-/mcp-mqtt-sdk-0.2.7.tgz","fileCount":35,"unpackedSize":159235,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emqx-ai%2fmcp-mqtt-sdk@0.2.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDqeYj1ReUQpFRvzVfPJUsIXdGnqtExLkUGHFib1THWGQIgDeIAO42M/9m5BxTr86H2gR7um+bym3rgmCy0sUUUwSg="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5a5c6847-fea0-4e1d-820c-4f28b2e73f87"}},"directories":{},"maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-mqtt-sdk_0.2.7_1771984146411_0.48171592299280075"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-15T08:13:56.562Z","modified":"2026-02-25T01:49:06.946Z","0.1.0":"2025-09-15T08:13:56.779Z","0.2.0":"2025-09-15T09:13:53.070Z","0.2.1":"2025-09-15T09:41:56.376Z","0.2.2":"2025-09-15T15:36:19.276Z","0.2.6":"2026-01-18T14:25:12.822Z","0.2.7":"2026-02-25T01:49:06.628Z"},"bugs":{"url":"https://github.com/emqx/mcp-typescript-sdk/issues"},"author":{"name":"EMQX Team"},"license":"Apache-2.0","homepage":"https://github.com/emqx/mcp-typescript-sdk#readme","keywords":["mcp","mqtt","model-context-protocol","ai","llm","typescript"],"repository":{"type":"git","url":"git+https://github.com/emqx/mcp-typescript-sdk.git"},"description":"MCP (Model Context Protocol) over MQTT SDK for TypeScript","maintainers":[{"name":"emqx","email":"yusf@emqx.io"}],"readme":"# @emqx-ai/mcp-mqtt-sdk\n\nA TypeScript SDK for implementing Model Context Protocol (MCP) over MQTT, supporting both browser and Node.js environments with full type safety and automatic environment detection.\n\n## 🎯 Live Demo\n\nSee this SDK in action! Check out our **[MCP AI Companion Demo](https://github.com/emqx/mcp-ai-companion-demo)** - a real-world implementation using both Python and TypeScript with this SDK to create an AI agent that can:\n\n- 🎤 Control browser audio/video functionality\n- 😊 Switch facial expressions and emotions\n- 💬 Power interactive conversational experiences\n- 🌐 Demonstrate cross-platform MCP communication\n\nThis demo showcases how to build sophisticated AI agents using MCP over MQTT for seamless browser control and interaction.\n\n## Features\n\n- 🚀 **Universal**: Works seamlessly in browser (WebSocket) and Node.js (TCP) environments\n- 🔒 **Type Safe**: Full TypeScript support with Zod schema validation for MCP protocol\n- 🌐 **MQTT Transport**: Uses MQTT as the transport layer for reliable MCP communication\n- 🏗️ **Constructor-Based**: Clean object-oriented API with proper TypeScript classes\n- 📋 **Standards Compliant**: Follows MCP specification v2024-11-05\n- 🔧 **Tool & Resource Support**: Complete support for MCP tools and resources\n- 🔍 **Auto Discovery**: Automatic server discovery over MQTT topics\n- 🌍 **Environment Detection**: Automatic browser/Node.js detection with appropriate defaults\n\n## Requirements\n\n- Node.js >= 18\n\n## Installation\n\n```bash\nnpm install @emqx-ai/mcp-mqtt-sdk\n```\n\n## Quick Start\n\n### Creating an MCP Server\n\n```typescript\nimport { McpMqttServer } from '@emqx-ai/mcp-mqtt-sdk'\n\n// Create server instance\nconst server = new McpMqttServer({\n  // MQTT connection\n  host: 'mqtt://localhost:1883',\n\n  // Server identification\n  serverId: 'unique-server-id-123',\n  serverName: 'myapp/greeting-server',  // Hierarchical naming\n\n  // Server information\n  name: 'My MCP Server',\n  version: '1.0.0',\n\n  // Optional configuration\n  description: 'A sample MCP server providing greeting tools',\n  capabilities: {\n    tools: { listChanged: true },\n    resources: { listChanged: true, subscribe: false },\n  },\n})\n\n// Add a tool\nserver.tool(\n  'greet',\n  'Greet someone with a personalized message',\n  {\n    type: 'object',\n    properties: {\n      name: {\n        type: 'string',\n        description: 'Name of the person to greet'\n      },\n      language: {\n        type: 'string',\n        enum: ['en', 'es', 'fr'],\n        description: 'Language for greeting'\n      }\n    },\n    required: ['name'],\n  },\n  async ({ name, language = 'en' }) => {\n    const greetings = {\n      en: `Hello, ${name}!`,\n      es: `¡Hola, ${name}!`,\n      fr: `Bonjour, ${name}!`\n    }\n\n    return {\n      content: [{\n        type: 'text',\n        text: greetings[language] || greetings.en,\n      }],\n    }\n  }\n)\n\n// Add a resource\nserver.resource(\n  'config://app-settings',\n  'Application Settings',\n  async () => ({\n    contents: [{\n      uri: 'config://app-settings',\n      mimeType: 'application/json',\n      text: JSON.stringify({\n        theme: 'dark',\n        language: 'en',\n        notifications: true\n      }, null, 2),\n    }],\n  }),\n  {\n    description: 'Current application configuration',\n    mimeType: 'application/json',\n  }\n)\n\n// Event handlers\nserver.on('ready', () => {\n  console.log('Server is ready!')\n  console.log('Topics:', server.getTopics())\n})\n\nserver.on('error', (error) => {\n  console.error('Server error:', error)\n})\n\n// Start the server\nawait server.start()\n```\n\n### Creating an MCP Client\n\n```typescript\nimport { McpMqttClient } from '@emqx-ai/mcp-mqtt-sdk'\n\n// Create client instance\nconst client = new McpMqttClient({\n  // MQTT connection\n  host: 'mqtt://localhost:1883',\n\n  // Client information\n  name: 'My MCP Client',\n  version: '1.0.0',\n})\n\n// Set up server discovery handler\nclient.on('serverDiscovered', async (server) => {\n  console.log('📡 Discovered server:', server.name, '(ID:', server.serverId, ')')\n\n  try {\n    // Connect to the discovered server using serverId\n    await client.initializeServer(server.serverId)\n    console.log('✅ Connected to:', server.name)\n\n    // List available tools using serverId\n    const tools = await client.listTools(server.serverId)\n    console.log('🔧 Available tools:', tools.map(t => t.name))\n\n    // Call a tool using serverId\n    if (tools.some(t => t.name === 'greet')) {\n      const result = await client.callTool(server.serverId, 'greet', {\n        name: 'World',\n        language: 'es'\n      })\n      console.log('🎉 Tool result:', result.content[0]?.text)\n    }\n\n    // List and read resources using serverId\n    const resources = await client.listResources(server.serverId)\n    console.log('📚 Available resources:', resources.map(r => r.uri))\n\n    if (resources.some(r => r.uri === 'config://app-settings')) {\n      const config = await client.readResource(server.serverId, 'config://app-settings')\n      console.log('📄 Config:', config.contents[0]?.text)\n    }\n  } catch (error) {\n    console.error('❌ Error with server:', server.name, error)\n  }\n})\n\n// Connect and start discovery\nawait client.connect()\n\n// Graceful shutdown\nprocess.on('SIGINT', async () => {\n  await client.disconnect()\n  process.exit(0)\n})\n```\n\n## API Reference\n\n### McpMqttServer\n\n#### Constructor\n\n```typescript\nnew McpMqttServer(config: McpMqttServerConfig)\n```\n\n**Configuration:**\n\n```typescript\ninterface McpMqttServerConfig {\n  // MQTT connection settings\n  host: string\n  username?: string\n  password?: string\n\n  // Server identification (required)\n  serverId: string      // Unique server ID (MQTT client ID)\n  serverName: string    // Hierarchical server name (e.g., \"app/feature/server\")\n\n  // Server information (required)\n  name: string\n  version: string\n\n  // Optional configuration\n  description?: string     // Server description\n  capabilities?: {\n    prompts?: { listChanged?: boolean }\n    resources?: { subscribe?: boolean; listChanged?: boolean }\n    tools?: { listChanged?: boolean }\n  }\n  rbac?: {               // Optional role-based access control\n    roles: Array<{\n      name: string\n      description: string\n      allowed_methods: string[]\n      allowed_tools: string[] | \"all\"\n      allowed_resources: string[] | \"all\"\n    }>\n  }\n}\n```\n\n#### Methods\n\n##### `tool(name, description, inputSchema, handler)`\n\nRegister a tool that clients can call.\n\n```typescript\nserver.tool(\n  'calculate',\n  'Perform mathematical calculations',\n  {\n    type: 'object',\n    properties: {\n      expression: {\n        type: 'string',\n        description: 'Mathematical expression to evaluate'\n      },\n    },\n    required: ['expression'],\n  },\n  async ({ expression }) => {\n    try {\n      // Safe evaluation - implement your own parser\n      const result = evaluateExpression(expression)\n      return {\n        content: [{\n          type: 'text',\n          text: `${expression} = ${result}`,\n        }],\n      }\n    } catch (error) {\n      return {\n        content: [{\n          type: 'text',\n          text: `Error: ${error.message}`,\n        }],\n        isError: true,\n      }\n    }\n  }\n)\n```\n\n##### `resource(uri, name, handler, options?)`\n\nRegister a resource that clients can read.\n\n```typescript\nserver.resource(\n  'file://logs/app.log',\n  'Application Logs',\n  async () => {\n    const logs = await readLogFile()\n    return {\n      contents: [{\n        uri: 'file://logs/app.log',\n        mimeType: 'text/plain',\n        text: logs,\n      }],\n    }\n  },\n  {\n    description: 'Current application log entries',\n    mimeType: 'text/plain',\n  }\n)\n```\n\n##### `start()` / `stop()`\n\nControl server lifecycle.\n\n```typescript\nawait server.start()  // Start listening for requests\nawait server.stop()   // Gracefully shutdown\n```\n\n##### `getTopics()`\n\nGet MQTT topics used by this server.\n\n```typescript\nconst { request, response } = server.getTopics()\n```\n\n#### Events\n\n```typescript\nserver.on('ready', () => console.log('Server ready'))\nserver.on('error', (error) => console.error('Server error:', error))\nserver.on('closed', () => console.log('Server closed'))\n```\n\n### McpMqttClient\n\n#### Client Constructor\n\n```typescript\nnew McpMqttClient(config: McpMqttClientConfig)\n```\n\n**Configuration:**\n\n```typescript\ninterface McpMqttClientConfig {\n  // MQTT connection settings\n  host: string\n  clientId?: string\n  username?: string\n  password?: string\n  clean?: boolean\n  keepalive?: number\n  connectTimeout?: number\n  reconnectPeriod?: number\n\n  // Client information (required)\n  name: string\n  version: string\n\n  // Optional configuration\n  capabilities?: {\n    roots?: { listChanged?: boolean }\n    sampling?: Record<string, any>\n  }\n\n  // Advanced MQTT settings (optional)\n  will?: {\n    topic: string\n    payload: string | Buffer\n    qos?: 0 | 1 | 2\n    retain?: boolean\n  }\n  properties?: Record<string, any>\n}\n```\n\n#### Client Methods\n\n##### Connection Management\n\n```typescript\nawait client.connect()           // Connect to MQTT broker and start discovery\nawait client.disconnect()        // Disconnect from broker\nawait client.initializeServer(serverId)  // Initialize connection to a specific server\n```\n\n##### Tool Operations\n\n```typescript\nconst tools = await client.listTools(serverId)\nconst result = await client.callTool(serverId, toolName, args)\n```\n\n##### Resource Operations\n\n```typescript\nconst resources = await client.listResources(serverId)\nconst data = await client.readResource(serverId, uri)\n```\n\n##### Discovery\n\n```typescript\nconst discovered = client.getDiscoveredServers()\nconst connected = client.getConnectedServers()\n```\n\n#### Client Events\n\n```typescript\nclient.on('serverDiscovered', (server) => {\n  console.log('Found server:', server.name, 'ID:', server.serverId)\n})\n\nclient.on('serverInitialized', (server) => {\n  console.log('Connected to:', server.name)\n})\n\nclient.on('serverDisconnected', (serverId) => {\n  console.log('Server disconnected:', serverId)\n})\n\nclient.on('connected', () => console.log('Client connected'))\nclient.on('disconnected', () => console.log('Client disconnected'))\nclient.on('error', (error) => console.error('Client error:', error))\n```\n\n## MQTT Configuration\n\nThe SDK supports comprehensive MQTT connection options:\n\n```typescript\ninterface MqttConnectionOptions {\n  host: string               // Complete connection URL (e.g., ws://localhost:8083, wss://broker.emqx.io:8084)\n  clientId?: string          // Auto-generated if not provided\n  username?: string\n  password?: string\n  clean?: boolean            // Clean session (default: true)\n  keepalive?: number         // Keep-alive interval in seconds\n  connectTimeout?: number    // Connection timeout in milliseconds\n  reconnectPeriod?: number   // Reconnection period in milliseconds\n  will?: {                   // Last Will Testament\n    topic: string\n    payload: string | Buffer\n    qos?: 0 | 1 | 2\n    retain?: boolean\n  };\n}\n```\n\n### Connection Examples\n\nThe `host` parameter should be a complete connection URL:\n\n```typescript\n// WebSocket connections (browser)\nhost: 'ws://localhost:8083'\nhost: 'wss://broker.emqx.io:8084'\n\n// TCP connections (Node.js)\nhost: 'mqtt://localhost:1883'\nhost: 'mqtts://broker.emqx.io:8883'\n```\n\n## Advanced Usage\n\n### Accessing Underlying MQTT Client\n\nBoth server and client provide `getMqttClient()` method to access the underlying MQTT client for custom pub/sub operations:\n\n```typescript\nconst mqttClient = server.getMqttClient() // or client.getMqttClient()\nif (mqttClient) {\n  mqttClient.subscribe('custom/topic', { qos: 1 })\n  mqttClient.publish('custom/topic', 'Hello World')\n}\n```\n\n### Custom Tool Validation\n\n```typescript\nimport { z } from 'zod'\n\n// Define schema for tool parameters\nconst CalculateSchema = z.object({\n  operation: z.enum(['add', 'subtract', 'multiply', 'divide']),\n  a: z.number(),\n  b: z.number(),\n})\n\nserver.tool(\n  'calculate',\n  'Perform arithmetic operations',\n  {\n    type: 'object',\n    properties: {\n      operation: { type: 'string', enum: ['add', 'subtract', 'multiply', 'divide'] },\n      a: { type: 'number' },\n      b: { type: 'number' },\n    },\n    required: ['operation', 'a', 'b'],\n  },\n  async (params) => {\n    // Validate with Zod\n    const { operation, a, b } = CalculateSchema.parse(params)\n\n    let result: number\n    switch (operation) {\n      case 'add': result = a + b; break\n      case 'subtract': result = a - b; break\n      case 'multiply': result = a * b; break\n      case 'divide':\n        if (b === 0) throw new Error('Division by zero')\n        result = a / b\n        break\n    }\n\n    return {\n      content: [{\n        type: 'text',\n        text: `${a} ${operation} ${b} = ${result}`,\n      }],\n    }\n  }\n)\n```\n\n### Error Handling\n\n```typescript\n// Server-side error handling\nserver.tool('risky-operation', 'An operation that might fail', schema, async (params) => {\n  try {\n    const result = await performRiskyOperation(params)\n    return {\n      content: [{ type: 'text', text: `Success: ${result}` }],\n    }\n  } catch (error) {\n    return {\n      content: [{ type: 'text', text: `Operation failed: ${error.message}` }],\n      isError: true, // Mark as error response\n    }\n  }\n})\n\n// Client-side error handling\ntry {\n  const result = await client.callTool(serverName, 'risky-operation', params)\n  if (result.isError) {\n    console.error('Tool returned error:', result.content[0]?.text)\n  } else {\n    console.log('Success:', result.content[0]?.text)\n  }\n} catch (error) {\n  console.error('Tool call failed:', error.message)\n}\n```\n\n### Resource Streaming\n\n```typescript\n// Server: Streaming resource\nserver.resource(\n  'stream://live-data',\n  'Live Data Stream',\n  async () => {\n    const chunks = await getLiveDataChunks()\n    return {\n      contents: chunks.map((chunk, index) => ({\n        uri: `stream://live-data#${index}`,\n        mimeType: 'application/json',\n        text: JSON.stringify(chunk),\n      })),\n    }\n  }\n)\n\n// Client: Read streaming resource\nconst stream = await client.readResource(serverName, 'stream://live-data')\nfor (const content of stream.contents) {\n  const data = JSON.parse(content.text!)\n  processStreamChunk(data)\n}\n```\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Build in watch mode\nnpm run build:watch\n\n# Run tests\nnpm run test\n\n# Type checking\nnpm run typecheck\n\n# Linting\nnpm run lint\nnpm run lint:fix\n```\n\n## Protocol Details\n\n### MQTT Topic Structure\n\nThe SDK follows the official MCP over MQTT specification topic hierarchy:\n\n```text\nMCP over MQTT Topic Structure:\n\n🗂️ Server Topics:\n├── $mcp-server/{server-id}/{server-name}              # Control topic (initialization)\n├── $mcp-server/capability/{server-id}/{server-name}   # Capability change notifications\n└── $mcp-server/presence/{server-id}/{server-name}     # Server presence (online/offline)\n\n🗂️ Client Topics:\n├── $mcp-client/capability/{mcp-client-id}             # Client capability changes\n└── $mcp-client/presence/{mcp-client-id}               # Client presence\n\n🗂️ RPC Communication:\n└── $mcp-rpc/{mcp-client-id}/{server-id}/{server-name} # Bidirectional RPC communication\n\nExample Topics:\n- Control: $mcp-server/server-123/myapp/greeting-server\n- Capability: $mcp-server/capability/server-123/myapp/greeting-server\n- Presence: $mcp-server/presence/server-123/myapp/greeting-server\n- RPC: $mcp-rpc/client-456/server-123/myapp/greeting-server\n```\n\n### Message Flow\n\n1. **Service Discovery**: Client subscribes to `$mcp-server/presence/+/#`\n2. **Server Registration**: Server publishes presence to `$mcp-server/presence/{server-id}/{server-name}`\n3. **Initialization**: Client sends `initialize` request to `$mcp-server/{server-id}/{server-name}`\n4. **RPC Communication**: Bidirectional communication via `$mcp-rpc/{client-id}/{server-id}/{server-name}`\n\n### Error Codes\n\nThe SDK follows JSON-RPC 2.0 error codes:\n\n- `-32700`: Parse error\n- `-32600`: Invalid request\n- `-32601`: Method not found\n- `-32602`: Invalid params\n- `-32603`: Internal error\n- `-32000` to `-32099`: Implementation-defined server errors\n\n## Other Language SDKs\n\nLooking for MCP over MQTT support in other languages?\n\n- **[Python SDK](https://github.com/emqx/mcp-python-sdk)** - MCP over MQTT implementation for Python\n- **[Erlang SDK](https://github.com/emqx/mcp-mqtt-erl)** - MCP over MQTT implementation for Erlang\n- **[Paho MCP over MQTT](https://github.com/mqtt-ai/paho-mcp-over-mqtt)** - MCP over MQTT implementation in C\n- **[ESP MCP over MQTT](https://github.com/mqtt-ai/esp-mcp-over-mqtt)** - MCP over MQTT for ESP32/embedded devices in C\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes with tests\n4. Run the test suite (`npm test`)\n5. Submit a pull request\n\n## License\n\nApache License 2.0. See [LICENSE](LICENSE) file for details.\n","readmeFilename":"README.md"}