AI scheduling agent. CC it into an email thread and it coordinates a meeting — checks your calendar, proposes times, and books when confirmed.
- You're emailing someone about meeting up
- CC
sched-agent@agentmail.tointo the thread - The agent reads the conversation, checks your Google Calendar, and proposes 2-3 times
- The other party replies to pick a time
- The agent creates the calendar event and confirms to everyone
You Guest Agent
│ │ │
│── email ───────────>│ │
│<──── reply ─────────│ │
│── reply + CC agent─>│───────────────────>│
│ │ │── check calendar
│ │<── propose times ──│
│ │── "Tuesday works"─>│
│ │ │── create event
│<── "Booked!" ───────│<── "Booked!" ──────│
The agent only manages your calendar. Guests don't need accounts.
Email the agent directly for the first time and it sends a Google Calendar OAuth link. Once connected, it can manage your calendar.
- Runtime: Node.js + TypeScript (ESM)
- Server: Hono
- AI: Claude Haiku 4.5 via
@anthropic-ai/claude-agent-sdk - Calendar: Google Calendar via Composio
- Email: AgentMail
- Database: PostgreSQL (Supabase) via Prisma
- Hosting: Railway
| File | Purpose |
|---|---|
src/index.ts |
Hono server, webhook handler, routing logic |
src/agent.ts |
Claude agent — system prompt, email processing, tool wiring |
src/tools.ts |
MCP mail tools (reply, save_preferences) |
src/composio.ts |
Google Calendar integration via Composio |
src/mail.ts |
AgentMail client wrapper |
src/onboarding.ts |
Onboarding flow — welcome email + OAuth link |
src/onboarding-tools.ts |
Onboarding-specific agent tools |
src/utils.ts |
Email parsing, participant classification |
src/types.ts |
Shared types and thread formatting |
src/db.ts |
Prisma client |
npm installCreate a .env file:
| Variable | Required | Description |
|---|---|---|
AGENTMAIL_API_KEY |
Yes | From AgentMail |
COMPOSIO_API_KEY |
Yes | From Composio |
CLAUDE_CODE_OAUTH_TOKEN |
Yes | Claude Code OAuth token (or ANTHROPIC_API_KEY) |
INBOX_USERNAME |
Yes | Inbox name (e.g. sched-agent -> sched-agent@agentmail.to) |
WEBHOOK_URL |
Yes | Webhook URL (ngrok for local, Railway URL for prod) |
DATABASE_URL |
Yes | PostgreSQL connection string (pooled) |
DIRECT_URL |
Yes | PostgreSQL direct connection string |
PORT |
No | Default 3000 |
# Terminal 1: expose localhost
ngrok http 3000
# Terminal 2: dev server (auto-restarts on changes)
npm run devSet WEBHOOK_URL in .env to the ngrok HTTPS URL. The server registers the inbox and webhook on startup.
The app is deployed on Railway as a long-running Node.js service using Docker.
The Claude Agent SDK spawns a subprocess that can run for 30+ seconds per request. Vercel's serverless functions time out too quickly. Railway runs the app as a persistent server — no timeout issues.
The Dockerfile handles:
- OpenSSL for Prisma
- Non-root user — Claude Code refuses to run as root (security restriction)
- Prisma client generation at build time
# Install Railway CLI
npm i -g @railway/cli
# Login
railway login
# Init project (first time)
railway init
# Deploy
railway upSet all env vars from the table above on Railway:
railway variables set KEY=value ...Set WEBHOOK_URL to https://<your-app>.up.railway.app/webhooks.
railway domain| Command | Purpose |
|---|---|
npm run dev |
Dev server with file watching |
npm start |
Production server |
npm run chat |
CLI chat with the agent (no email) |
npm run simulate |
E2E simulation with real AgentMail |
GET /— Health checkPOST /webhooks— AgentMail webhook (message.received)POST /users— Register a new userGET /oauth/callback— Composio OAuth redirect