m365图形mcp网关
用于Microsoft 365 Graph API的可生产MCP网关,具有MSAL身份验证和护栏。
特性
- 邮件:列表、搜索、获取、起草、回复、全部回复、发送(确认门控)
- 日历:列表、获取、空闲时段、创建(团队会议+议程)、响应、取消
- 文件:SharePoint/OneDrive搜索和内容提取
- 跨实体搜索:跨邮件、文件和日历事件查找
- 护栏:电子邮件域分配列表、附件限制、HTML清理、审核日志记录
- 安全:写入操作需要显式
confirm=true
快速入门(本地)
# Install
npm install
# Configure
cp .env.example .env
# Set GRAPH_MCP_CLIENT_ID and GRAPH_MCP_TENANT_ID
# Optionally set GRAPH_MCP_API_KEY to protect the /mcp endpoint
# Build
npm run build
# Login (interactive browser)
npm run login
# Start server
npm run start认证
交互式浏览器登录(推荐)
npm run login
# Opens browser for Microsoft sign-in设备代码登录(无头/SSH)
npm run login:device
# Displays a code to enter at https://microsoft.com/deviceloginAzure AD应用程序注册
使用以下设置在Azure AD中注册应用程序:
- 首选 Azure门户>应用程序注册>新注册
- 设置名称(例如。,
graph-mcp-gateway) - 在...之下 支持的帐户类型,选择“仅此组织目录中的帐户(单个租户)”
- 在...之下 重定向URI,添加一个适合您的SOAP交互式登录流的localhost重定向URI。
例子: http://localhost
- 点击 注册
- 注意 应用程序(客户端)ID 和 目录(租户)ID --你需要这些
API权限 (全部委托):
| 权限 | 类型 | 描述 |
|---|---|---|
Mail.Read | 委托 | 读取用户邮件 |
Mail.Read.Shared | 委派 | 读取共享邮箱 |
Mail.ReadWrite | 委托 | 读写用户邮件 |
Mail.ReadWrite.Shared | 委派 | 读/写共享邮箱 |
Mail.Send | 委派 | 以用户身份发送邮件 |
Mail.Send.Shared | 委派 | 从共享邮箱发送邮件 |
Calendars.Read | 委派 | 读取用户日历 |
Calendars.Read.Shared | 委派 | 读取共享日历 |
Calendars.ReadWrite | 委托 | 读写用户日历 |
Calendars.ReadWrite.Shared | 委派 | 读/写共享日历 |
User.Read | 委托 | 登录并阅读用户配置文件 |
Files.Read.All | 委托 | 读取用户可以访问的所有文件 |
Sites.Read.All | 委派 | 读取所有网站集中的项目 |
添加权限后授予租户管理员同意。
共享邮箱/日历访问还需要Exchange共享/委派权限。 仅凭范围是不够的。
MCP端点
| 端点 | 方法 | 描述 |
|---|---|---|
/mcp | POST | MCP JSON-RPC端点 |
/health | GET | 健康检查 |
/auth/status | GET | 身份验证状态(JSON) |
MCP服务器接受普通JSON-RPC POST请求——没有SSE,没有会话。每个请求都是独立的。
支持的MCP方法
| 方法 | 说明 |
|---|---|
initialize | 握手--返回协议版本和服务器功能 |
ping | 协议级健康检查(返回 {}) |
tools/list | 列出所有具有输入模式的可用工具 |
tools/call | 按名称和参数执行工具 |
通知(notifications/initialized, notifications/cancelled)返回没有正文的HTTP 204。
MCP呼叫示例
curl -s http://localhost:3000/mcp -d '{
"jsonrpc": "2.0", "id": 1,
"method": "tools/call",
"params": { "name": "find", "arguments": { "query": "budget report", "entity_types": ["files"] } }
}' | jq配置
config.yaml 控制范围、护栏和输出限制:
guardrails:
email:
allowDomains:
- '*.example.com' # restrict outbound email to your org看 config.yaml 对于所有选项。
API密钥验证
可选择保护 /mcp 带有Bearer令牌的端点。设置 GRAPH_MCP_API_KEY 环境变量(或将其添加到 .env):
GRAPH_MCP_API_KEY=my-secret-key设置后,所有 /mcp 请求必须包含标头:
Authorization: Bearer my-secret-key没有有效密钥的请求将接收HTTP 401。健康(/health)和身份验证状态(/auth/status)端点从不被API密钥选通。当 GRAPH_MCP_API_KEY 未设置或为空 /mcp 端点是开放访问的。
码头工人
# Build and start
docker compose build && docker compose up -d
# Login via device code in container
docker compose run --rm -it m365-graph-mcp-gateway node dist/index.js --login-device
# Health check
curl -s http://localhost:18790/health | jq
# Logs
docker compose logs -f m365-graph-mcp-gateway______________________________________________________________________
部署到Azure容器应用程序
本节将介绍如何部署Azure容器应用的网关。架构部署 每个用户的容器应用实例 共享Azure基础架构。每个用户都有自己的隔离容器,其中包含:
- 单独的MSAL凭据和Graph API令牌
- NFS支持的持久存储,用于令牌缓存和审计日志
- 默认情况下缩放为零(空闲时无成本)
- 仅限内部网络(不公开互联网)
架构概述
┌─────────────────────────────────────────────────────────────────┐
│ Resource Group │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ ACR │ │ Key Vault │ │ Log Analytics │ │
│ │ (images) │ │ (secrets) │ │ (logs) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
│ ┌──────────────────── VNet (10.0.0.0/16) ───────────────────┐ │
│ │ │ │
│ │ ┌──── snet-containerapps (/23) ────────────────────────┐ │ │
│ │ │ Container Apps Environment (internal-only) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ ca-...-alice │ │ ca-...-bob │ │ ca-...-jdoe │ │ │ │
│ │ │ │ :3000/mcp │ │ :3000/mcp │ │ :3000/mcp │ │ │ │
│ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬──────┘ │ │ │
│ │ │ └────────────────┼─────────────────┘ │ │ │
│ │ │ NFS Volume Mount │ │ │
│ │ └──────────────────────────┼───────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──── snet-privateendpoints (/24) ─────────────────────┐ │ │
│ │ │ Private Endpoint ──► Storage Account (NFS) │ │ │
│ │ │ ┌────────────────────────┐ │ │ │
│ │ │ │ /data/alice/graph-mcp │ │ │ │
│ │ │ │ /data/bob/graph-mcp │ │ │ │
│ │ │ │ /data/jdoe/graph-mcp │ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘每个用户的容器将令牌和审计日志写入共享NFS挂载上的自己的目录。存储帐户只能通过专用端点访问,不能访问公共网络。
先决条件
- Azure命令行界面 随着
containerapp扩展名:
# Install Azure CLI: https://aka.ms/installazurecli
az extension add --name containerapp --upgrade
az login- Azure 订阅 具有创建权限:
- 资源组、容器注册表、密钥库 - 虚拟网络、专用端点、专用DNS区域 - 容器应用程序环境,容器应用程序 - 存储帐户(高级文件存储)
- Azure AD应用程序注册 --看 Azure AD应用程序注册 上面。你需要 客户端ID 和 租户ID.
- Node.js 22+ 和 npm (用于本地构建/TypeScript编译)
步骤1:配置环境文件
您需要两个环境文件——一个用于应用程序机密,一个用于Azure资源命名。
1a。应用程序秘密(.env)
此文件保存Azure AD应用程序注册凭据。部署脚本读取这些信息以为密钥库机密设置种子。
cp .env.example .env编辑 .env:
# Required: from your Azure AD App Registration
GRAPH_MCP_CLIENT_ID=
GRAPH_MCP_TENANT_ID=1b。Azure资源配置(.env.azure.prod)
此文件定义了所有Azure资源的名称和位置。Makefile在每次部署命令之前都会获取它。
cp .env.azure.example .env.azure.prod编辑 .env.azure.prod:
# Environment label — drives default naming for all resources.
# This is the single source of truth. All resource names are derived from it
# unless explicitly overridden below.
AZURE_ENV_LABEL=prod
# Resource Group — all resources are created in this group
AZURE_RESOURCE_GROUP=rg-myproject-prod
# Location — Azure region for all resources
AZURE_LOCATION=eastus
# Container Registry — stores the Docker image
# Must be globally unique, lowercase, alphanumeric only
AZURE_ACR_NAME=myprojectprodacr
# Key Vault — stores GRAPH_MCP_CLIENT_ID and GRAPH_MCP_TENANT_ID as secrets
# Must be globally unique
AZURE_KEY_VAULT_NAME=kvmyprojectprod
# Log Analytics Workspace — aggregates container logs
AZURE_LAW_NAME=law-myproject-prod
# Container Apps Environment — hosts all per-user Container Apps
AZURE_CONTAINERAPPS_ENV=cae-myproject-prod
# Virtual Network — provides network isolation
AZURE_VNET_NAME=vnet-myproject-prod
# Subnet for Container Apps (must be /23 or larger, delegated to Microsoft.App/environments)
AZURE_SUBNET_NAME=snet-containerapps-prod
# Storage Account — Premium FileStorage for NFS
# Must be globally unique, lowercase, alphanumeric only
AZURE_STORAGE_ACCOUNT=myprojectprodst
# NFS storage mount name in the Container Apps Environment
AZURE_NFS_STORAGE_NAME=myproject-nfs-prod两者 .env 和 .env.azure.* 文件是无效的。仅 .env.example 和 .env.azure.example 被跟踪。
多个环境:使用ENV=dev针对不同的环境: ``bash cp .env.azure.example .env.azure.dev # Edit .env.azure.dev with dev-specific values make deploy ENV=dev``
步骤2:创建共享基础设施
make deploy这运行 azure.sh init 紧随其后 azure.sh buildThe init 命令按顺序创建所有共享基础结构(跳过已存在的资源):
| # | 资源 | 它的作用 |
|---|---|---|
| 1 | 资源组 | 所有资源的容器 |
| 2 | 容器注册表 (基本SKU) | 存储Docker镜像。管理员访问已禁用——容器应用程序通过托管身份进行拉取。 |
| 3 | 密钥库 (RBAC模式) | 存储 GRAPH_MCP_CLIENT_ID 和 GRAPH_MCP_TENANT_ID 作为秘密。每个容器应用程序的系统分配的托管身份 Key Vault Secrets User 角色。您的用户将获得 Key Vault Secrets Officer 播种秘密。 |
| 4 | 日志分析工作区 | 聚合所有用户实例的容器日志。 |
| 5 | 虚拟网络 (10.0.0.0/16) | 网络隔离。容器应用程序中NFS卷装载所需。 |
| 6 | 子网 (10.0.0.0/23) | 委托给 Microsoft.App/environments。托管容器应用程序环境。这 /23 提供约500个IP(Azure CAE最低要求 /23). |
| 7 | 容器应用程序环境 | 共享计算环境 --internal-only true (无公共IP)。通过委派子网连接到ExpressRoute。 |
| 8 | 存储帐户 (高级文件存储) | 支持NFS的文件存储。创建于 publicNetworkAccess=Disabled, allowSharedKeyAccess=false, httpsOnly=false. |
| 9 | NFS文件共享 (data,100 GiB) | 实际的NFS共享。100 GiB是高级文件存储的最低容量。 |
| 10 | 专用端点子网 (10.0.2.0/24) | 专用终结点的独立子网(不能共享CAE子网)。 |
| 11 | 专用终结点 | 将存储帐户连接到ExpressRoute。之所以需要,是因为 publicNetworkAccess=Disabled. |
| 12 | 专用DNS区域 (privatelink.file.core.windows.net) | 解决 .file.core.windows.net 将私有端点的IP地址转移到ExpressRoute中。 |
| 13 | DNS区域链接 | 将私有DNS区域链接到ExpressRoute,以便容器可以解析存储帐户名。 |
| 14 | DNS区域组 | 将专用终结点与DNS区域相关联。自动创建A记录。 |
| 15 | CAE存储架 | 将NFS共享作为命名卷装载到容器应用程序环境中。单个容器按名称引用此装载。 |
| 16 | 密钥库秘密 | 种子 graph-mcp-client-id 和 graph-mcp-tenant-id 从你的 .env 文件。 |
之后 init,the build 命令编译TypeScript源代码,通过ACR任务构建Docker镜像(云构建——不需要本地Docker),并将其推送到注册表。图像同时标记了这两个 latest 以及当前的git short SHA。
步骤3:部署用户
make add-user U=jdoe这将创建一个名为的每个用户的容器应用程序 ca-graph-mcp-gw-- (例如。, ca-graph-mcp-gw-prod-jdoe).创作分为四个阶段:
在部署之前,身份锁定来源于密钥库密钥 -entra-object-id.
- 如果
-entra-object-id存在,直接使用。 - 如果缺失,脚本将解析
${slug}@doh.gov.ae在Entra ID中创建-entra-object-id.
- 创建 --部署具有默认快速入门映像和系统分配的托管标识的容器应用程序。此时身份尚不存在,因此我们无法从ACR中提取或引用密钥库机密。
- 基于角色的访问控制 --授予新的托管身份:
- AcrPull 在容器注册表上(提取图像) - Key Vault Secrets User 在密钥库上(在运行时读取机密) - 等待30秒以传播AAD角色。
- 注册表 --将容器应用程序配置为使用其托管身份(无管理员凭据或访问密钥)从ACR中提取。
- YAML更新 --应用全容器规范:
- ACR的真实图像 - 密钥库秘密参考 GRAPH_MCP_CLIENT_ID 和 GRAPH_MCP_TENANT_ID - 密钥库秘密参考 EXPECTED_AAD_OBJECT_ID (从 -entra-object-id) - NFS卷装载位置 /app/data - 环境变量(HOST, PORT, NODE_ENV, USER_SLUG) - 健康探针(活性、准备状态、启动) - 比例规则: minReplicas=1, maxReplicas=1
如果容器应用程序已经存在, add-user 跳到第4阶段(YAML更新)——这就是您推出图像更新的方式。
成功后,该命令将打印内部FQDN:
✓ ca-graph-mcp-gw-prod-jdoe [prod]
FQDN: ca-graph-mcp-gw-prod-jdoe.internal...azurecontainerapps.io
MCP: https://ca-graph-mcp-gw-prod-jdoe.internal...azurecontainerapps.io/mcp
Health: https://ca-graph-mcp-gw-prod-jdoe.internal...azurecontainerapps.io/health第四步:一次性登录
每个用户必须通过设备代码流进行一次身份验证。这将获取Microsoft Graph令牌并将其持久化到NFS共享。
make login-user U=jdoe此命令:
- 将容器缩放为1个副本(如果当前为零)
- 等待复制副本运行
- 跑
node dist/index.js --login-device集装箱内通过az containerapp exec - 显示设备代码--转到https://microsoft.com/devicelogin并输入它
- 成功登录后,令牌将缓存在
/app/data/jdoe/graph-mcp/tokens/token-cache.json在NFS共享上 - 将比例恢复为零
需要交互式终端 — az containerapp exec 需要一个TTY。您不能从非交互式脚本或CI管道运行此操作。令牌在容器重启和扩展到零周期内持续存在,因为它存储在NFS共享上。
步骤5:通过烟雾测试进行验证
make smoke-user U=jdoe这会放大容器,在其中运行一个内置的烟雾测试套件,然后缩小。烟雾测试验证了所有工具类别的端到端功能:
| # | 检查 | 它测试什么 |
|---|---|---|
| 1 | health | GET /health 返回状态OK和已验证用户 |
| 2 | tools/list | MCP tools/list 返回全部22个工具 |
| 3 | find mail | 图形API邮件搜索工作 |
| 4 | find events | 图表API日历日期范围搜索工作 |
| 5 | find files | 图形API文件搜索工作 |
| 6 | get_file_metadata | 按驱动器/项目ID检索文件元数据 |
| 7 | get_file_content | 文件内容下载(文本内联或二进制base64) |
| 8 | get_email | 按ID检索电子邮件 |
| 9 | get_email_thread | 会话线程获取(通过会话_id和消息_id) |
| 10 | get_event | 按ID检索日历事件 |
| 11 | compose_email | 起草、发送和回复流程 |
| 12 | schedule_meeting | 预览+创建具有自动免费插槽查找功能的会议 |
| 13 | respond_to_meeting | 接受会议邀请 |
预期产出(缩写):
MCP Gateway — Remote Smoke Test
▸ Health check
✓ health
{"status":"ok","user":"jdoe@example.com"}
▸ tools/list
✓ tools/list count = 11
▸ find — mail
✓ find mail
▸ find — events
✓ find events
▸ find — files
✓ find files
✓ get_file_metadata ...
✓ get_file_content ...
▸ get_email / get_email_thread
✓ get_email returned correct ID
✓ get_email_thread correct conversation_id
✓ get_email_thread (by msg_id) ...
▸ compose_email
✓ compose_email draft ...
✓ compose_email send ...
▸ schedule_meeting
✓ schedule_meeting ...
▸ respond_to_meeting
✓ respond_to_meeting accept ...
▸ Results: N passed, 0 failed, 0 warnings
All smoke tests passed!喜欢 login-user,这需要一个交互式终端。步骤6:连接您的AI代理
容器默认为 minReplicas=1 (始终打开)。部署后可立即访问MCP端点。将AI代理的MCP配置指向内部FQDN:
https://ca-graph-mcp-gw-prod-jdoe.internal...azurecontainerapps.io/mcp成本控制:如果你想在长时间缺勤(假期等)期间停止支付计算费用,你可以手动缩减和备份:
# Stop the container (MCP endpoint becomes unreachable)
make scale-down U=jdoe
# Start it again before your next session
make scale-up U=jdoe在0.25 vCPU/0.5 GiB的情况下,始终在线的成本约为每位用户每月15-20美元。
scale-up 等待正在运行的副本并打印FQDN。 scale-down 套 minReplicas=0 --容器在空闲时停止。
将AI代理的MCP配置指向内部FQDN:
https://ca-graph-mcp-gw-prod-jdoe.internal...azurecontainerapps.io/mcp管理用户
# Add more users
make add-user U=alice
make login-user U=alice
# Deploy multiple users at once
# (run add-user for each — the script accepts multiple users)
# bash: set -a; . ./.env.azure.prod; set +a; bash scripts/azure.sh add alice bob carol
# Remove a user (deletes the Container App; NFS data is preserved)
make remove-user U=jdoe
# Check status of all users
make deploy-status
# Check status of a specific user
make deploy-status U=jdoe
# Tail logs
make deploy-logs U=jdoe
# Dry-run: see what exists and what's missing
make deploy-plan更新应用程序
代码更改后,重建映像并更新每个用户的容器应用程序:
# 1. Build and push new image to ACR
make deploy # or: set -a; . ./.env.azure.prod; set +a; bash scripts/azure.sh build
# 2. Update each user (pulls the new image, reapplies the full YAML spec)
make add-user U=jdoe
make add-user U=alice
# 3. Verify
make smoke-user U=jdoeadd-user 在现有的容器应用程序上是一个更新——它重新应用了YAML规范 image: latest,引发新的修订。
环境变量参考
所有变量的默认值均源自 AZURE_ENV_LABEL。只覆盖您需要更改的内容。
| 变量 | 默认值(当 ENV_LABEL=prod) | 描述 |
|---|---|---|
AZURE_ENV_LABEL | dev | 环境名称。驱动所有默认资源名称。 |
AZURE_RESOURCE_GROUP | rg-graph-mcp-{env} | 资源组名称 |
AZURE_LOCATION | eastus | Azure区域 |
AZURE_ACR_NAME | graphmcp{env}acr | 容器注册表名称 |
AZURE_KEY_VAULT_NAME | kvgraphmcp{env} | 密钥库名称 |
AZURE_LAW_NAME | law-graph-mcp-{env} | 日志分析工作区名称 |
AZURE_CONTAINERAPPS_ENV | cae-graph-mcp-{env} | 容器应用程序环境名称 |
AZURE_VNET_NAME | vnet-graph-mcp-{env} | 虚拟网络名称 |
AZURE_SUBNET_NAME | snet-containerapps-{env} | CAE子网名称 |
AZURE_STORAGE_ACCOUNT | graphmcp{env}st | 存储帐户名称 |
AZURE_NFS_STORAGE_NAME | graph-mcp-nfs-{env} | CAE存储架名称 |
派生(不可配置):
| 名称 | 值 | 描述 |
|---|---|---|
APP_PREFIX | ca-graph-mcp-gw-{env} | 容器应用程序名称前缀。用户应用程序已命名 {APP_PREFIX}-{user}. |
容器应用程序名称的长度限制为32个字符。带有前缀 ca-graph-mcp-gw-prod-,用户名最多可包含12个字符。
拆除
# Delete everything — resource group and all resources in it
make deploy-destroy
# Delete only CAE + Log Analytics (preserves ACR, Key Vault, Storage)
set -a; . ./.env.azure.prod; set +a; bash scripts/azure.sh destroy-infradeploy-destroy 通过要求您键入资源组名称来提示确认。
建筑笔记
NFS存储——为什么它在没有共享密钥的情况下工作
存储帐户是通过以下方式创建的 allowSharedKeyAccess=false 和 publicNetworkAccess=Disabled。这与NFS兼容,因为:
- NFS使用网络级身份验证 (
sec=sys),而不是存储帐户密钥。容器应用程序环境通过以下方式挂载共享--storage-type NfsAzureFile它不使用--access-key完全。 - 访问由专用端点控制 --只有ExpressRoute中的资源可以访问存储帐户。
- 这与SMB不同(
AzureFile)安装,这需要allowSharedKeyAccess=true因为它们使用存储帐户密钥进行身份验证。
| 装载类型 | 授权机制 | 需要 allowSharedKeyAccess=true? |
|---|---|---|
NfsAzureFile (NFS) | 网络/网络连接 | 否 |
AzureFile (SMB) | 存储帐户密钥 | 是 |
NFS有特定的要求:
- 高级文件存储 账户
--sku Premium_LRS --kind FileStorage) - 最低100GiB 配额(Azure文件NFS地板)
httpsOnly=false--NFS协议不支持HTTPS- 专用端点 需要时
publicNetworkAccess=Disabled - 专用子网 CAE(委托
Microsoft.App/environments,最小值/23)
仅内部容器应用程序环境
CAE是用 --internal-only true,意思是:
- 未分配公共IP
- 容器应用程序只能从ExpressRoute内部访问
- FQDN使用
.internal.子域名 - 访问MCP端点的AI代理必须在同一网络上(或使用VPN/专用链路)
始终处于默认状态
容器部署为 minReplicas=1 (始终打开),因此MCP端点始终可访问:
- 始终可达:AI代理可以随时调用MCP端点,而不会出现冷启动延迟
- 代币持续存在:存储在NFS共享上,容器重启后仍能存活
- 成本:每位用户每月约15-20美元,0.25 vCPU/0.5 GiB
- 手动缩小可用:
make scale-down U=jdoe套minReplicas=0在长时间缺席期间节省成本。make scale-up U=jdoe把它带回来。
集装箱入口点
Docker镜像以root身份启动,在NFS共享上创建用户特定的目录(/app/data//graph-mcp/tokens/ 和 /app/data//graph-mcp/audit/),然后下降到无特权者 node 用户通过 gosu。这由以下人员处理 scripts/entrypoint.sh.
这 make 和 azure.sh 关系
Makefile提供了两层部署目标:
- 部署目标 (
make deploy,make add-user U=x等)——来源.env.azure.$(ENV)打电话之前azure.sh。将这些用于正常操作。 - Azure目标 (
make azure-init,make azure-add U=x等)--呼叫azure.sh无需获取任何环境变量文件。当您已经导出环境变量或想要使用脚本默认值时,请使用这些变量。
这 azure.sh 脚本是完全幂等的——每个资源创建都受到存在性检查的保护。可以随时重新运行。
______________________________________________________________________
项目结构
src/
auth/ MSAL login, token cache, Graph client
config/ YAML config loader with Zod validation
graph/ Graph API modules (calendar, files, mail)
mcp/ HTTP + stdio MCP JSON-RPC server
tools/ MCP tool definitions (find, get, compose-email, etc.)
utils/ Helpers, audit logger, types, structured logging, smoke test
index.ts Entry point (--smoke, --login-device, --user, --stdio flags)
scripts/
azure.sh Full Azure lifecycle (init, build, add, remove, login, scale, smoke, destroy)
entrypoint.sh Container entrypoint (NFS dir setup + gosu drop)
test-all-tools.sh Local smoke test (all tools including writes)发展
npm run dev # Watch mode
npm run lint # ESLint
npm run format # Prettier
npm run ci # Full CI: lint + format:check + build + test工具合同
看 docs/TOOL_CONTRACT.md 面向代理的完整参考: 工具名称、参数模式、响应形状、多步骤工作流模式、错误代码和写入安全规则。 在配置使用此网关的LLM代理时,将其作为上下文传递。
安全默认值
mail_send,calendar_create_meeting,calendar_respond,calendar_cancel_meeting需要confirm=true- 出站电子邮件收件人已分配域
- 审核日志记录所有写入操作和阻止的尝试
