日历管理服务器

一个基于Cloudflare Workers的安全多用户MCP服务器,通过Google Calendar集成实现自然语言查询和管理日历事件。

  • by workingman
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 workingman/gcal-mcp

使用说明

日历MCP服务器

通过模型上下文协议(MCP)为Claude集成谷歌日历

运行在Cloudflare Workers上的安全、多用户MCP服务器,使Claude Desktop能够使用自然语言查询、搜索和管理Google日历事件。具有加密令牌存储、多用户隔离和自动令牌刷新功能。

特性

  • 7 MCP工具 全面的日历管理:

    • list_events -使用自然语言日期范围查询事件(“未来7天”、“今天”、“本周”)
    • get_event -获取特定事件的详细信息
    • search_events -按关键字、与会者或位置在日历中搜索
    • get_free_busy -检查日程安排的可用性
    • create_event -使用丰富的元数据创建新的日历事件
    • move_event -在日历之间重新安排或移动事件
    • calendar_auth_status -检查Google帐户连接和令牌状态
  • 安全第一设计:

    • Cloudflare KV中的AES-256-GCM加密令牌存储
    • 基于HMAC-SHA256的会话验证(三层验证)
    • 完全多用户隔离(无跨帐户访问)
    • 自动主动令牌刷新(5分钟阈值)
    • 安全事件的全面审计日志记录
  • 生产就绪:

    • 在Cloudflare Workers上运行(全球边缘部署)
    • 典型查询的响应时间低于2秒
    • 支持并行取数的多日历查询
    • 处理大型事件列表(最多1000个事件)的分页
    • 优雅的错误恢复,带有可操作的错误消息

建筑

┌─────────────────┐         ┌──────────────────┐         ┌─────────────────┐
│ Claude Desktop  │────────>│  Calendar MCP    │────────>│  Google         │
│                 │  MCP    │  (Cloudflare)    │  OAuth  │  Calendar API   │
│                 │<────────│  Workers         │<────────│                 │
└─────────────────┘         └──────────────────┘         └─────────────────┘
                                     │
                                     │ Encrypted Storage
                                     v
                            ┌──────────────────┐
                            │  Cloudflare KV   │
                            │  (AES-256-GCM)   │
                            └──────────────────┘

组件:

  • MCP服务器 (mcp-server.ts):实施7个MCP工具,处理工具布线
  • 令牌管理器 (crypto.ts):使用AES-256-GCM加密/解密OAuth令牌
  • 会话验证器 (session.ts):使用HMAC-SHA256验证用户身份
  • 日历API客户端 (calendar-api.ts):Google日历API集成
  • OAuth路由 (app.ts):处理MCP和Google OAuth流

先决条件

  • Node.js 18+和npm
  • Cloudflare帐户 (免费版作品)
  • 谷歌云帐户 用于OAuth凭据
  • 牧马人CLI: npm install -g wrangler
  • 克劳德桌面 (支持MCP的最新版本)

快速开始

自动设置

运行自动安装脚本:

bash scripts/setup.sh

这将:

  1. 安装依赖项
  2. 创建Cloudflare KV命名空间
  3. 生成加密密钥
  4. 设置Cloudflare机密
  5. 指导您完成GCP OAuth设置

手动设置

Click to expand manual setup instructions

1.安装依赖项

npm install

2.配置Cloudflare KV命名空间

创建KV命名空间(如果尚未创建):

npx wrangler kv namespace create OAUTH_KV
npx wrangler kv namespace create GOOGLE_TOKENS_KV

更新 wrangler.jsonc 使用输出中的命名空间ID。

3.生成和设置加密密钥

生成加密密钥:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"  # TOKEN_ENCRYPTION_KEY
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"  # TOKEN_HMAC_KEY

设置为Cloudflare机密:

echo "<your-encryption-key>" | npx wrangler secret put TOKEN_ENCRYPTION_KEY
echo "<your-hmac-key>" | npx wrangler secret put TOKEN_HMAC_KEY

4.谷歌云平台设置

  1. 首选 Google Cloud控制台凭据
  2. 创建新项目或选择现有项目
  3. 启用Google日历API
  4. 创建OAuth 2.0客户端ID(Web应用程序)
  5. 设置授权重定向URI: https://calendar-mcp.<your-subdomain>.workers.dev/google/callback
  6. 复制客户端ID和客户端密码

