Skip to main content
Glama
tersePrompts

Frontdesk AI

Official
by tersePrompts

Frontdesk AI

One agent core. Three protocols. Zero config. Self-hosted personal AI concierge that books your Calendly, takes messages to your inbox, and answers questions about you — exposed to humans (chat UI), AI tools (MCP), and other agents (A2A) from a single ~250-line tool core.

Deploy with Vercel CI

Docs · Make it yours · Architecture · Contributing · Security · Agent protocols — A2A

Why this exists

Every personal site gets the same CTA: "email me and I'll get back to you." This repo is the upgrade — a streaming AI assistant that acts when visitors don't want to wait for a reply. But the real contribution is architectural:

The same assistant, three doors in. A single tool core (calendar, send_email) served simultaneously as a human chat UI, an MCP server for AI tool clients, and an A2A agent for agent-to-agent delegation. Most projects build these as three separate codepaths; this is a minimal, readable, deployable proof that one core can serve all three.

                 ┌──────────────────────────────┐
   Humans ─────▶ │  Chat UI  (POST /api/chat)   │
                 │                              │
  MCP clients ─▶ │  MCP      (POST /api/mcp)    │──▶ ┌───────────────┐
 (Claude, Cursor)│                              │    │    tool core   │──▶ Calendly
                 │                              │    │  calendar      │
    A2A agents ─▶│  A2A      (POST /a2a)        │──▶ │  send_email    │──▶ Email (+ ntfy)
                 └──────────────────────────────┘    └───────────────┘
                    one model, one persona,         ~250 lines, shared by all three
                    one provider key you choose

Related MCP server: Calendly MCP Server

Deploy in 2 steps

  1. Click DeployDeploy with Vercel (Vercel forks the repo and opens its UI for you).

    • Or: fork on GitHub → Import in Vercel → same thing.

  2. Add one LLM key in the Vercel project → Settings → Environment:

    Key

    Provider

    ANTHROPIC_API_KEY

    Claude (recommended default)

    OPENAI_API_KEY

    OpenAI

    LLM_API_KEY (+ LLM_BASE_URL)

    LiteLLM proxy or any OpenAI-compatible endpoint

    ZAI_API_KEY

    Z.ai GLM (legacy default)

    Then optionally:

    • RESEND_API_KEY + ASSISTANT_INTERNAL_EMAIL + ASSISTANT_EMAIL_FROM → take-a-message → email

    • CALENDLY_ACCESS_TOKEN → meeting booking

    • NTFY_TOPIC → visit push notifications

Hit Deploy. Done — the app runs fine with just one LLM key; missing keys disable the feature with a clear error instead of breaking chat.

Every free tier you need: pick your LLM (see Swap your LLM), Vercel hobby, Calendly free, Resend free, ntfy.sh free.

Swap your LLM

Frontdesk AI is provider-agnostic. The first provider with a key configured wins (lib/ai.ts):

# Claude
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-sonnet-4-5

# OpenAI
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o

# LiteLLM (or any OpenAI-compatible endpoint — Ollama, vLLM, Groq, Together…)
LLM_API_KEY=...
LLM_BASE_URL=https://your-proxy.example/v1
LLM_MODEL=your-model

# Z.ai GLM (default)
ZAI_API_KEY=...
ZAI_MODEL=glm-5.3-flash

No code changes — lib/persona.ts, lib/calendly.ts, and lib/sendEmail.ts are all provider-agnostic.

Make it yours

A full step-by-step walkthrough (persona, branding, assets, keys, deploy checklist) lives in docs/CUSTOMIZE.md → Make it yours — 10-minute checklist. Short version:

  • Persona — edit the OWNER object at the top of lib/persona.ts (name, bio, links, highlights). This is what the assistant actually knows and follows.

  • Branding — set NEXT_PUBLIC_ASSISTANT_NAME / NEXT_PUBLIC_ASSISTANT_TAGLINE, NEXT_PUBLIC_CONTACT_EMAIL, NEXT_PUBLIC_LINKEDIN / NEXT_PUBLIC_GITHUB, NEXT_PUBLIC_UPI_ID ("buy me a coffee"), and drop your photo at public/avatar.jpg + your CV at public/resume.pdf.

  • Keys/config — everything is env-driven; nothing to fork-edit.

