A deliberately minimal AI agent on Cloudflare Workers, built on Think (Agents SDK) and reachable from Telegram.
It's a thin skeleton — a PersonalAgent class (src/agent.ts) wired up by a small Worker entry (src/index.ts), with tools, MCP servers, Telegram helpers, and the skill catalog split into their own small modules. It's meant to be read, trimmed, and extended: tools are a few lines each and the model discovers them from their descriptions. Keep what you need, delete what you don't, add your own.
- Telegram chat with persistent per-conversation memory (Durable Object SQLite).
- Web search via Tavily —
web_search. - Web browsing via Chrome DevTools / Browser Run —
browser_search,browser_execute. - Send files — send any file from the workspace, a public URL, or a live page screenshot to the chat (
send_file). - Smart home — control Home Assistant devices over MCP.
- Workspace tools — files + sandboxed bash, built into Think.
- Skills — on-demand playbooks the model activates per task; ships with
bookmark(getSkills()).
Want another capability? Add a
tool()ingetTools(), or another MCP server inconfigureSession(). Don't need one? Delete its few lines.
Needs a Cloudflare account and Node. Run npm install, then create a .env (gitignored) with the secrets for the pieces you want.
Telegram — the chat channel (required)
- Create a bot with @BotFather; copy the token and username.
- Add to
.env:TELEGRAM_BOT_TOKEN=123456:ABC... TELEGRAM_BOT_USERNAME=your_bot # without the @ TELEGRAM_WEBHOOK_SECRET_TOKEN=any-long-random-string - Register the webhook by opening
https://<your-url>/setuponce (see Run / Deploy).
The bot replies to direct messages and @mentions. Edit getSystemPrompt() to change its personality.
Workers AI — the model
Already wired through the AI binding (getModel() → @cf/moonshotai/kimi-k2.6). No key, but run npx wrangler login — the binding runs against your account even in local dev. Swap the model string in getModel() to use a different one.
Web search — Tavily
- Get a key at tavily.com.
- Add to
.env:TAVILY_API_KEY=tvly-...
Browser & screenshots — Browser Run
No keys. Uses the BROWSER and LOADER (Worker Loader) bindings already in wrangler.jsonc; wrangler dev provisions a browser automatically. Just needs Browser Run available on your account.
Home Assistant — smart home (optional)
- In Home Assistant, add the Model Context Protocol Server integration and expose the devices you want to Assist.
- Create a Long-Lived Access Token (Profile → Security).
- HA must be reachable over public HTTPS (Nabu Casa or a Cloudflare Tunnel) — LAN addresses are blocked.
- Add to
.env:HA_MCP_URL=https://<your-home-assistant>/api/mcp HA_TOKEN=<long-lived access token>
Start the dev server + tunnel
npm run devThis starts the Worker and a Cloudflare Quick Tunnel, printing a public https://<name>.trycloudflare.com URL. Open https://<name>.trycloudflare.com/setup once to point Telegram at it, then DM your bot.
Quick Tunnel URLs are ephemeral — if it changes, just open /setup again.
Use the Deploy to Cloudflare button above for one-click setup — it clones the repo, provisions the Durable Object and Workers AI, and prompts for the secrets listed in .env.example. Or deploy from the CLI:
Deploy to a stable workers.dev URL
Set each secret, then deploy:
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TELEGRAM_BOT_USERNAME
npx wrangler secret put TELEGRAM_WEBHOOK_SECRET_TOKEN
npx wrangler secret put TAVILY_API_KEY
npx wrangler secret put HA_MCP_URL
npx wrangler secret put HA_TOKEN
npm run deployThen open https://personal-agent.<your-subdomain>.workers.dev/setup.
All six are listed in
secrets.required(wrangler.jsonc), so deploy fails if any is missing — drop the ones for capabilities you removed.
/setupand/resetare unauthenticated — add a guard (e.g. a secret query param) before exposing them publicly.
| Route | Purpose |
|---|---|
GET /setup |
Register the Telegram webhook at the current host. |
GET /reset |
Wipe the conversation and Chat SDK state. |
/messengers/telegram/webhook |
Telegram delivery (set automatically by /setup). |
src/
index.ts — Worker entry: routes (/setup, /reset, webhook) and Durable Object exports
agent.ts — the PersonalAgent class: model, prompt, MCP session, progress hooks, tool/skill wiring
tools.ts — custom tools (web_search, send_file) plus the browser tools
mcps.ts — MCP servers connected each turn (Home Assistant)
telegram.ts — Telegram Bot API helpers: chat-id decode, file upload, progress messages
skills.ts — skill catalog (the bookmark skill)
- Add a tool → a
tool({ description, inputSchema, execute })entry ingetTools()(tools.ts). - Add a service → an entry in
mcpServers()(mcps.ts). - Per-chat memory → remove
conversation: "self"ingetMessengers()(agent.ts). - Another channel → add an adapter (Slack, Discord, …) beside
telegramingetMessengers().
After changing bindings in wrangler.jsonc, run npm run cf-typegen.
