cloudflare-agentic-inbox
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare Agentic Inbox
Cloudflare Agentic 收件箱
Skill by ara.so — AI Agent Skills collection.
Agentic Inbox is a self-hosted email client with an AI agent, running entirely on Cloudflare Workers. It uses Email Routing for receiving emails, Durable Objects with SQLite for per-mailbox storage, R2 for attachments, and Workers AI with the Cloudflare Agents SDK for AI-powered email assistance.
由ara.so提供的Skill — AI Agent技能合集。
Agentic Inbox是一款搭载AI Agent的自托管邮件客户端,完全运行在Cloudflare Workers上。它使用Email Routing接收邮件,通过搭配SQLite的Durable Objects实现每个邮箱的存储,利用R2存储附件,并借助Workers AI和Cloudflare Agents SDK提供AI驱动的邮件辅助功能。
Installation & Deployment
安装与部署
Quick Deploy (Recommended)
快速部署(推荐)
-
Deploy via button (provisions R2, Durable Objects, Workers AI automatically):bash
# Visit: https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/agentic-inbox # When prompted, enter your domain: yourdomain.com -
Configure Cloudflare Access (required for production):
- Navigate to Worker Settings → Domains & Routes
- Enable one-click Cloudflare Access
- Note the and
POLICY_AUDvaluesTEAM_DOMAIN - Set as Worker secrets:
bashwrangler secret put POLICY_AUD wrangler secret put TEAM_DOMAIN -
Set up Email Routing:
- Go to your domain in Cloudflare dashboard
- Navigate to Email Routing
- Create a catch-all rule forwarding to this Worker
-
Enable Email Service:
- Add binding to
send_email:wrangler.jsonc
jsonc{ "send_email": [ { "name": "SEB", "destination_address": "you@example.com" } ] } - Add
-
通过按钮部署(自动配置R2、Durable Objects、Workers AI):bash
# 访问:https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/agentic-inbox # 提示时输入你的域名:yourdomain.com -
配置Cloudflare Access(生产环境必需):
- 导航至Worker设置 → 域名与路由
- 启用一键式Cloudflare Access
- 记录和
POLICY_AUD的值TEAM_DOMAIN - 设置为Worker密钥:
bashwrangler secret put POLICY_AUD wrangler secret put TEAM_DOMAIN -
设置Email Routing:
- 进入Cloudflare控制台中的你的域名页面
- 导航至Email Routing
- 创建一个全匹配规则,将邮件转发至该Worker
-
启用邮件服务:
- 在中添加
wrangler.jsonc绑定:send_email
jsonc{ "send_email": [ { "name": "SEB", "destination_address": "you@example.com" } ] } - 在
Manual Setup
手动设置
bash
undefinedbash
undefinedClone repository
克隆仓库
git clone https://github.com/cloudflare/agentic-inbox.git
cd agentic-inbox
git clone https://github.com/cloudflare/agentic-inbox.git
cd agentic-inbox
Install dependencies
安装依赖
npm install
npm install
Create R2 bucket
创建R2存储桶
wrangler r2 bucket create agentic-inbox
wrangler r2 bucket create agentic-inbox
Configure domain in wrangler.jsonc
在wrangler.jsonc中配置域名
Set DOMAINS variable to your domain
将DOMAINS变量设置为你的域名
Deploy
部署
npm run deploy
undefinednpm run deploy
undefinedConfiguration
配置
wrangler.jsonc Structure
wrangler.jsonc结构
jsonc
{
"name": "agentic-inbox",
"main": "worker/index.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"DOMAINS": "yourdomain.com"
},
"durable_objects": {
"bindings": [
{
"name": "MAILBOX",
"class_name": "MailboxDurableObject",
"script_name": "agentic-inbox"
},
{
"name": "EMAIL_AGENT",
"class_name": "EmailAgentDurableObject",
"script_name": "agentic-inbox"
}
]
},
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agentic-inbox"
}
],
"ai": {
"binding": "AI"
},
"send_email": [
{
"name": "SEB",
"destination_address": "fallback@yourdomain.com"
}
]
}jsonc
{
"name": "agentic-inbox",
"main": "worker/index.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"DOMAINS": "yourdomain.com"
},
"durable_objects": {
"bindings": [
{
"name": "MAILBOX",
"class_name": "MailboxDurableObject",
"script_name": "agentic-inbox"
},
{
"name": "EMAIL_AGENT",
"class_name": "EmailAgentDurableObject",
"script_name": "agentic-inbox"
}
]
},
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agentic-inbox"
}
],
"ai": {
"binding": "AI"
},
"send_email": [
{
"name": "SEB",
"destination_address": "fallback@yourdomain.com"
}
]
}Environment Variables (Secrets)
环境变量(密钥)
bash
undefinedbash
undefinedRequired for Cloudflare Access authentication
Cloudflare Access认证必需
wrangler secret put POLICY_AUD
wrangler secret put TEAM_DOMAIN
wrangler secret put POLICY_AUD
wrangler secret put TEAM_DOMAIN
TEAM_DOMAIN can be either:
TEAM_DOMAIN可以是以下任意一种:
- Your Access team URL: yourteam.cloudflareaccess.com
- 你的Access团队URL:yourteam.cloudflareaccess.com
- Full certs URL: yourteam.cloudflareaccess.com/cdn-cgi/access/certs
- 完整证书URL:yourteam.cloudflareaccess.com/cdn-cgi/access/certs
undefinedundefinedDevelopment
开发
Local Development
本地开发
bash
undefinedbash
undefinedStart dev server with hot reload
启动带热重载的开发服务器
npm run dev
npm run dev
Access at http://localhost:8787
Note: Cloudflare Access is disabled in local development
注意:本地开发时Cloudflare Access处于禁用状态
undefinedundefinedProject Structure
项目结构
agentic-inbox/
├── app/ # React frontend
│ ├── routes/ # React Router v7 routes
│ ├── components/ # UI components
│ └── lib/ # Utilities, stores (Zustand)
├── worker/ # Cloudflare Worker backend
│ ├── index.ts # Hono router, email handler
│ ├── mailbox-do.ts # Mailbox Durable Object
│ ├── email-agent-do.ts # AI Agent Durable Object
│ └── auth.ts # Access JWT validation
└── wrangler.jsonc # Cloudflare configurationagentic-inbox/
├── app/ # React前端
│ ├── routes/ # React Router v7路由
│ ├── components/ # UI组件
│ └── lib/ #工具函数、状态存储(Zustand)
├── worker/ # Cloudflare Worker后端
│ ├── index.ts # Hono路由、邮件处理器
│ ├── mailbox-do.ts # Mailbox Durable Object
│ ├── email-agent-do.ts # AI Agent Durable Object
│ └── auth.ts # Access JWT验证
└── wrangler.jsonc # Cloudflare配置文件Key API Patterns
核心API模式
Creating a Mailbox
创建邮箱
typescript
// POST /api/mailboxes
const response = await fetch('/api/mailboxes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: 'hello@yourdomain.com'
})
});
const mailbox = await response.json();
// { id: "uuid", address: "hello@yourdomain.com", createdAt: "..." }typescript
// POST /api/mailboxes
const response = await fetch('/api/mailboxes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: 'hello@yourdomain.com'
})
});
const mailbox = await response.json();
// { id: "uuid", address: "hello@yourdomain.com", createdAt: "..." }Sending Email
发送邮件
typescript
// POST /api/mailboxes/:id/send
const response = await fetch(`/api/mailboxes/${mailboxId}/send`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: ['recipient@example.com'],
subject: 'Hello',
body: '<p>Email content</p>',
cc: [],
bcc: [],
inReplyTo: null,
references: []
})
});typescript
// POST /api/mailboxes/:id/send
const response = await fetch(`/api/mailboxes/${mailboxId}/send`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: ['recipient@example.com'],
subject: 'Hello',
body: '<p>Email content</p>',
cc: [],
bcc: [],
inReplyTo: null,
references: []
})
});Accessing AI Agent
访问AI Agent
typescript
// WebSocket connection to agent
const ws = new WebSocket(`wss://yourapp.workers.dev/agents/${mailboxId}`);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'message',
content: 'Summarize my unread emails'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Stream: { type: 'text-delta', text: '...' }
// Tools: { type: 'tool-call', toolName: 'read_inbox', args: {...} }
// Result: { type: 'tool-result', result: {...} }
};typescript
// 与Agent建立WebSocket连接
const ws = new WebSocket(`wss://yourapp.workers.dev/agents/${mailboxId}`);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'message',
content: 'Summarize my unread emails'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 流式输出:{ type: 'text-delta', text: '...' }
// 工具调用:{ type: 'tool-call', toolName: 'read_inbox', args: {...} }
// 结果返回:{ type: 'tool-result', result: {...} }
};Durable Object Implementation
Durable Object实现
Mailbox Durable Object
Mailbox Durable Object
typescript
import { DurableObject } from 'cloudflare:workers';
export class MailboxDurableObject extends DurableObject {
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === '/emails' && request.method === 'GET') {
const stmt = this.ctx.storage.sql.exec(
'SELECT * FROM emails ORDER BY receivedAt DESC LIMIT 50'
);
return Response.json(stmt.toArray());
}
if (url.pathname === '/emails' && request.method === 'POST') {
const email = await request.json();
const result = this.ctx.storage.sql.exec(
`INSERT INTO emails (id, subject, from_address, to_address, body, receivedAt)
VALUES (?, ?, ?, ?, ?, ?)`,
email.id, email.subject, email.from, email.to, email.body, Date.now()
);
return Response.json({ success: true });
}
return new Response('Not found', { status: 404 });
}
}typescript
import { DurableObject } from 'cloudflare:workers';
export class MailboxDurableObject extends DurableObject {
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === '/emails' && request.method === 'GET') {
const stmt = this.ctx.storage.sql.exec(
'SELECT * FROM emails ORDER BY receivedAt DESC LIMIT 50'
);
return Response.json(stmt.toArray());
}
if (url.pathname === '/emails' && request.method === 'POST') {
const email = await request.json();
const result = this.ctx.storage.sql.exec(
`INSERT INTO emails (id, subject, from_address, to_address, body, receivedAt)
VALUES (?, ?, ?, ?, ?, ?)`,
email.id, email.subject, email.from, email.to, email.body, Date.now()
);
return Response.json({ success: true });
}
return new Response('Not found', { status: 404 });
}
}Email Agent Durable Object
Email Agent Durable Object
typescript
import { AIChatAgent } from '@cloudflare/agents-sdk';
import { DurableObject } from 'cloudflare:workers';
export class EmailAgentDurableObject extends DurableObject {
private agent?: AIChatAgent;
async fetch(request: Request) {
if (!this.agent) {
this.agent = new AIChatAgent({
model: '@cf/moonshotai/kimi-k2.5',
binding: this.env.AI,
tools: [
{
name: 'read_inbox',
description: 'Read emails from the inbox',
parameters: {
type: 'object',
properties: {
limit: { type: 'number', default: 10 }
}
},
handler: async ({ limit }) => {
// Fetch from mailbox DO
const mailboxId = this.ctx.id.toString();
const emails = await this.fetchMailboxEmails(mailboxId, limit);
return { emails };
}
},
{
name: 'send_email',
description: 'Send an email',
parameters: {
type: 'object',
properties: {
to: { type: 'array', items: { type: 'string' } },
subject: { type: 'string' },
body: { type: 'string' }
},
required: ['to', 'subject', 'body']
},
handler: async ({ to, subject, body }) => {
// Send via Email Service
await this.env.SEB.send({
from: this.getMailboxAddress(),
to,
subject,
content: [{ type: 'text/html', value: body }]
});
return { success: true };
}
}
],
systemPrompt: 'You are an email assistant...'
});
}
// Handle WebSocket upgrade for streaming
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader === 'websocket') {
const [client, server] = Object.values(new WebSocketPair());
this.ctx.acceptWebSocket(server);
return new Response(null, { status: 101, webSocket: client });
}
return new Response('Expected WebSocket', { status: 400 });
}
async webSocketMessage(ws: WebSocket, message: string) {
const { content } = JSON.parse(message);
for await (const chunk of this.agent.stream(content)) {
ws.send(JSON.stringify(chunk));
}
}
}typescript
import { AIChatAgent } from '@cloudflare/agents-sdk';
import { DurableObject } from 'cloudflare:workers';
export class EmailAgentDurableObject extends DurableObject {
private agent?: AIChatAgent;
async fetch(request: Request) {
if (!this.agent) {
this.agent = new AIChatAgent({
model: '@cf/moonshotai/kimi-k2.5',
binding: this.env.AI,
tools: [
{
name: 'read_inbox',
description: 'Read emails from the inbox',
parameters: {
type: 'object',
properties: {
limit: { type: 'number', default: 10 }
}
},
handler: async ({ limit }) => {
// 从Mailbox DO获取邮件
const mailboxId = this.ctx.id.toString();
const emails = await this.fetchMailboxEmails(mailboxId, limit);
return { emails };
}
},
{
name: 'send_email',
description: 'Send an email',
parameters: {
type: 'object',
properties: {
to: { type: 'array', items: { type: 'string' } },
subject: { type: 'string' },
body: { type: 'string' }
},
required: ['to', 'subject', 'body']
},
handler: async ({ to, subject, body }) => {
// 通过邮件服务发送
await this.env.SEB.send({
from: this.getMailboxAddress(),
to,
subject,
content: [{ type: 'text/html', value: body }]
});
return { success: true };
}
}
],
systemPrompt: 'You are an email assistant...'
});
}
// 处理WebSocket升级以实现流式输出
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader === 'websocket') {
const [client, server] = Object.values(new WebSocketPair());
this.ctx.acceptWebSocket(server);
return new Response(null, { status: 101, webSocket: client });
}
return new Response('Expected WebSocket', { status: 400 });
}
async webSocketMessage(ws: WebSocket, message: string) {
const { content } = JSON.parse(message);
for await (const chunk of this.agent.stream(content)) {
ws.send(JSON.stringify(chunk));
}
}
}Email Routing Handler
Email Routing处理器
typescript
// worker/index.ts
import { EmailMessage } from 'cloudflare:email';
export default {
async email(message: EmailMessage, env: Env) {
const to = message.to;
const mailboxId = await env.KV.get(`address:${to}`);
if (!mailboxId) {
message.setReject('Mailbox not found');
return;
}
// Forward to Mailbox DO
const id = env.MAILBOX.idFromString(mailboxId);
const stub = env.MAILBOX.get(id);
const emailData = {
id: crypto.randomUUID(),
from: message.from,
to: message.to,
subject: message.headers.get('subject'),
body: await message.text(),
receivedAt: Date.now()
};
await stub.fetch('https://mailbox/emails', {
method: 'POST',
body: JSON.stringify(emailData)
});
}
};typescript
// worker/index.ts
import { EmailMessage } from 'cloudflare:email';
export default {
async email(message: EmailMessage, env: Env) {
const to = message.to;
const mailboxId = await env.KV.get(`address:${to}`);
if (!mailboxId) {
message.setReject('Mailbox not found');
return;
}
// 转发至Mailbox DO
const id = env.MAILBOX.idFromString(mailboxId);
const stub = env.MAILBOX.get(id);
const emailData = {
id: crypto.randomUUID(),
from: message.from,
to: message.to,
subject: message.headers.get('subject'),
body: await message.text(),
receivedAt: Date.now()
};
await stub.fetch('https://mailbox/emails', {
method: 'POST',
body: JSON.stringify(emailData)
});
}
};Common Patterns
通用模式
Access Authentication Middleware
Access认证中间件
typescript
// worker/auth.ts
import * as jose from 'jose';
export async function validateAccessToken(request: Request, env: Env) {
if (!env.POLICY_AUD || !env.TEAM_DOMAIN) {
throw new Error('Cloudflare Access must be configured in production');
}
const token = request.headers.get('Cf-Access-Jwt-Assertion');
if (!token) {
throw new Error('Missing Access token');
}
const certsUrl = env.TEAM_DOMAIN.includes('/cdn-cgi/access/certs')
? env.TEAM_DOMAIN
: `https://${env.TEAM_DOMAIN}/cdn-cgi/access/certs`;
const jwks = jose.createRemoteJWKSet(new URL(certsUrl));
const { payload } = await jose.jwtVerify(token, jwks, {
audience: env.POLICY_AUD,
issuer: env.TEAM_DOMAIN
});
return payload;
}typescript
// worker/auth.ts
import * as jose from 'jose';
export async function validateAccessToken(request: Request, env: Env) {
if (!env.POLICY_AUD || !env.TEAM_DOMAIN) {
throw new Error('生产环境必须配置Cloudflare Access');
}
const token = request.headers.get('Cf-Access-Jwt-Assertion');
if (!token) {
throw new Error('缺少Access令牌');
}
const certsUrl = env.TEAM_DOMAIN.includes('/cdn-cgi/access/certs')
? env.TEAM_DOMAIN
: `https://${env.TEAM_DOMAIN}/cdn-cgi/access/certs`;
const jwks = jose.createRemoteJWKSet(new URL(certsUrl));
const { payload } = await jose.jwtVerify(token, jwks, {
audience: env.POLICY_AUD,
issuer: env.TEAM_DOMAIN
});
return payload;
}Agent System Prompt Customization
Agent系统提示词自定义
typescript
// Update system prompt per mailbox
const response = await fetch(`/api/mailboxes/${mailboxId}/agent/system-prompt`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
systemPrompt: `You are a professional email assistant for sales@company.com.
Always be polite and concise. When drafting replies, maintain a friendly tone.`
})
});typescript
// 按邮箱更新系统提示词
const response = await fetch(`/api/mailboxes/${mailboxId}/agent/system-prompt`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
systemPrompt: `You are a professional email assistant for sales@company.com.
Always be polite and concise. When drafting replies, maintain a friendly tone.`
})
});Attachment Storage in R2
R2附件存储
typescript
// Store attachment in R2
async function storeAttachment(env: Env, emailId: string, file: File) {
const key = `attachments/${emailId}/${file.name}`;
await env.R2.put(key, file.stream(), {
httpMetadata: {
contentType: file.type
}
});
return key;
}
// Retrieve attachment
async function getAttachment(env: Env, key: string) {
const object = await env.R2.get(key);
if (!object) return null;
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'
}
});
}typescript
// 在R2中存储附件
async function storeAttachment(env: Env, emailId: string, file: File) {
const key = `attachments/${emailId}/${file.name}`;
await env.R2.put(key, file.stream(), {
httpMetadata: {
contentType: file.type
}
});
return key;
}
// 获取附件
async function getAttachment(env: Env, key: string) {
const object = await env.R2.get(key);
if (!object) return null;
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'
}
});
}Troubleshooting
故障排查
Invalid or Expired Access Token
Access令牌无效或过期
Issue: error when accessing deployed app.
Invalid or expired Access tokenSolution:
bash
undefined问题:访问已部署应用时出现错误。
Invalid or expired Access token解决方案:
bash
undefined1. Turn Access off and back on in Worker Settings → Domains & Routes
1. 在Worker设置 → 域名与路由中,先关闭再重新开启Access
2. Copy new POLICY_AUD and TEAM_DOMAIN values from modal
2. 从弹窗中复制新的POLICY_AUD和TEAM_DOMAIN值
3. Reset secrets
3. 重置密钥
wrangler secret put POLICY_AUD
wrangler secret put POLICY_AUD
Paste new value
粘贴新值
wrangler secret put TEAM_DOMAIN
wrangler secret put TEAM_DOMAIN
Paste new value (can be team URL or full certs URL)
粘贴新值(可以是团队URL或完整证书URL)
4. Redeploy
4. 重新部署
npm run deploy
undefinednpm run deploy
undefinedEmails Not Arriving
邮件未送达
Issue: Catch-all rule configured but emails not appearing in inbox.
Checklist:
- Verify Email Routing is enabled for your domain
- Check catch-all rule forwards to the Worker (not an email address)
- Confirm mailbox exists for the recipient address:
bash
# Check via API curl https://yourapp.workers.dev/api/mailboxes - Check Worker logs:
bash
wrangler tail
问题:已配置全匹配规则,但邮件未出现在收件箱中。
检查清单:
- 确认你的域名已启用Email Routing
- 检查全匹配规则是否转发至Worker(而非邮箱地址)
- 确认收件人地址对应的邮箱已存在:
bash
# 通过API检查 curl https://yourapp.workers.dev/api/mailboxes - 查看Worker日志:
bash
wrangler tail
Agent Not Responding
Agent无响应
Issue: WebSocket connection fails or agent doesn't respond.
Solution:
typescript
// Verify Workers AI binding in wrangler.jsonc
{
"ai": {
"binding": "AI"
}
}
// Check agent initialization
const ws = new WebSocket(`wss://yourapp.workers.dev/agents/${mailboxId}`);
ws.onerror = (error) => console.error('WebSocket error:', error);
ws.onclose = (event) => console.log('Closed:', event.code, event.reason);问题:WebSocket连接失败或Agent无响应。
解决方案:
typescript
// 验证wrangler.jsonc中的Workers AI绑定
{
"ai": {
"binding": "AI"
}
}
// 检查Agent初始化
const ws = new WebSocket(`wss://yourapp.workers.dev/agents/${mailboxId}`);
ws.onerror = (error) => console.error('WebSocket error:', error);
ws.onclose = (event) => console.log('Closed:', event.code, event.reason);R2 Bucket Not Found
R2存储桶未找到
Issue: Attachment upload fails with R2 error.
Solution:
bash
undefined问题:附件上传失败,出现R2错误。
解决方案:
bash
undefinedCreate bucket if missing
若存储桶缺失则创建
wrangler r2 bucket create agentic-inbox
wrangler r2 bucket create agentic-inbox
Verify binding in wrangler.jsonc
验证wrangler.jsonc中的绑定
{
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agentic-inbox"
}
]
}
{
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agentic-inbox"
}
]
}
Redeploy
重新部署
npm run deploy
undefinednpm run deploy
undefinedSend Email Not Working
邮件发送失败
Issue: binding not available or emails not sending.
send_emailSolution:
- Enable Email Service in Cloudflare dashboard for your account
- Add binding to :
wrangler.jsoncjsonc{ "send_email": [ { "name": "SEB", "destination_address": "verified@yourdomain.com" } ] } - Verify domain ownership for sending
- Redeploy Worker
问题:绑定不可用或邮件无法发送。
send_email解决方案:
- 在Cloudflare控制台中为你的账户启用邮件服务
- 在中添加绑定:
wrangler.jsoncjsonc{ "send_email": [ { "name": "SEB", "destination_address": "verified@yourdomain.com" } ] } - 验证发送域名的所有权
- 重新部署Worker
Local Development Access Errors
本地开发访问错误
Issue: Access errors when running .
npm run devNote: Cloudflare Access is intentionally disabled in local development. If you see Access-related errors in production mode locally, set:
typescript
// worker/auth.ts - for local testing only
if (env.ENVIRONMENT === 'development') {
return { email: 'dev@localhost' }; // Skip Access validation
}问题:运行时出现访问错误。
npm run dev说明:本地开发时Cloudflare Access会被有意禁用。如果在本地生产模式下看到Access相关错误,请设置:
typescript
// worker/auth.ts - 仅用于本地测试
if (env.ENVIRONMENT === 'development') {
return { email: 'dev@localhost' }; // 跳过Access验证
}CLI Commands
CLI命令
bash
undefinedbash
undefinedDevelopment
开发相关
npm run dev # Start local dev server
npm run build # Build frontend and worker
npm run deploy # Deploy to Cloudflare
npm run dev # 启动本地开发服务器
npm run build # 构建前端和Worker
npm run deploy # 部署至Cloudflare
Wrangler commands
Wrangler命令
wrangler tail # Stream Worker logs
wrangler secret put KEY # Set environment secret
wrangler r2 bucket list # List R2 buckets
wrangler d1 execute DB --command "SELECT * FROM emails" # Query D1 (if using D1)
wrangler tail # 流式输出Worker日志
wrangler secret put KEY # 设置环境密钥
wrangler r2 bucket list # 列出R2存储桶
wrangler d1 execute DB --command "SELECT * FROM emails" # 查询D1(若使用D1)
Debugging
调试相关
wrangler dev --remote # Debug against production resources
wrangler kv:key list --binding=KV # List KV keys (if using KV)
undefinedwrangler dev --remote # 针对生产资源进行调试
wrangler kv:key list --binding=KV # 列出KV键(若使用KV)
undefinedMCP Server Integration
MCP服务器集成
Agentic Inbox includes an MCP server at for external AI tools (Claude Code, Cursor):
/mcpjson
// claude_desktop_config.json or similar
{
"mcpServers": {
"agentic-inbox": {
"url": "https://yourapp.workers.dev/mcp",
"headers": {
"Cf-Access-Client-Id": "YOUR_SERVICE_TOKEN_ID",
"Cf-Access-Client-Secret": "YOUR_SERVICE_TOKEN_SECRET"
}
}
}
}MCP tools have access to all mailboxes by passing parameter. Security relies on the Cloudflare Access policy.
mailboxIdAgentic Inbox在路径提供了MCP服务器,供外部AI工具(如Claude Code、Cursor)使用:
/mcpjson
// claude_desktop_config.json或类似配置文件
{
"mcpServers": {
"agentic-inbox": {
"url": "https://yourapp.workers.dev/mcp",
"headers": {
"Cf-Access-Client-Id": "YOUR_SERVICE_TOKEN_ID",
"Cf-Access-Client-Secret": "YOUR_SERVICE_TOKEN_SECRET"
}
}
}
}MCP工具可通过传递参数访问所有邮箱,安全性依赖于Cloudflare Access策略。
mailboxId