One assistant, three protocols

The same tools (lib/calendly.ts, lib/sendEmail.ts) and the same model are exposed three ways. For the full picture of why this design wins and how to add a new capability across all three surfaces, read docs/ARCHITECTURE.md.

1. Human chat UI (REST)

POST /api/chat — the streaming chat on the homepage. Vercel AI SDK streamText, multi-step tool use (maxSteps=20), rate-limited 40 req/min/IP. No auth (public).

2. MCP server (for AI tool clients)

POST /api/mcp — an MCP Streamable HTTP server exposing calendar and send_email as tools. Any MCP client (Claude, Cursor, opencode, etc.) can point at it and book meetings / take messages directly.

// MCP client config (e.g. claude_desktop_config / .mcp.json)
{
  "mcpServers": {
    "concierge": {
      "type": "http",
      "url": "https://<your-app>.vercel.app/api/mcp",
      "headers": { "Authorization": "Bearer <your LLM API key>" }
    }
  }
}

Auth: Authorization: Bearer <any configured provider key> · rate-limited 20 req/min/IP. GET /api/mcp returns a human-readable capability summary.

3. A2A agent (for agent-to-agent delegation)

POST /a2a — an A2A (Agent-to-Agent) JSON-RPC endpoint with streaming. Other AI agents discover it via the Agent Card at /.well-known/agent-card.json and delegate full tasks: book a meeting, leave a message, or ask a question.

# Discover the agent
curl https://<your-app>.vercel.app/.well-known/agent-card.json

# Send a task (blocking)
curl -X POST https://<your-app>.vercel.app/a2a \
  -H "Authorization: Bearer $YOUR_LLM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "SendMessage",
    "params": { "message": { "role": "user",
      "parts": [{ "type": "text", "text": "Book a 30-minute meeting on Thursday" }] } },
    "id": "req-1"
  }'

Auth: Authorization: Bearer <any configured provider key> · rate-limited 20 req/min/IP · supports SendMessage, SendStreamingMessage, GetTask, CancelTask.


Features

  • Streaming chat — token-by-token streaming; raw text renders instantly mid-stream, then react-markdown appears once the reply settles (no UI jank).

  • Bring your own LLM — Claude, OpenAI, LiteLLM/any OpenAI-compatible endpoint, or Z.ai GLM. First key configured wins.

  • Calendly schedulingcalendar tool: list event types, check availability, book an invitee, cancel, confirm account.

  • Take-a-message emailsend_email tool delivers to your internal inbox; the envelope address is never exposed and never accepts an external recipient.

  • Two conversation modesChat (assistant & peer) and Recruiter (CV walkthrough) personas; transcripts persist per-mode in localStorage.

  • Privacy by default — the persona never reveals your phone or internal email, refuses jailbreak attempts, and collects name + email confirmation before any booking/send.

  • Visitor tracking/api/track best-effort: ip-api geolocation → ntfy push + email. Never blocks the visitor.

How it works

Visitor
   │  POST /api/chat  {"messages", "mode"}
   ▼
app/api/chat/route.ts         Vercel AI SDK streamText (maxSteps=20)
   │  model: lib/ai.ts (Claude / OpenAI / LiteLLM / Z.ai)   rate-limit: 40 req/min/IP
   │  tools: calendar · send_email          (Zod-validated)
   ▼
lib/calendly.ts   ── CALENDLY_ACCESS_TOKEN ──> Calendly API
lib/sendEmail.ts  ── RESEND_API_KEY        ──> Resend API

lib/persona.ts supplies the persona + guardrails; the chat route sanitizes replayed tool-call history so streamText never chokes on a tool invocation missing its result.

