outsystems-mcp-relay
outsystems-mcp-relay
一个轻量级、通用的 stdio → 远程 MCP 中继,支持 OAuth,并带有 RFC 9207 签发者覆盖功能,用于远程服务器的已发布 OAuth 元数据与授权响应不匹配的情况。零运行时依赖。单文件。
stdio (your MCP client) ⇄ outsystems-mcp-relay ⇄ remote MCP server (Streamable HTTP)为什么需要这个
某些远程 MCP 部署是 Keycloak 前面的反向代理(OutSystems Developer Cloud MCP 网关就是其中之一)。它们发布的 OAuth 元数据中 issuer 是代理 URL(例如 https://<tenant>/mcp),但授权服务器在授权响应的 iss 参数中盖的是其真实签发者(例如 https://<tenant>/auth/realms/<realm>)。
符合 RFC 9207 的客户端必须拒绝这种不匹配,因此 OAuth 登录在几乎所有工具链上都会失败——Claude Code、pi、Cursor、Codex,无一例外。这个中继让你可以针对真实后端签发者验证 iss,同时保持所有其他 OAuth 检查严格不变。对于正常服务器,它的行为就像一个普通中继。
Related MCP server: mcp-auth-proxy
何时使用
先尝试官方直连方式——将你的工具链直接指向远程 MCP URL,中间不经过中继。只有当直连因上述 RFC 9207 签发者不匹配错误而失败时,才考虑使用这个中继。
它的存在仅仅是为了绕开那一个服务端 bug。一旦该 bug 不存在,它并不会比官方路径做得更好——所以如果 OutSystems 在租户范围内修复了它,或者你的租户从一开始就没遇到这个问题,就放弃中继,直接连接。中继会告诉你何时属于这种情况:在成功登录时,它会检查是否确实需要签发者修正,如果不需要,会向 stderr 打印一条说明。不要等着做"是否还需要它"的复查——如果你看到那条说明,立即切回官方直连。
安装
需要 Node.js ≥ 20。无依赖——只有这一个文件。
npm install -g outsystems-mcp-relay # recommended
# or, without a global install:
npx outsystems-mcp-relay <remote-url> ...使用中继不需要克隆此仓库。 从 npm 安装(或使用 npx)即可。克隆仅用于审计源码(一个约 500 行的单文件)或贡献代码。
用法
outsystems-mcp-relay <remote-url> [options]
--as-metadata-url <url> OAuth AS metadata URL (default: discover from remote-url)
--expected-issuer <url> Override the RFC 9207 expected issuer (the proxy fix)
--client-id <id> Pre-registered client id (skips dynamic registration)
--bearer <token> Static bearer token mode (skips OAuth entirely)
--force Ignore cached tokens and re-authenticate
--no-open Print the authorization URL instead of opening a browser
--help Show help通用示例(普通远程服务器)
// mcp.json
{
"mcpServers": {
"my-remote": {
"command": "outsystems-mcp-relay",
"args": ["https://api.example.com/mcp"]
}
}
}OutSystems 示例(签发者不匹配)
{
"mcpServers": {
"outsystems": {
"command": "outsystems-mcp-relay",
"args": ["https://<tenant>.outsystems.dev/mcp"]
}
}
}无需 --expected-issuer——中继会从授权重定向中自动检测真实的授权服务器签发者(Keycloak realm 场景)。只有当自动检测失败时(非 Keycloak 服务器),才添加
--expected-issuer https://<tenant>.outsystems.dev/auth/realms/<realm-id>:OAuth 错误消息会打印 realm——其收到的值就是要使用的值。自动检测只会在跨源候选者的签发者被 realm 自身的元数据(可通过源允许列表访问)确认后才采用;对于真正独立的 IdP 主机,请传入 --expected-issuer(或 --as-metadata-url 以预先加入允许列表)。
首次调用时,中继会打开浏览器进行登录(或使用 --no-open 打印 URL)。令牌会被缓存并自动刷新。
通过 AI 助手安装(粘贴此内容)
粘贴到 Claude Code、pi、Cursor 或任何 AI 工具链中:
Install the OutSystems MCP fix (outsystems-mcp-relay). The official OutSystems
MCP fails OAuth on every AI harness with an RFC 9207 issuer mismatch — a
server-side bug in the gateway's Keycloak setup.
Step 1: run `npm install -g outsystems-mcp-relay`.
Step 2: ask me for my OutSystems tenant hostname (something like
`mycompany.outsystems.dev`).
Step 3: when I tell you, replace the broken server:
`claude mcp remove outsystems`
`claude mcp add -s user --transport stdio outsystems -- outsystems-mcp-relay https://<my-tenant>/mcp`
(substitute my actual tenant. No other flags needed — the relay auto-detects
the real authorization-server issuer; if auto-detection can't, ask me to paste
the OAuth error and use its "received" URL as `--expected-issuer`.)
Step 4: tell me to restart Claude Code, then ask anything OutSystems-related.
The first tool call opens a browser for sign-in (or prints the URL with
`--no-open`).Claude Code 快速入门(OutSystems 签发者不匹配)
你看到的错误是这样的:
Issuer mismatch in authorization response (RFC 9207):
expected "https://<tenant>.outsystems.dev/mcp",
received "https://<tenant>.outsystems.dev/auth/realms/<realm-id>"在终端中(不是在 Claude Code 内部):
npm install -g outsystems-mcp-relay
# 1. remove the broken HTTP entry
claude mcp remove outsystems
# 2. add the relay as a local stdio server — no other flags needed: it
# auto-detects the real authorization-server issuer
claude mcp add -s user --transport stdio outsystems -- \
outsystems-mcp-relay \
https://<tenant>.outsystems.dev/mcp然后重启 Claude Code。在首次调用 OutSystems 工具时,中继会打开浏览器进行登录(如果更愿意粘贴 URL,可添加 --no-open)。令牌会被缓存,因此后续会话会跳过登录。用 /mcp 验证(服务器应显示已连接),并执行一个简单的"列出我的环境"。
你不需要去寻找 realm 签发者。 中继会从授权重定向中自动检测它。如果自动检测无法完成(非 Keycloak 服务器),错误消息会打印出来:错误中的收到的值就是
--expected-issuer的值。
工作原理
协议无关的透传:从 stdin 读取换行分隔的 JSON-RPC,将每个帧原样 POST 到远程服务器,将 JSON-RPC 响应写回 stdout。这里不包含任何工具语义——适用于工具、资源、提示词等一切内容。
处理 Streamable HTTP 细节:
Mcp-Session-Id回显、直接 JSON 响应,以及202/text/event-stream响应(SSE 重组)。OAuth:发现授权服务器元数据,动态注册公共客户端(PKCE S256),打开浏览器,验证
state和iss,交换授权码,在 401 时刷新令牌。--expected-issuer设置iss的验证目标——这是针对代理/Keycloak 不匹配的修复方案。请求被串行化(stdout 上不会出现交错响应)。
安全性
强制执行 RFC 9207:仅当授权服务器实际发送
iss时才进行验证(缺失 = AS 未实现 RFC 9207,不检查;存在 = 与预期签发者进行严格字符串匹配)。--expected-issuer选择不同的预期值——它绝不会禁用验证。源允许列表:中继只联系配置的远程源(以及显式提供的
--as-metadata-url)。重定向被手动遍历,每一跳都在允许列表中(307/308 保留请求体;301/302/303 按 HTTP 语义降级为 GET),并且当重定向改变源时,Authorization/Cookie会被剥离(与原生 fetch 一致)。无 SSRF。PKCE S256 + 随机
state(已验证)+ 仅限 localhost 的回调服务器,使用临时端口。绝不记录机密:令牌和授权码永远不会出现在输出中(所有诊断信息都发送到 stderr;stdout 只承载协议消息)。
令牌存储在
~/.mcp-auth/outsystems-mcp-relay-<sha1(url)>.json,权限为0600—— 这是生态系统的惯例(与mcp-remote的存储格式相同)。操作系统钥匙串存储是计划中的增强功能;参见非目标。
测试
npm test # mock-server protocol test (passthrough, session-id, SSE, 401)
npm run test:e2e -- <remote-url> --expected-issuer <issuer> # real-tenant round trip故障排查
症状 | 修复 |
| 通常自动检测无需任何标志即可处理。如果无法处理,请将收到的 URL 作为 |
长时间空闲后 | 缓存的令牌已过期且刷新失败。使用 |
浏览器从未打开 | 添加 |
动态客户端注册失败 | 服务器的注册端点受限(例如 Keycloak 的 Trusted-Hosts 策略)。如果是 OutSystems 代理,这不应发生;否则请自行注册客户端并传入 |
其他问题 | 提交 issue,附上完整错误文本(所有诊断信息都发送到 stderr——请对任何令牌进行脱敏) |
非目标(v1)
操作系统钥匙串令牌存储(目前使用 0600 权限的文件)
多服务器聚合/管理(请使用网关)
超出透传范围的服务器发起通知流
自定义 CA 标志
许可证
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Hosted OAuth MCP at https://www.taskade.com/mcp, or local @taskade/mcp-server.
Enable secure connectivity between Sentry issues and debugging data, and LLM clients, using a Model Context Protocol (MCP) server.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Related MCP Servers
- AlicenseAqualityCmaintenanceLocal stdio proxy for Uno MCP Gateway that enables MCP clients without OAuth support to securely connect to authenticated remote servers.821 PyPI1MIT
- FlicenseNot gradedqualityDmaintenanceBridges stdio-based LLM harnesses to OAuth-protected remote MCP servers via Streamable HTTP, handling PKCE browser login and token refresh automatically.5 npm-
- AlicenseNot gradedqualityAmaintenanceBridge that lets stdio-only MCP clients connect to remote MCP servers with OAuth and other auth support, enabling local clients to use remote, authorized MCP servers.19 npm54MIT
- FlicenseNot gradedqualityCmaintenanceEnables local MCP clients to access tools from a remote FastMCP server over stdio, handling OAuth authentication and Streamable HTTP communication.-