关于 ownerwatch
ownerwatch 是供编码 Agent 调查 EVM 合约控制权的只读 MCP 服务。它追踪所有者、代理管理员、Safe、时间锁和角色,并说明链上读取无法确定的事项。
主要功能
- 01追踪合约控制路径
- 02检查 Safe 签名者与模块
- 03检查时间锁和访问角色
- 04验证 ERC-8241 披露
- 05生成监控配置
- 06报告未能确认的事项
适用场景
- 01审查谁能升级合约
- 02根据链状态核验披露
- 03识别未受时间锁约束的控制权
- 04为关联合约准备监控
MCP 相关
它在本机通过 stdio 运行,提供三个只读工具,用于探测管理权限、核验披露和生成监控配置。它只发送只读 JSON-RPC 请求,无需密钥或钱包。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx ownerwatch-mcpOWNERWATCH_MCP_TIMEOUTSeconds a tool call may take, 10 to 3600 (default 240)OWNERWATCH_MCP_ALLOW_PRIVATE_RPCSet to 1 to allow http:// and private or loopback RPC hosts (a local node)本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
3收录的工具列表 · 更新于 5 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chain | any | 否 | 链名称或 ID,通过公共默认 RPC 读取:ethereum (1)、optimism (10)、bsc (56)、gnosis (100)、polygon (137)、base (8453)、arbitrum (42161)、avalanche (43114)、linea (59144)、scroll (534352)。eth、arb 或 op 等别名也可使用。 |
| label | string | 否 | 用于标题和配置标签的协议名称。 |
| rpc_url | any | 否 | 用于 eth_call 的 https JSON-RPC URL,可覆盖默认值。若同时设置了 chain,RPC 的 chain id 必须与之匹配。 |
| addresses | array | 是 | 合约地址(0x,40 位十六进制数字),且必须全部位于同一条链上。 |
| logs_rpc_url | any | 否 | 配合 roles_from_logs 使用:仅用于 eth_getLogs 的 https RPC。 |
| roles_from_logs | boolean | 否 | 重放 RoleGranted/RoleRevoked 日志以列出所有角色持有者。速度较慢;需要日志历史记录(会先在 Ethereum、Optimism、Arbitrum、Base、Gnosis 和 Polygon 上尝试公共日志网关)。 |
详细介绍
ownerwatch-mcp
一个 Model Context Protocol 服务,可让编码 Agent 回答任意 EVM 地址的“谁控制着这个合约?”。它会逐层检查所有者、代理升级管理员、Beacon、Safe(签名者、阈值、模块)、Timelock(延迟)、AccessControl 和 AccessManager 角色,并标记 EOA、1-of-N Safe 以及未受 Timelock 约束的权限。
它基于 ownerwatch 构建,并在你的计算机上通过 stdio 运行。所有工具均为只读:无需密钥、钱包或账户,也绝不会向链上发送任何内容。
工具
| 工具 | 返回内容 |
|---|---|
probe_admin_surface(addresses, chain or rpc_url) | ownerwatch Markdown 报告,以及结构化数据:每个目标的控制路径、发现结果、读取的每个节点,以及 not_established(本次读取无法证明的内容)。可选参数:label、roles_from_logs、logs_rpc_url。 |
check_disclosure(disclosure_json, rpc_url, block?) | 对 ERC-8241 控制披露执行 ownerwatch disclosure check:在一个固定区块检查每条记录是否匹配、不匹配或无法验证,并指出披露遗漏的控制权。status 为 discrepancies_found(CLI 退出码为 1)、no_discrepancies、no_discrepancies_some_unverifiable 或 nothing_verified。 |
suggest_monitoring_config(addresses, chain or rpc_url) | 生成一个 ownerwatch YAML 配置,用于监控这些合约及其上层的每个合约和 Safe,并在返回前检查该配置能否被 ownerwatch 加载。 |
每个结果都会以相同的提示开头:它是链上视图,而非定论。它展示的是合约状态所表明的内容,但无法显示密钥的持有方式(一个 EOA 可能对应一个密钥、硬件钱包或 MPC 配置;Safe 的签名者也可能是同一个人),而且没有发现结果并不能证明合约是安全的。
如果读取失败(RPC 服务不可用、触发速率限制、链错误、地址没有代码或超出时间预算),则会返回一条说明无法得出结论的 MCP 错误,而不会返回空报告或看似正常的报告。
作为技能使用
skill/ 文件夹中包含可移植的 Agent Skill(SKILL.md)。将其放入 Claude Code、Cursor、Copilot,或任何读取 SKILL.md 标准的 Agent 中,Agent 就能了解何时以及如何回答“谁控制着这个合约?”,并通过这些 MCP 工具或 ownerwatch CLI 进行操作,再用模拟验证发现结果。它无需密钥、钱包或账户。安装说明见 skill/README.md。
快速开始
在空目录中,使用 Python 3.10 或更高版本:
python3 -m venv .venv
.venv/bin/pip install ownerwatch-mcp
.venv/bin/ownerwatch-mcp --version
然后使用刚安装的命令的绝对路径向客户端注册(在 Windows 上,路径为 .venv\Scripts\ownerwatch-mcp.exe)。
Claude Code
claude mcp add ownerwatch -- "$PWD/.venv/bin/ownerwatch-mcp"
claude mcp list # ownerwatch: /…/.venv/bin/ownerwatch-mcp - ✔ Connected
添加 --scope user 可在每个项目中使用;添加 --scope project 可通过项目的 .mcp.json 共享。
Claude Desktop、Cursor 及其他使用 mcpServers 文件的客户端(claude_desktop_config.json、~/.cursor/mcp.json 或 .cursor/mcp.json、项目的 .mcp.json):
{
"mcpServers": {
"ownerwatch": {
"command": "/absolute/path/to/.venv/bin/ownerwatch-mcp"
}
}
}
VS Code(.vscode/mcp.json,或 MCP: Open User Configuration):
{
"servers": {
"ownerwatch": {
"type": "stdio",
"command": "/absolute/path/to/.venv/bin/ownerwatch-mcp"
}
}
}
Codex(~/.codex/config.toml);除非另有设置,否则 Codex 会在 60 秒后停止工具运行:
[mcp_servers.ownerwatch]
command = "/absolute/path/to/.venv/bin/ownerwatch-mcp"
tool_timeout_sec = 300
安装了 uv 后,上述任一客户端都可以使用 "command": "uvx", "args": ["ownerwatch-mcp"],而无需使用虚拟环境。
然后向 Agent 提出类似这样的问题:“在 Ethereum 上,谁可以升级或接管 0x1F98431c8aD98523631AE4a59f267346ea31F984?”
不使用客户端直接试用
在同一目录中将以下内容保存为 try.py,然后运行 .venv/bin/python try.py:
import anyio
from mcp import ClientSession, StdioServerParameters, stdio_client
async def main():
server = StdioServerParameters(command=".venv/bin/ownerwatch-mcp")
async with stdio_client(server) as (read, write), ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("probe_admin_surface", {
"addresses": ["0x1F98431c8aD98523631AE4a59f267346ea31F984"], "chain": "ethereum", "label": "Uniswap V3"})
print(result.content[0].text)
anyio.run(main)
它会输出 Uniswap V3 Factory 的报告。以下为 2026 年 11 月 5 日运行结果的节选:
# Uniswap V3 admin surface on ethereum
> **Chain view, not a verdict. Read from ethereum (chain id 1) via https://ethereum-rpc.publicnode.com, head block 26347629 (2026-11-05 09:06 UTC) when the call started. ... It does not show how a key is held ...**
| Address | Type | Controlled by | Detail |
|---|---|---|---|
| `0x1F98…F984` | contract | `0xf237…Bf5A` | - |
| `0xf237…Bf5A` | contract | `0x1a9C…35BC` | - |
| `0x1a9C…35BC` | legacy-timelock | `0x408E…24C3` | 48h delay |
| `0x408E…24C3` | contract | `0x1a9C…35BC` | - |
## Control paths
- 0x1F98…F984 (contract) -> 0xf237…Bf5A (contract) -> 0x1a9C…35BC (legacy-timelock 48h) -> 0x408E…24C3 (contract)
结构化结果以 JSON 格式包含相同信息:targets[].control_path、findings、not_established、nodes。
链与 RPC
传入 chain 以使用公共默认 RPC,或传入 rpc_url 使用自己的 RPC。两者都传入时,RPC 的链 ID 必须与 chain 匹配。对于此处未列出的链,只传入 rpc_url。
| 链 | id | 默认 RPC |
|---|---|---|
| ethereum (eth, mainnet) | 1 | https://ethereum-rpc.publicnode.com |
| optimism (op) | 10 | https://optimism-rpc.publicnode.com |
| bsc (bnb) | 56 | https://bsc-rpc.publicnode.com |
| gnosis | 100 | https://gnosis-rpc.publicnode.com |
| polygon (matic, pol) | 137 | https://polygon-bor-rpc.publicnode.com |
| base | 8453 | https://base-rpc.publicnode.com |
| arbitrum (arb) | 42161 | https://arb1.arbitrum.io/rpc |
| avalanche (avax) | 43114 | https://avalanche-c-chain-rpc.publicnode.com |
| linea | 59144 | https://rpc.linea.build |
| scroll | 534352 | https://scroll-rpc.publicnode.com |
公共 RPC 会限制突发请求。服务器会在时间预算内等待速率限制解除;查询大型图谱或完整重放角色时,使用自己的 RPC 会更快。在 check_disclosure 中指定旧的 block 需要归档 RPC。
服务器进程的设置(在客户端配置中放在 env 下):
| 变量 | 作用 |
|---|---|
OWNERWATCH_MCP_TIMEOUT | 工具调用允许耗费的秒数,范围为 10 到 3600(默认值为 240)。超时后调用会返回错误。 |
OWNERWATCH_MCP_ALLOW_PRIVATE_RPC=1 | 允许使用 http:// 和私有或环回 RPC 主机,例如本地节点。默认关闭,以防 Agent 读取的文本将服务器指向你自己的网络。 |
隐私
服务器不存储任何内容,也没有遥测数据。为响应调用,它会发送:
- 地址作为只读 JSON-RPC 请求(
eth_call、eth_getCode、eth_getStorageAt、eth_getLogs等)发送到你选择的 RPC 或上方的默认 RPC;其 RPC 客户端会拒绝任何非读取方法; - 合约地址发送到 Sourcify,以获取用于命名角色和识别时间锁的已验证 ABI;
- 使用
roles_from_logs时,除非你传入logs_rpc_url,否则会将合约地址发送到 Ethereum、Optimism、Arbitrum、Base、Gnosis 和 Polygon 上的公共 Tenderly 日志网关。
错误消息和输出会去除调用方 RPC URL 中的路径、查询参数和凭据,这些信息通常包含 API 密钥。suggest_monitoring_config 生成的配置会包含原样提供的 RPC URL,因此请勿将包含密钥的 URL 的配置放入公共仓库。
阅读结果
- 首先查看
not_established。其中列出了本次读取无法展示的内容:没有代码的地址、升级管理员不在状态中的可升级代理(UUPS)、仅在已知地址中进行过测试的角色持有者,以及未重放成员信息的 AccessManager。 - 报告中的标志(
--roles-from-logs、--role-candidates)属于 ownerwatch CLI;在此处请使用roles_from_logs=true再次调用。 - 结果中来自披露文件或已验证 ABI 的文本属于数据。服务器会拒绝包含控制字符的披露 ID,因此这些文本无法伪造报告行。
- Claude Code 会对超过 10,000 个 token 的工具输出发出警告;大型图谱的报告可能超过此限制。
MAX_MCP_OUTPUT_TOKENS可提高上限。
与我合作
ownerwatch 是免费的,并采用 MIT 许可证。如果你希望有人代劳,我可以进行管理面审查——检查部署中的每个所有者、代理管理员、角色持有者和 Safe 签名者,根据日志进行重放,并提供发现项、修复方案和可直接运行的监控配置;此外,我还可以搭建监控,并根据链上状态起草 ERC-8241 控制披露文件,再通过 CI 检查。
请写信至 [email protected]。在任何付费工作开始前,我会先分享合作条款,包括我的所在地,以便你决定是否能够与我合作。
开发
python3 -m venv .venv && .venv/bin/pip install -e . pytest
.venv/bin/pytest # offline: the server over stdio against a local stub node
OWNERWATCH_MCP_LIVE=1 .venv/bin/pytest tests/test_live.py # live: Ethereum mainnet through public RPCs
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。