Tech stack

Next.js 15 (App Router, React 19, TypeScript) · Vercel AI SDK v4 · Claude / OpenAI / LiteLLM / Z.ai GLM (provider-agnostic) · Calendly API · Resend · ntfy.sh · react-markdown + remark-gfm · Vitest + jsdom + Testing Library · GitHub Actions · @modelcontextprotocol/sdk (MCP) · @a2a-js/sdk (A2A).

Developing

npm install
cp .env.example .env   # add your keys
npm run dev            # http://localhost:3000

Script

What it does

npm run dev

dev server

npm run build / npm run start

production build / serve

npm run typecheck

tsc --noEmit (includes test files)

npm test

Vitest suite

Configuration

Full reference in .env.example:

Variable

Required

Purpose

ZAI_API_KEY

or one below

Z.ai GLM API key (legacy default)

OPENAI_API_KEY / OPENAI_MODEL

or one above

OpenAI

ANTHROPIC_API_KEY / ANTHROPIC_MODEL

or one above

Anthropic Claude

LLM_API_KEY / LLM_BASE_URL / LLM_MODEL

or one above

LiteLLM / OpenAI-compatible

RESEND_API_KEY

email

Resend API key — https://resend.com/api-keys

ASSISTANT_INTERNAL_EMAIL

email

Inbox the assistant emails messages and visit alerts to

ASSISTANT_EMAIL_FROM

email

Verified sender, e.g. Concierge <onboarding@resend.dev>

CALENDLY_ACCESS_TOKEN

calendar

Calendly PAT — https://calendly.com/integrations/api_webhooks

NTFY_TOPIC

alerts

ntfy.sh topic for push notifications

NEXT_PUBLIC_ASSISTANT_NAME / NEXT_PUBLIC_ASSISTANT_TAGLINE

branding

Header title / tagline

NEXT_PUBLIC_CONTACT_EMAIL / NEXT_PUBLIC_LINKEDIN / NEXT_PUBLIC_GITHUB

branding

Header contact links

NEXT_PUBLIC_UPI_ID

branding

"Buy me a coffee" UPI id (empty hides the button)

NEXT_PUBLIC_SITE_URL

branding

Fallback origin for the copied agent set-up

ASSISTANT_NAME / ASSISTANT_TAGLINE

metadata

<title> / meta description

ASSISTANT_TIMEZONE

optional

IANA timezone for availability display

MCP_ENABLED / A2A_ENABLED

optional

Feature flags (default: enabled)

VERCEL_TOKEN / VERCEL_PROJECT_ID

deploy scripts

See below

Deploying

deploy.sh (macOS/Linux) and deploy.ps1 (Windows) each run a local build check, deploy --prod to Vercel, and report the deployment state. They temporarily hide .git during the deploy as a workaround for Vercel's seat-verification block, then restore it. Both resolve the project for status reporting from VERCEL_PROJECT_ID or .vercel/project.json (from vercel link); set your env vars in the Vercel dashboard regardless.

export VERCEL_TOKEN="<your token>"
./deploy.sh

Testing