将Google OAuth凭据设置为机密:

echo "<your-google-client-id>" | npx wrangler secret put GOOGLE_CLIENT_ID
echo "<your-google-client-secret>" | npx wrangler secret put GOOGLE_CLIENT_SECRET
echo "https://calendar-mcp.<your-subdomain>.workers.dev" | npx wrangler secret put WORKER_URL

5.部署

npm run deploy

用法

从克劳德桌面连接

  1. 部署MCP服务器 (请参阅上面的设置部分)

  2. 配置Claude桌面 -添加到您的Claude Desktop MCP配置文件中:

    macOS/Linux: ~/Library/Application Support/Claude/claude_desktop_config.json 视窗: %APPDATA%\Claude\claude_desktop_config.json

    {
      "mcpServers": {
        "calendar": {
          "url": "https://calendar-mcp.<your-subdomain>.workers.dev",
          "transport": {
            "type": "sse"
          }
        }
      }
    }
    
  3. 向Google授权:

    • 在Claude Desktop中,询问:“检查我的日历身份验证状态”
    • Claude将提供一个授权URL
    • 访问浏览器中的URL并完成Google OAuth
    • 返回克劳德并确认授权

MCP工具参考

list_events

使用自然语言日期范围查询日历事件。

参数:

  • date_range (string):自然语言日期范围
    • 示例:“今天”、“明天”、“接下来的7天”、“本周”、“2026-02-20至2026-02-27”
  • calendar_id (可选):特定日历ID(默认:所有日历)
  • max_results (可选):要返回的最大事件数(默认值:250,最大值:2500)

示例:

Show me my calendar events for the next week

响应:事件列表,包括标题、开始/结束时间、与会者、地点


get_event

获取特定事件的详细信息。

参数:

  • event_id (字符串):谷歌日历事件ID
  • calendar_id (可选):日历ID(默认:主)

示例:

Get details for event abc123xyz

响应:完整的事件详细信息,包括描述、会议链接、重复规则


search_events

按关键字、与会者或位置搜索所有日历。

参数:

  • query (字符串):搜索查询(搜索标题、描述、位置、与会者)
  • date_range (可选):将搜索限制在日期范围内(默认值:接下来的90天)
  • max_results (可选):要返回的最大事件数(默认值:100)

示例:

Search for all events with "standup" in the title

响应:按相关性排序的匹配事件


get_free_busy

检查日历的可用性以进行日程安排。

参数:

  • time_min (string):开始时间(ISO 8601或自然语言)
  • time_max (字符串):结束时间(ISO 8601或自然语言)
  • calendar_ids (可选):要检查的特定日历(默认:全部)

示例:

Am I free tomorrow afternoon between 2pm and 5pm?

响应:指定时间范围内的忙/闲时段


create_event

创建新的日历事件。

参数:

  • summary (string):事件标题
  • start_time (string):开始时间(ISO 8601或自然语言)
  • end_time (字符串):结束时间(ISO 8601或自然语言)
  • description (可选):事件描述
  • location (可选):活动地点
  • attendees (可选):与会者电子邮件地址数组
  • calendar_id (可选):目标日历(默认:主日历)

示例:

Create a meeting called "Team Sync" tomorrow at 10am for 1 hour

响应:创建了带有ID和确认的事件详细信息


move_event

重新安排或将活动移动到其他日历。

参数:

  • event_id (string):要移动的事件ID
  • source_calendar_id (可选):源日历(默认:主日历)
  • destination_calendar_id (可选):目标日历(用于在日历之间移动)
  • new_start_time (可选):新的开始时间
  • new_end_time (可选):新结束时间

示例:

Move my 3pm meeting to 4pm today

响应:更新了事件详细信息


calendar_auth_status

检查Google帐户连接和令牌状态。

参数:无

示例:

Check my calendar auth status

响应:连接状态、令牌到期、授权URL(如果需要)

故障排除

身份验证错误

错误: Google account not connected for user@example.com

解决方案:

  1. 询问克劳德:“检查我的日历身份验证状态”
  2. 访问提供的授权URL
  3. 在浏览器中完成Google OAuth
  4. 返回Claude并重试该操作

错误: Token expired or invalid

解决方案:令牌会自动刷新,但如果这种情况持续存在:

  1. 询问克劳德:“检查我的日历身份验证状态”
  2. 如果需要,请访问提供的URL重新授权

