abjt01/asyncMCP
If you are the rightful owner of asyncMCP and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
AsyncMCP is a minimal JavaScript MCP server designed for easy integration and extensibility, providing a secure and efficient environment for tool execution.
AsyncMCP — A Minimal JavaScript MCP Server 🚀
AsyncMCP is a lean, modern MCP server for JavaScript teams: a tiny, readable Node.js (ES modules) codebase that speaks clean JSON‑RPC 2.0 over STDIO for MCP’s core methods—“initialize”, “tools/list”, and “tools/call”—so tools are discoverable and callable with zero friction. It boots fast, keeps the surface area small, and stays beginner‑friendly without sacrificing clarity. Start minimal for local dev, then flip on the optional HTTP adapter when MERN integration calls for it. No fluff, no Python—just straightforward tooling that’s easy to extend, safe by default, and built to get real work done.
Overview 📖
- Goal: Provide a compact, secure, and extensible MCP-style tool server that runs locally and can be integrated with MCP-capable desktop clients or MERN stacks.
- Use-cases: Allow LLMs (ChatGPT Desktop with MCP, Claude Desktop, custom agents) to call small JS functions or sandboxed commands safely.
- Principles: minimal core, explicit tool registry, safe defaults, readable code, zero Python, easy to adopt.
Repository Structure 📁
asyncMCP/
├── LICENSE
├── package.json
├── config/
│ ├── asyncmcp.json
│ └── .env.example
├── examples/
│ └── demoClient.js
└── src/
├── auth(to setup)/
│ └── jwt.js
├── core/
│ ├── dispatcher.js
│ ├── protocol.js
│ ├── sandbox.js
│ ├── server.js
│ └── validator.js
├── http(transport via http)/
│ └── httpServer.js
├── middleware/
│ ├── logger.js
│ └── rateLimiter.js
├── tools/
│ ├── calc.js
│ ├── echo.js
│ ├── index.js
│ ├── time.js
│ └── whatsapp(mock).js
└── utils/
└── redactor.js
Getting Started 🚀
Prerequisites ✅
- Node.js 18+ installed and available in PATH.
Install 📦
Clone the repository and install dependencies:
git clone https://github.com/abjt01/asyncMCP && cd asyncMCP && npm install
Development Workflow & Commands 🛠️
- Install: npm install
- STDIO server: npm start (node src/core/server.js)
- Optional HTTP: npm run http (if a script is present; otherwise node src/http/httpServer.js)
- Recommended: use console.error for server logs; keep stdout clean for JSON replies.
Initialize ⚙️
Sends MCP initialize and prints the reply:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05"}})+"\n");cp.stdin.end();'
Returns: protocolVersion and serverInfo.
List Tools 📋
Enumerates echo, time, calc, whatsapp:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:2,method:"tools/list"})+"\n");cp.stdin.end();'
Returns: the tools array.
Call Echo 🗣️
Simple echo back utility:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:3,method:"tools/call",params:{name:"echo",arguments:{message:"Hello AsyncMCP!"}}})+"\n");cp.stdin.end();'
Returns: the echoed message.
Call Time ⏰
Returns ISO and unix timestamp:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:4,method:"tools/call",params:{name:"time"}})+"\n");cp.stdin.end();'
Yields: server time data.
Call Calc ➕
Sanitized math evaluation:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:5,method:"tools/call",params:{name:"calc",arguments:{expression:"2 + 3 * 4"}}})+"\n");cp.stdin.end();'
Returns: 14 with the original expression.
Note: Only digits, +, -, *, /, parentheses, and whitespace are allowed; invalid characters produce an error.
Call WhatsApp (Mock-Default) 📱
Mock send that succeeds without credentials:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:6,method:"tools/call",params:{name:"whatsapp",arguments:{phone:"+911234567890",message:"Hello from AsyncMCP!"}}})+"\n");cp.stdin.end();'
Returns: success:true and a mock messageId.
Call WhatsApp (Live Mode) 📲
Provide token and phone_id; if the Graph API responds, you’ll see mode:"live" and apiId; on failure, the tool returns mode:"mock_fallback" with error details to keep development unblocked:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:7,method:"tools/call",params:{name:"whatsapp",arguments:{phone:"+911234567890",message:"Hi from live",token:"EAAG...",phone_id:"1234567890"}}})+"\n");cp.stdin.end();'
Error-Path Tests ⚠️
Unknown Method:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:8,method:"nope/method"})+"\n");cp.stdin.end();'
Returns: -32601 Method not found.
Unknown Tool:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write(JSON.stringify({jsonrpc:"2.0",id:9,method:"tools/call",params:{name:"not-a-tool"}})+"\n");cp.stdin.end();'
Returns: -32601 Tool not found.
Parse Error:
node -e 'const cp=require("child_process").spawn("node",["src/core/server.js"],{stdio:["pipe","pipe","inherit"]});cp.stdout.on("data",d=>process.stdout.write(d));cp.stdin.write("{bad json}\n");cp.stdin.end();'
Returns: -32700 Parse error.
Transports 🌐
STDIO (Default)
The server reads one JSON per line on stdin and writes one JSON per line to stdout. This matches local MCP desktop clients that spawn subprocesses.
HTTP 🌍
src/http/httpServer.js provides a simple Express-based adapter (POST /mcp). Keep it disabled unless required by a frontend or remote agent.
Sandbox — Purpose and Usage🛡️
Gives asyncMCP a safe “escape hatch” for running external commands with enforced timeouts and captured stdout/stderr, making it ideal for future system‑level tools like ping, traceroute, or lightweight CLI utilities.
For the current minimal toolset, sandboxing isn’t required—but when adding untrusted or OS‑touching tools, implement buildSandboxCommand(args) to return { bin, argv }, and let the dispatcher invoke runSandboxed so each command runs in a contained subprocess with controlled execution and predictable outputs.
FAQ ❓
Q: Do I need HTTP for ChatGPT? A: No — ChatGPT Desktop (Plus) and Claude Desktop (free) can spawn your STDIO server directly via the asyncmcp.json discovery file.
Q: Can ChatGPT Free use AsyncMCP? A: ChatGPT Free (desktop) may not expose the "Add MCP Server" UI. Claude Desktop (free) generally does. For ChatGPT Desktop, you'd typically need Plus while the feature is in beta.
Q: Is it safe to run WhatsApp live from my local machine? A: Yes in dev, but guard your tokens; keep them in .env and do not commit them. For production, authenticate requests and limit allowed phone numbers.
Q: Why JSON-RPC 2.0? A: It’s the standard MCP clients expect. Keep jsonrpc: "2.0" in every request.
License 📜
MIT
Acknowledgements 🙏
This project draws inspiration from the excellent FastMCP work by Jason Lowin, particularly its clear framing of MCP server composition and transport patterns; grateful credit to the jlowin/fastmcp for influencing this minimal, readable design.