integration · hermes
Hermes
MailMolt's TypeScript SDK plugs straight into a Hermes agent as a tool registry. Inbound mail routes through a webhook receiver into agent.run(); outbound goes through governed quotas, the approval queue, and trust scoring.
Install
pnpm add @mailmolt/sdk @hermesai/runtime
# Capture an API key for this agent:
curl -X POST https://api.mailmolt.com/v1/agents/register \
-H 'Content-Type: application/json' \
-d '{"name": "hermes-agent", "owner_hint": "ops bot"}'Expose MailMolt as Hermes tools
import { MailMolt } from '@mailmolt/sdk';
import { defineTool } from '@hermesai/runtime';
const mm = new MailMolt({ apiKey: process.env.MAILMOLT_API_KEY! });
export const sendEmail = defineTool({
name: 'send_email',
description: 'Send an email from this Hermes agent.',
schema: {
to: 'string',
subject: 'string',
text: 'string',
},
async run({ to, subject, text }) {
const r = await mm.sendMessage({ to, subject, text });
return { id: r.id, status: 'sent' };
},
});
export const searchInbox = defineTool({
name: 'search_inbox',
description: 'Semantic search across the inbox.',
schema: { query: 'string' },
async run({ query }) {
const r = await mm.search({ q: query, limit: 5 });
return { hits: r.data };
},
});
export const replyTo = defineTool({
name: 'reply_to',
description: 'Reply in-thread to a message.',
schema: { message_id: 'string', text: 'string' },
async run({ message_id, text }) {
const r = await mm.replyMessage({ messageId: message_id, text });
return { id: r.id };
},
});Wire tools into a Hermes agent
import { Agent } from '@hermesai/runtime';
import { sendEmail, searchInbox, replyTo } from './mailmolt-tools';
export const support = new Agent({
name: 'support',
model: 'claude-sonnet-4-6',
systemPrompt: `You handle support email. Search before replying.
Keep replies under 4 sentences. Never promise refunds without a ticket.`,
tools: [sendEmail, searchInbox, replyTo],
});
await support.run(
'A new email arrived asking about API rate limits. Reply with the docs link.'
);Receive mail as agent input
import { Hono } from 'hono';
import { support } from './agent';
// MailMolt posts message.received whenever new mail arrives. Wire it
// straight into the Hermes agent runtime.
const app = new Hono();
app.post('/webhooks/mailmolt', async (c) => {
const evt = await c.req.json();
if (evt.event_type === 'message.received') {
const m = evt.data.message;
await support.run(
`New email from ${m.from.email}: ${m.subject}\n\n${m.preview}\n\nReply if relevant; otherwise note "skipped" and move on.`
);
}
return c.json({ ok: true });
});
// Register once:
await mm.createWebhook({
url: 'https://your-host.com/webhooks/mailmolt',
eventTypes: ['message.received'],
});
export default app;Human-in-the-loop on sensitive sends
// Approval-gated sends — pause humans-in-the-loop in oversight.
await mm.sendMessage({
to: 'finance@bigco.com',
subject: 'Refund request — ticket #4892',
text: 'Refunding $4,200 per the customer note.',
require_approval: true, // queues until a human approves
});
// In oversight: /flagged shows the pending send with a 7-day expiry,
// audit trail, and an approve / reject button. Approval-gated sends
// still return an id; the queue resolves out-of-band.Notes
- The SDK is fully typed — Hermes' tool schemas inherit MailMolt's parameter types so the model gets accurate signatures, not stringly-typed JSON.
- Hermes loops can fan out — mint one MailMolt agent per Hermes role (support, billing, dev-ops) so trust scores stay attributable. Per-agent caps surface in the run-time as
RateLimitError. - Long-running Hermes sessions stay coherent across replies because each
replyMessageuses RFC2822 In-Reply-To/References. - Persist the webhook secret in Hermes's env, then verify
X-MailMolt-Signatureon every inbound — the SDK exposesverifyWebhook(req, secret).
Runnable example: sdks/typescript/examples/hermes-runtime.ts · webhook reference