错误: Request had invalid authentication credentials

解决方案:检查Cloudflare机密设置是否正确:

npx wrangler secret list

验证是否存在所有机密:

  • TOKEN_ENCRYPTION_KEY
  • TOKEN_HMAC_KEY
  • GOOGLE_CLIENT_ID
  • GOOGLE_CLIENT_SECRET
  • WORKER_URL

速率限制

错误: Rate limit exceeded

解决方案:谷歌日历API允许每个用户每100秒进行1000次查询。服务器对重试执行指数回退。如果您经常达到速率限制:

  • 减少查询频率
  • 使用更具体的日期范围来减少事件计数
  • 在对话上下文中缓存结果

调试

启用详细日志记录:

  1. 检查Cloudflare Workers日志: npx wrangler tail
  2. 安全事件以JSON格式记录到stdout
  3. 寻找 category: "security_violation" 或 category: "token_access"

本地测试令牌加密:

npm test crypto.test.ts

验证KV命名空间是否存在:

npx wrangler kv namespace list

发展

本地开发

npm run dev

在以下位置启动本地开发服务器 http://localhost:8787

备注:地方发展需要建立 .dev.vars 包含机密的文件(请参见 .env.example).

类型检查

npm run typecheck

测试

运行所有测试(跨单元、集成、安全、E2E和性能的298个测试):

npm test

运行特定的测试套件:

npm test crypto.test.ts                    # Token encryption/decryption
npm test security-validation.test.ts       # Security validation
npm test e2e-flow.test.ts                  # End-to-end flows
npm test performance.test.ts               # Performance benchmarks

测试覆盖率:

  • 单元测试:加密,会话验证,日历API客户端
  • 集成测试:OAuth流程、MCP工具、多用户场景
  • 安全测试:加密存储、多用户隔离、HMAC验证
  • E2E测试:完成从OAuth到工具使用的用户旅程
  • 性能测试:CPU预算合规性、并发负载测试

构建(试运行)

npm run build

在不实际部署的情况下验证部署。

贡献

  1. 编写新功能的测试(请参见 tests/ 目录)
  2. 确保所有测试通过: npm test
  3. 运行类型检查: npm run typecheck
  4. 遵循现有的代码模式(请参见 src/ 目录)
  5. 根据需要更新文档

项目结构

calendar-mcp/
├── src/
│   ├── index.ts           # Main Worker entry point
│   ├── app.ts             # Hono routes (Google OAuth, MCP OAuth)
│   ├── mcp-server.ts      # CalendarMCP Durable Object (7 MCP tools)
│   ├── crypto.ts          # Token encryption/decryption (AES-256-GCM)
│   ├── session.ts         # Session validation utilities
│   ├── calendar-api.ts    # Google Calendar API client
│   ├── utils.ts           # HTML rendering helpers
│   ├── types.ts           # TypeScript type definitions
│   └── env.d.ts           # Cloudflare Workers environment types
├── tests/
│   ├── crypto.test.ts     # TokenManager tests
│   ├── session.test.ts    # Session validation tests
│   └── calendar-api.test.ts # Google Calendar API client tests
├── scripts/
│   ├── setup.sh           # Automated setup script
│   └── teardown.sh        # Cleanup script
├── docs/
│   ├── prd-calendar-mcp.md  # Product requirements
│   ├── tdd-calendar-mcp.md  # Technical design document
│   └── arch-calendar-mcp.mmd # Architecture diagram
├── wrangler.jsonc         # Cloudflare Workers configuration
├── package.json           # Dependencies and scripts
└── tsconfig.json          # TypeScript configuration

已实现的功能

