feat(outlook): 添加 Outlook MCP 集成支持并优化分页功能

- 新增 NANO_OUTLOOK_MCP_URL 和 NANO_OUTLOOK_MCP_SERVER_ID 环境变量配置
- 实现 Outlook 邮件和日历的分页查询功能,添加安全参数验证
- 为 app-instance 创建脚本添加 Outlook MCP 服务器 ID 参数
- 更新前端 Outlook 页面实现邮件列表和日历事件的分页浏览
- 添加 Git 忽略文件配置和 Docker 挂载路径修复

BREAKING CHANGE: Outlook 集成现在需要配置 MCP URL 和服务器 ID 环境变量
This commit is contained in:
2026-03-16 17:01:58 +08:00
parent 04501fea22
commit b3767dd4ab
20 changed files with 1671 additions and 83 deletions

View File

@ -354,6 +354,9 @@ a2a:planner
- 如果不传 `scope/scopes`,服务会返回该 audience 下允许的全部 scope
- 如果传了 `scope/scopes`,必须是允许 scope 的子集,否则返回 `403 Requested scopes exceed backend permissions`
- 如果 audience 未启用,返回 `403 Audience is not enabled for this backend`
- 当前默认开启 `AUTHZ_MCP_PERMISSIVE_DEFAULT=1`
-`mcp:*` audience会优先放行本次请求里声明的 scopes并自动补 `list_tools`
- 如果你后面要改回严格模式,把这个环境变量设成 `0`
### Token 内省:`POST /oauth/introspect`

View File

@ -39,7 +39,8 @@ ACCESS_TOKEN_TTL_SECONDS = int(os.getenv("AUTHZ_ACCESS_TOKEN_TTL_SECONDS", "3600
PRIVATE_KEY_PATH = Path(os.getenv("AUTHZ_PRIVATE_KEY_PATH", DATA_DIR / "signing_key.pem"))
DEPLOY_API_BASE_URL = os.getenv("DEPLOY_API_BASE_URL", "http://127.0.0.1:8090").rstrip("/")
DEPLOY_API_TOKEN = os.getenv("DEPLOY_API_TOKEN", "").strip()
UPSTREAM_TIMEOUT_SECONDS = float(os.getenv("AUTHZ_UPSTREAM_TIMEOUT_SECONDS", "15"))
UPSTREAM_TIMEOUT_SECONDS = float(os.getenv("AUTHZ_UPSTREAM_TIMEOUT_SECONDS", "90"))
MCP_PERMISSIVE_DEFAULT = os.getenv("AUTHZ_MCP_PERMISSIVE_DEFAULT", "1").strip() not in {"0", "false", "False"}
store = JsonStore(DATA_DIR)
signer = JwtSigner(PRIVATE_KEY_PATH, ISSUER, ACCESS_TOKEN_TTL_SECONDS)
@ -235,10 +236,14 @@ def _issue_token(payload: OAuthTokenRequest) -> OAuthTokenResponse:
raise HTTPException(status_code=403, detail="Backend is disabled")
allowed = _allowed_scopes_for_audience(credential.backend_id, payload.aud)
requested = {item.strip() for item in (payload.scopes or []) if isinstance(item, str) and item.strip()}
if payload.aud.startswith("mcp:") and MCP_PERMISSIVE_DEFAULT:
allowed = set(allowed)
allowed.add("list_tools")
allowed.update(requested)
if not allowed:
raise HTTPException(status_code=403, detail="Audience is not enabled for this backend")
requested = set(payload.scopes or [])
if requested:
if not requested.issubset(allowed):
raise HTTPException(status_code=403, detail="Requested scopes exceed backend permissions")