yjgr-agent-dns
基于 DNS 的 AI Agent 发现与调用 MCP 服务器(DNS-AID 协议),修好了远程 MCP 的鉴权。
这是 dns-aid/dns-aid-core(Apache-2.0)的二次开发版本。
上游的 MCP 插件在调用需要鉴权的远程 Agent 时,Bearer 只有一种精确写法能通,
OAuth 对 MCP 场景基本不可用,而且所有 401 都被吞成一句 Invocation failed (status: error)。
我们向上游反馈过,没有回应,所以自己修、自己发。
在 Claude Code / Codex 里使用
发布到 PyPI 后,两边都只需要一段配置,不用预装任何东西(依赖 uv)。
Claude Code(写入项目根目录 .mcp.json,或 ~/.claude.json 的 mcpServers):
{
"mcpServers": {
"yjgr-agent-dns": {
"command": "uvx",
"args": ["yjgr-agent-dns-mcp"]
}
}
}
或者一条命令:
claude mcp add yjgr-agent-dns -- uvx yjgr-agent-dns-mcp
Codex(~/.codex/config.toml):
[mcp_servers.yjgr-agent-dns]
command = "uvx"
args = ["yjgr-agent-dns-mcp"]
只做发现和调用时不需要任何环境变量。要发布自己的 Agent 到 DNS 时,
按上游文档配置 DNS_AID_BACKEND 等后端凭据(见 docs/UPSTREAM-README.md)。
鉴权怎么用
远程 Agent 有三种常见鉴权方式,现在三种都能用:
| 远程服务要求 | 怎么调 |
|---|---|
| Bearer token / API key | call_agent_tool(..., credentials={"token": "..."})。auth_type 可省略,会按凭据的键名推断;access_token、bearer_token、api_key 都认。 |
| OAuth2 客户端凭据(机器对机器) | credentials={"client_id": "...", "client_secret": "..."},token 地址来自发现元数据或 auth_config.token_url。 |
| MCP 规范 OAuth(浏览器登录) | 先调一次 agent_login(endpoint),浏览器里完成授权;之后对该服务的所有调用自动带 token、自动刷新,不用再传任何凭据。 |
判断远程服务用哪种:直接调一次,401 的报错里会带服务端的 WWW-Authenticate 挑战和下一步提示,例如:
HTTP 401 Unauthorized from https://agent.example.com/mcp;
WWW-Authenticate: Bearer resource_metadata="https://agent.example.com/.well-known/oauth-protected-resource";
The server uses MCP OAuth: run the agent_login tool for this endpoint (browser login), then retry.
MCP 规范 OAuth 就是 Claude Code / Codex 连远程 MCP 用的那套:
RFC 9728 资源元数据 → RFC 8414 授权服务器元数据 → RFC 7591 动态注册 → 授权码 + PKCE → 回调到本机 → 换 token。
实现直接用官方 mcp SDK 的 OAuthClientProvider,本项目只补了 token 落盘、本机回调服务和浏览器拉起。
新增的 MCP 工具:
agent_login(endpoint, scopes?, client_id?, client_secret?, timeout_seconds?)浏览器登录agent_logout(endpoint)忘掉登录agent_auth_status(endpoint)查看登录状态(不返回 token 值)
list_agent_tools 也补上了 auth_type / auth_config / credentials 参数,上游版本连这个入口都没有。
命令行同样可用:
yjgr-agent-dns agent login https://agent.example.com/mcp
yjgr-agent-dns agent status https://agent.example.com/mcp
yjgr-agent-dns list-tools https://agent.example.com/mcp # 自动用已登录的 token
yjgr-agent-dns call https://agent.example.com/mcp echo -a '{"text":"hi"}' --token XXX
token 存在 ~/.yjgr-agent-dns/oauth/<origin>.json,权限 0600,按服务的 origin 隔离。
相关环境变量
| 变量 | 作用 |
|---|---|
YJGR_AGENT_DNS_HOME |
token 存放根目录(默认 ~/.yjgr-agent-dns) |
YJGR_AGENT_DNS_OAUTH_LOGIN_TIMEOUT |
等浏览器登录的秒数(默认 180) |
YJGR_AGENT_DNS_OAUTH_CALLBACK_PORT |
固定回调端口(默认随机;授权服务器要求固定回调地址时用) |
YJGR_AGENT_DNS_OAUTH_NO_BROWSER |
设为 1 则不自动拉浏览器,只在 stderr 打印授权 URL |
DNS_AID_FETCH_ALLOWLIST |
逗号分隔的主机白名单,白名单内的主机不再受 HTTPS-only 和公网 IP 限制 |
DNS_AID_ALLOW_PRIVATE_NETWORKS |
设为 1 允许 http:// 和内网 / 回环地址(内网部署、内网 IdP 必开) |
内网部署注意
上游的 SSRF 防护把所有 http:// 和私网地址一律拒绝,连白名单都绕不过,
所以 OAuth2 客户端凭据模式的 token 地址不能指向内网 IdP。
现在:白名单的主机不限 scheme;或者整体开 DNS_AID_ALLOW_PRIVATE_NETWORKS=1。
file:// 和 URL 内嵌账号密码依旧拒绝。
相对上游改了什么
全部改动都在 git log 里逐条可查,这里列结论:
- 凭据被静默丢弃:
auth_type为空时上游直接不鉴权。现在按凭据推断类型,或自动用已存的登录。 list_agent_tools没有鉴权参数:补上。- 401 / 403 被吞:从 anyio 的 ExceptionGroup 里解出 HTTP 状态和
WWW-Authenticate,报错说明是没带凭据还是凭据被拒。 - 没有 MCP 规范 OAuth:上游只有 client_credentials。新增
sdk/auth/mcp_oauth.py与三个工具。 - 内网 IdP 被 SSRF 防护拦死:白名单优先于 scheme 检查,新增
DNS_AID_ALLOW_PRIVATE_NETWORKS。 - 依赖声明打架:上游 main 允许
mcp<3,装上 mcp 2.x 后客户端直接报 transport unavailable。现钉死<2.0.0,且mcp、typer、rich进核心依赖,uvx yjgr-agent-dns-mcp零额外参数可用。
包名、模块名、命令名全部改为 yjgr-agent-dns / yjgr_agent_dns。
线上协议标识(X-DNS-AID-* 头、dns-aid-jwks.json、DNS_AID_* 环境变量)刻意没改,
保证和上游工具发布的 DNS 记录互通。
开发
uv venv --python 3.12 && uv pip install -e '.[all,dev]'
scripts/test_per_file.sh # 单元测试(每个文件独立进程,上游测试之间有状态泄漏)
.venv/bin/python -m pytest tests/integration/test_mcp_oauth_e2e.py # OAuth 全流程端到端
.venv/bin/ruff check src/ && .venv/bin/mypy src/yjgr_agent_dns
uv build # dist/ 下出 wheel
tests/testbed/oauth_mcp_target.py 是一个符合 MCP 规范的本地授权服务器 + 受保护 MCP 服务,
可以单独起来手工验证:python tests/testbed/oauth_mcp_target.py 8766,然后
yjgr-agent-dns agent login http://127.0.0.1:8766/mcp。
同步上游:git fetch upstream && git merge upstream/main(remote 名为 upstream)。
许可
Apache-2.0,与上游一致。保留上游 LICENSE 与 NOTICE,修改部分版权归 YJGR。
Metadata
Release files for yjgr-agent-dns 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| yjgr_agent_dns-1.0.0.tar.gz | 1.2 MB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yjgr_agent_dns-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / yjgr_agent_dns-1.0.0.tar.gz
| Download URL | yjgr_agent_dns-1.0.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da788be433377d4b217a529ec9bafb3692754602f1b3ee29a50bbf88b476b65c
|
|
BLAKE2b-256 checksum How to use checksums |
d2ee7197eb717cd19282ade2bae70e9b7e2926f24f90638627cfe3085107f6b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / yjgr_agent_dns-1.0.0-py3-none-any.whl
| Download URL | yjgr_agent_dns-1.0.0-py3-none-any.whl |
|---|---|
| Size | 412.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5586db24e45db7d0da0d3c6c2278da8e48198e92c0f02fe51f7b9839d2c2915a
|
|
BLAKE2b-256 checksum How to use checksums |
5935d16f0af4fa4c467e7801ccd397ea20cdb68dc6518e5c8957c6d56933ea5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for yjgr-agent-dns 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.