基础设施(母公司问题#1)-完成

  • ✅ 带有Cloudflare Workers配置的TypeScript项目
  • ✅ 用于OAuth和令牌存储的KV命名空间
  • ✅ 使用Web Crypto API的AES-256-GCM令牌加密
  • ✅ 基于HMAC的会话验证
  • ✅ Google日历API客户端
  • ✅ Google OAuth授权流程
  • ✅ MCP OAuth路由
  • ✅ CalendarMCP耐用对象骨架(已注册7个工具)
  • ✅ 构建和部署管道

MCP工具

所有7个工具都注册了占位符实现:

  1. list_events -使用筛选器检索日历事件
  2. get_event -获取特定活动的完整详细信息
  3. search_events -按关键字在日历中搜索
  4. get_free_busy -检查可用性
  5. create_event -创建新日历事件
  6. move_event -重新安排现有活动
  7. calendar_auth_status -检查Google帐户连接

安全

加密令牌存储(SEC-001)

所有Google OAuth令牌都使用静态加密 AES-256-GCM 每个加密具有唯一的初始化向量(IV)。令牌从不以明文形式存储在Cloudflare KV中。

验证:

  • 手动KV检查仅显示密文(base64编码的二进制数据)
  • 加密密钥从不出现在日志或错误消息中
  • 每种加密都会生成一个唯一的随机12字节IV

多用户隔离(SEC-002)

用户之间完全隔离 三层验证:

  1. 层1:基于HMAC-SHA256的KV密钥阻止枚举

    • KV密钥格式: google_tokens:{HMAC-SHA256(user_email)}
    • 在没有HMAC密钥的情况下,密码学上不可能反转或猜测密钥
  2. 层2: user_id_hash KV获取后的验证

    • 加密令牌包括用户ID的HMAC哈希
    • 即使加密密钥泄露,也能防止跨用户令牌访问
  3. 层3:嵌入式 user_id 解密后验证

    • 解密令牌包括明文用户ID
    • 最终所有权检查确保令牌属于请求用户

验证:

  • 使用2+个帐户进行渗透测试,确认完全隔离
  • 用户A无法通过任何攻击向量访问用户B的令牌
  • 会话劫持尝试在所有验证层都被拒绝

主动令牌刷新(SEC-003)

当距离到期时间不足5分钟时,令牌会自动刷新:

const timeUntilExpiry = tokens.expires_at - Date.now();
if (timeUntilExpiry < 5 * 60 * 1000) {
  // Proactively refresh token
}

好处:

  • 降低使用过期令牌进行API调用的风险
  • 对用户透明(无需重新授权)
  • 有限的令牌寿命缩短了暴露窗口

安全测试

该项目包括全面的安全测试:

npm test security-validation.test.ts       # SEC-001, SEC-002, SEC-004 validation
npm test security-integration.test.ts      # Multi-user attack scenarios
npm test kv-key-security.test.ts           # HMAC non-enumeration validation

测试覆盖率:

  • 加密存储检查(密文格式验证)
  • 多用户隔离(防止跨帐户访问)
  • 会话劫持预防(拒绝伪造用户ID)
  • 凭证泄漏预防(密钥从不出现在日志/错误中)
  • 令牌篡改检测(GCM身份验证标签验证)

安全最佳实践

  1. 定期轮换加密密钥:生成新密钥并重新加密令牌
  2. 监控审核日志:检查 security_violation 事件
  3. 限制令牌范围:仅限请求 calendar 范围(无法完全访问Google帐户)
  4. 仅使用HTTPS:工作URL必须使用HTTPS(由Cloudflare强制执行)
  5. 查看OAuth同意屏幕:确保用户了解权限

清理

要删除所有Cloudflare资源,请执行以下操作:

bash scripts/teardown.sh

这将删除KV命名空间,并提供删除机密的说明。 必须从Google Cloud控制台手动删除GCP OAuth客户端。

演出

响应时间:

  • 95%的查询在2秒内完成
  • 加密/解密:每次操作<10ms
  • HMAC计算:每次操作<5ms
  • 批量操作(100个用户):<50ms(在Cloudflare Workers CPU预算范围内)

可扩展性:

  • 支持100+并发用户
  • 多日历查询使用并行取数(n个日历≈与1相同的时间)
  • 大型事件列表(最多1000个事件)的自动分页
  • 日历列表的KV缓存(1小时TTL)

性能测试:

npm test performance.test.ts

测试验证了CPU预算合规性、并发负载处理和内存效率。

文档

项目状态

完成:

  • ✅ 所有7个MCP工具均已实施和测试(298项测试通过)
  • ✅ 安全验证(加密存储、多用户隔离)
  • ✅ E2E集成测试(完整的用户流程)
  • ✅ 性能测试(CPU预算合规性)
  • ✅ 全面的文件
  • ✅ 自动设置和部署脚本

生产就绪:此MCP服务器已准备好与Claude Desktop一起部署和使用。

许可证

麻省理工学院