f74f90a2f0e5ab1370f9e045971713142803b5b3
The global express.json() middleware at line 77 was parsing the body
into a JS object before the route-level express.raw() could capture
the raw Buffer. When WHATSAPP_APP_SECRET is set and a signature is
present, crypto.createHmac().update(req.body) received an Object
instead of Buffer, throwing TypeError and crashing the process.
Fix: register app.use('/webhook/whatsapp', express.raw({ type: '*/*' }))
before app.use(express.json()) so the raw body is preserved for HMAC.
Post-deploy verification: all 7 webhook tests pass, pod 0 restarts.
Related: SquareMCP/2026-06-10-twilio-whatsapp-webhook-deployment.md
Hermes MCP — SquareMCP Gateway
Hermes is the MCP server powering SquareMCP. It exposes 51 tools across 11 platforms (email, Obsidian, WhatsApp, LinkedIn, TikTok, Facebook, Instagram, Twitter, Telegram, Discord, Snapchat) over Streamable HTTP, with per-user authentication, OAuth 2.0, and multi-tenant credential isolation.
Production endpoint:
https://hermes.squaremcp.com/mcp
Quick connect
claude.ai
- Settings → MCP Servers → Add → enter
https://hermes.squaremcp.com - Complete the OAuth popup (login with your SquareMCP credentials)
- Click "Connect MCP client"
Claude Desktop
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"squaremcp": {
"type": "http",
"url": "https://hermes.squaremcp.com/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}
Codex CLI
# ~/.codex/config.toml
[mcp_servers.squaremcp]
url = "https://hermes.squaremcp.com/mcp"
headers = { Authorization = "Bearer YOUR_TOKEN" }
opencode
{
"mcp": {
"squaremcp": {
"type": "remote",
"url": "https://hermes.squaremcp.com/mcp",
"headers": { "x-api-key": "YOUR_API_KEY" }
}
}
}
Get your token from the SquareMCP dashboard → Connect MCP Client.
Architecture
See ARCHITECTURE.md for the full architecture with diagrams.
nginx ingress (TLS)
│
┌────────────┼────────────┐
│ │ │
hermes-mcp squaremcp-app squaremcp-docs
:3456 :8080 :80
(MCP API) (SaaS UI) (Docs)
│
┌─────────┼─────────┐
│ │ │
MySQL 8 Redis 7 /vaults
(creds + (cache + (Obsidian)
billing) DLQ)
Stack: TypeScript / Node.js, Express, MySQL 8, Redis 7, MicroK8s, Docker Auth: JWT session cookies + OAuth 2.0 PKCE + API key Platform clients: 11 platforms, 51 MCP tools
Platforms
| Platform | Tools |
|---|---|
| Email (IMAP/SMTP) | search, read, send, draft, folders |
| Obsidian | search, read, append, update, sync |
| WhatsApp Business | send, template, list templates |
| profile, post, search connections, message, video | |
| TikTok | profile, creator info, upload video, status |
| page, posts, post, photo, video | |
| profile, media, post, reel | |
| Twitter/X | profile, tweets, search, tweet, video |
| Telegram | me, send, photo, updates, chat |
| Discord | me, guilds, channels, send, messages |
| Snapchat | me, ad accounts, create snap |
Authentication
Hermes accepts (in priority order):
x-api-keyheader — global superadmin or per-customer keyAuthorization: Bearer <token>— JWT or OAuth access tokenCookie: session=<JWT>— web session (set by/api/auth/loginor/login)
Transports
| Transport | URL |
|---|---|
| Streamable HTTP (preferred) | https://hermes.squaremcp.com/mcp |
| Legacy SSE | https://hermes.squaremcp.com/sse |
Client setup guides
- Codex CLI
- Claude Code / CLI agents
- opencode
- ChatGPT Custom GPT
- Social publishing (TikTok / Facebook)
Local development
npm install
cp .env.example .env # fill in credentials
npm run dev
curl http://localhost:3456/health
Server runs on port 3456 by default.
Deployment
See DEPLOY.md for the full deployment runbook.
The short version:
npm run build
docker build -t localhost:32000/hermes-mcp:latest .
docker push localhost:32000/hermes-mcp:latest
# update sha256 digest in hermes-k8s.yaml
microk8s kubectl apply -f hermes-k8s.yaml
Description
Languages
TypeScript
75.8%
JavaScript
9%
HTML
7.5%
Python
3.9%
CSS
2.9%
Other
0.9%