Vitest tests cover the Calendly tool (booking, availability, cancellation), the email tool (env gating, ntfy best-effort), persona safety rules, /api/track, the chat UI streaming-render behavior (raw text while streaming, markdown on settle, localStorage only when idle), the MCP server (tool listing + execution through the SDK's in-memory transport), and the A2A agent (Agent Card shape, intent routing, executor event lifecycle).

Project structure

app/
  api/
    chat/route.ts     streaming chat (tools + model + rate limit)
    mcp/route.ts      MCP Streamable HTTP server (calendar + send_email tools)
    track/route.ts    visitor tracking (ntfy push + email, best-effort)
  a2a/route.ts        A2A JSON-RPC endpoint (send/get/cancel task, streaming)
  .well-known/agent-card.json/route.ts  A2A Agent Card discovery
  page.tsx            chat UI (modes, streaming render, localStorage)
  Markdown.tsx        react-markdown wrapper
lib/
  ai.ts               provider-agnostic model factory (Claude/OpenAI/LiteLLM/Z.ai)
  calendly.ts         Calendly API wrapper (book/cancel/availability)
  sendEmail.ts        Resend email wrapper
  mcp/server.ts       MCP tool registry (calendar, send_email)
  a2a/executor.ts     A2A agent executor + intent routing
  a2a/types.ts        A2A Agent Card definition
  persona.ts          persona + privacy guardrails (editor-friendly template)
  rateLimit.ts        in-memory per-IP rate limiter
*.test.ts / *.test.tsx    Vitest suites
.github/workflows/ci.yml  typecheck + tests + build on push/PR
deploy.ps1 / deploy.sh    Vercel production deploy scripts

FAQ

Do I need to be a developer to set this up? No. Deploy-to-Vercel, add one LLM key, edit OWNER in lib/persona.ts and a few branding env vars. The 10-minute checklist covers it end-to-end. Editing lib/persona.ts is plain text — no framework knowledge required.

Which model should I pick? Whatever you already have a key for — Claude, OpenAI, LiteLLM (which also fronts Groq, Ollama, vLLM, Together, custom endpoints), or Z.ai GLM. Claude is a good default for concierge-style tool use. You can switch later without code changes.

Does this cost money? The app itself runs on Vercel's free hobby tier. All integrations (Calendly, Resend, ntfy.sh) have free tiers. You pay for LLM tokens — a personal site's chat traffic is pennies to a couple of dollars a month depending on the model.

Is it safe to expose on a public site? The persona never reveals your phone or internal email, refuses jailbreak attempts, and collects name+email confirmation before any booking or send. Email always routes to your internal inbox (never an external recipient), rate limits apply per IP, and MCP/A2A endpoints additionally require the bearer key. See SECURITY.md.

What's the difference between the three doors (chat / MCP / A2A)? The chat UI is for humans visiting your site. MCP is for AI tool clients (Claude, Cursor, opencode) that want to call calendar / send_email as tools. A2A is for other AI agents to hand you a whole task end-to-end. Same tools, same model, three protocols — see ARCHITECTURE.md.

Do I need a Calendly/Resend account just to try it? No. The app runs with one LLM key alone. Missing integration keys disable that feature with a clear error instead of breaking chat.

Can I extend it with my own tools? Yes. Add a lib/yourTool.ts with an env-gated run() function, register it as a chat tool, an MCP tool, and an A2A route case — ARCHITECTURE.md#requirements-for-adding-a-capability spells it out.

Security notes

  • Credentials live only in .env (gitignored); .env.example ships placeholders. sendEmail.ts and track/route.ts never fall back to a hardcoded inbox/topic — a misconfigured fork fails loudly instead of emailing the original author's address.

  • /api/* is set to no-store; security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy) are applied site-wide.

  • Rate limits: /api/chat (40/min/IP), /api/track (24/min/IP), /api/mcp (20/min/IP), /a2a (20/min/IP).

  • MCP and A2A require Authorization: Bearer <a configured provider key>; the chat UI is intentionally public.

  • The persona never reveals your phone or internal email, and refuses jailbreak attempts.

  • react-markdown renders without rehype-raw, so raw HTML in model output is inert.

  • Visit/push/email integrations are best-effort by design — failures never block the visitor's response.

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Agent-native MCP server for Carly, the AI scheduling assistant. Exposes 11 tools to read and manage booking pages, event types, calendars, bookings, and available slots. Allows you to manage and create booking pages from the command line.
    13
    14
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Multi-server MCP tool bridge that exposes banking, email, calendar, and custom API tools for AI clients via the Model Context Protocol.
    2,499
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Local MCP server for reading/sending email via Gmail and managing Google Calendar events, enabling an AI agent to handle email and calendar operations through natural language.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tersePrompts/frontdesk-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server