脱敏说明:本文已隐去真实公司域名、系统名称、仓库地址、表名、集群名、人员账号和业务数据。文中出现的“数据库平台”“日志平台”“发布平台”“工程平台”等均为抽象称呼,示例字段也经过泛化处理。 背景:浏览器不是所有办公查询的最佳入口 公司内部通常会有很多办公网站:数据库管理平台、日志平台、发布平台、工程管理平台、审批系统、工单系统、监控系统。它们承担了很多关键流程,但日常使用时常常有一个共同问题:用户...
脱敏说明:本文已隐去真实公司域名、系统名称、仓库地址、表名、集群名、人员账号和业务数据。文中出现的“数据库平台”“日志平台”“发布平台”“工程平台”等均为抽象称呼,示例字段也经过泛化处理。
背景:浏览器不是所有办公查询的最佳入口
公司内部通常会有很多办公网站:数据库管理平台、日志平台、发布平台、工程管理平台、审批系统、工单系统、监控系统。它们承担了很多关键流程,但日常使用时常常有一个共同问题:用户只是想查一个结果,却不得不打开页面、等待前端加载、切换菜单、输入筛选条件、翻页、再复制几段信息。
这些操作本身并不复杂,很多时候背后只是几个 HTTP 请求。真正复杂的是:
- 登录态在浏览器里。
- 权限必须跟随用户本人。
- 每个系统的接口风格都不一样。
- 页面更新后,封装能力要能持续维护。
- 不能为了效率绕过安全边界。
于是我们开始做一个本机工具:把内网办公网站中高频、只读、低风险的查询动作封装成 CLI、MCP 和 Codex Skill。
它的目标不是替代原系统,也不是做一个大而全的内部网关,而是把“每天反复点页面查信息”的动作变成结构化、可复用、可被 AI 工具调用的本机能力。
核心原则:只读、本机、本人权限
这个项目一开始就定了三个边界。
第一,只做只读能力。
查询列表、查看详情、查表结构、看少量样例数据、搜索日志、查工程和集群映射,这些可以封装。审批、删除、发布、修改配置、创建分支等会改变服务端状态的动作,不做。
第二,在用户本机运行。
工具不部署成集中服务,也不使用共享账号。每个人在自己的机器上运行,访问范围由自己的账号权限决定。
第三,复用浏览器登录态。
用户已经在 Chrome 或 Edge 里登录了内部系统,工具就从本机浏览器读取目标域名的 Cookie,并用同样的 Cookie 发起只读 HTTP 请求。这样既避免重复登录,也避免在项目里保存密码或长期 token。
换句话说,这个工具不是“绕过浏览器”,而是“把浏览器已经拥有的本人登录态用于更快的只读查询”。
形态:CLI、MCP、Skill 放在同一个仓库
这个项目最终有三个入口,但核心实现只有一份。
-------------------+ +-------------------+
| CLI | | MCP Server |
| dogdog action ... | | tools/call |
+---------+---------+ +---------+---------+
| |
+-------------+--------------+
|
+---------v----------+
| Action Registry |
| shared capability |
+---------+----------+
|
+---------v----------+
| Site Adapters |
| database/log/etc. |
+---------+----------+
|
+---------v----------+
| Intranet Websites |
+--------------------+
Codex Skill:
- 项目说明
- 接入流程
- 安全边界
- 站点文档索引
CLI 适合人直接调试和日常使用。比如:
dogdog capabilities
dogdog action database.find_table --json-input '{"table":"example_table"}' --json
dogdog action logs.query --json-input '{"keyword":"exampleKeyword","startAt":"2026-01-01 10:00:00","endAt":"2026-01-01 10:30:00"}' --json
MCP 适合接入 AI 客户端,让模型在用户授权的本机环境里调用这些只读能力。
Skill 则不是运行时逻辑。它更像一本给 Codex 看的项目手册:什么时候应该使用这个项目、怎么和用户一起浏览页面、怎么提取接口、哪些能力不能封装、文档写在哪里。
一个关键设计是:业务能力只实现一次,放在共享 action registry 里。CLI 和 MCP 都只是薄入口,Skill 只保存工作流和规范,不复制业务逻辑。
最关键的一步:打通浏览器登录态
内网工具最容易卡住的地方不是发 HTTP 请求,而是登录态。
如果让每个用户手动复制 Cookie,体验很差,也不安全。如果要求用户配置账号密码,更不合适。更自然的方式是读取本机浏览器已经保存的 Cookie。
以 Chromium 系浏览器为例,Cookie 存在本机 profile 目录里,但敏感值会被系统加密。macOS 上通常需要通过 Keychain 获取浏览器的 Safe Storage 密钥,再解密 Cookie;Windows 和 Linux 则分别有自己的系统密钥机制。
这个项目里的运行时策略大致是:
- 根据站点配置确认允许读取的目标域名。
- 在 Chrome / Edge 的常见 profile 中查找 Cookie 数据库。
- 复制一份 Cookie 数据库到临时目录,避免直接锁住浏览器文件。
- 只查询目标域名相关 Cookie。
- 解密后拼成请求头。
- 请求结束后不保存 Cookie,不打印 Cookie。
这里有一个很重要的小原则:工具不扫描所有 Cookie,不把 Cookie 当配置持久化,也不在日志里输出原始登录态。它只是按站点白名单临时读取、临时使用。
如何新增一个能力:和用户一起操作浏览器
这个项目的迭代方式也挺有意思。它不是靠猜接口,也不是先写一个抽象框架再慢慢填空,而是从真实页面出发。
一次能力接入通常这样走:
- 用户说清楚目标:想查什么,页面在哪里,最后希望得到什么。
- 用户和 Codex 一起打开浏览器,按日常方式操作一遍。
- 观察页面展示和 Network 请求。
- 区分核心接口、静态资源、埋点、菜单配置、噪音请求。
- 找到最小稳定的只读 HTTP 请求。
- 记录页面流程、请求参数、响应结构、只读判断。
- 在站点 adapter 中封装成 action。
- 同时验证 CLI 和 MCP 能看到这个 action。
- 更新 Skill 和站点文档,方便下一次继续维护。
这个流程的价值在于:用户懂业务,Codex 擅长从页面和代码中抽象接口。两者一起走一遍真实操作,通常能很快把“网页里的按钮”提炼成“可复用的读能力”。
一个能力应该长什么样
一个好的 action 不应该暴露任意 URL,也不应该让调用方拼业务系统的内部参数。它应该接收业务语义明确的输入,返回结构化输出。
比如“搜索工程和集群映射”可以抽象成:
{
"keyword": "example-service",
"groupName": "",
"includeUnavailable": true
}
输出则整理成:
{
"total": 1,
"items": [
{
"repository": "example-group/example-service",
"moduleName": "example-service",
"clusterName": "example_cluster",
"moduleType": "service",
"gitProjectId": 12345,
"gitSshUrl": "git@example.internal:example-group/example-service.git",
"searchStatusName": "available"
}
]
}
调用者不需要知道底层页面是哪个弹窗,也不需要关心底层请求叫 moduleName 还是 keyword。这些都由 adapter 消化。
目前接入过的几类能力
为了验证这套方式,我们陆续封装了几类典型只读场景。
数据库平台:
- 查询当前用户可见的数据库实例。
- 按表名定位表在哪个库。
- 查询表结构。
- 执行受限的只读
SELECT。
- 预览少量数据。
日志平台:
- 查询业务域和集群枚举。
- 按关键词、时间范围、集群搜索日志。
- 对容易超时的大范围查询做分片限制。
发布 / 云平台:
- 查询集群列表。
- 查询集群分组。
- 查询实例信息。
- 查询集群总览。
工程平台:
- 搜索工程、仓库和集群之间的映射。
- 识别当前用户对工程的可用状态。
这些能力有一个共同点:它们都能显著减少打开浏览器、等待页面、复制信息的时间;同时它们又都是读取信息,不改变服务端状态。
安全边界比功能更重要
这个项目越好用,越需要克制。
我们给它设了几条硬规则:
- 不做写操作。
- 不暴露任意 URL fetch。
- 查询型 POST 必须逐个记录为只读接口。
- SQL 只允许单条 SELECT,拒绝多语句、DDL、DML 和危险关键字。
- 不保存密码、Cookie、token。
- 不在文档、日志、fixture 里留下真实敏感数据。
- 能力输出尽量结构化,必要字段脱敏。
- MCP tool schema 必须明确,不能让模型自由拼接内部接口。
尤其是 MCP 场景,工具越容易被模型调用,schema 和边界越要清晰。模型应该调用“查某张表结构”这样的业务能力,而不是拿到一个“请求任意内部 URL”的万能工具。
项目局部上下文:让工具越用越顺
实际使用中还有一个小发现:很多查询不是孤立的。
比如某个业务项目经常要查同一批数据库表、同一个日志集群、同一组服务和工程映射。如果每次都重新找这些关系,效率还是不够高。
所以项目里引入了“用户项目本地上下文”的概念:在用户自己的业务工程目录里维护一个轻量说明文件,记录稳定的、非敏感的关联信息,例如:
- 常查表名和数据库位置。
- 常用日志业务域、集群和关键词。
- 服务名、工程名、仓库名、集群名的映射。
- 排查问题时积累下来的路径提示。
这个文件不属于工具安装目录,也不存任何凭据。它更像一张本地索引,让 AI 和用户下次在同一个业务项目里工作时少走弯路。
为什么要把 Skill 放进同一个仓库
这个项目不只是一个 CLI,也不只是一个 MCP server。它还有一个很重要的维护问题:能力会越来越多,接入方式需要一致。
把 Codex Skill 放在仓库根目录,有几个好处:
- Codex 能直接知道项目规范。
- 新增能力时能自动找到文档、源码、测试入口。
- 运行时和开发期边界写得清楚。
- 项目分发时,CLI、MCP、Skill、文档一起走。
Skill 的职责不是实现功能,而是告诉 Codex:
- 这是一个只读内网工具项目。
- 每个能力要放进共享 action registry。
- CLI 和 MCP 不重复业务逻辑。
- 浏览器控制只用于开发期发现接口。
- 运行时必须自己读取默认浏览器登录态。
- 哪些东西不能提交、不能打印、不能封装。
这让项目在持续增长时不容易散。
一点工程取舍
为了启动快、维护轻,项目一开始没有引入复杂框架。Node 直接运行 TypeScript 源码,CLI 和 MCP 共享同一套 registry。站点 adapter 只是普通模块,按需注册 action。
这种选择有几个现实收益:
- 启动快。
- 调试简单。
- 新增站点成本低。
- 不需要先理解一个很重的插件体系。
- 后续如果要加测试、缓存、TUI 或本地 Web UI,也可以在核心 registry 上扩展。
当然,轻量不等于随意。真正要守住的是能力定义、认证读取、安全策略、文档记录这几条主线。
总结
这个项目的核心不是“把网页接口扒出来”这么简单,而是建立一套可持续的本机只读能力生产方式:
- 用户在浏览器里完成真实操作。
- Codex 协助观察页面和请求。
- 把最小稳定的读接口封装成 action。
- CLI 和 MCP 共享能力。
- Skill 记录流程和边界。
- 运行时复用用户自己的浏览器登录态。
- 权限始终跟随用户本人。
它解决的是一个很日常、但很有价值的问题:让内部工具从“只能在浏览器里慢慢点”变成“可以被命令行、AI 和自动化上下文可靠调用”的结构化能力。
更重要的是,这条路没有要求重建所有系统,也没有要求目标系统立刻提供完美开放 API。它只是从高频只读场景开始,一点点把已经存在的页面能力沉淀成更快、更稳、更适合协作的本机工具。
这可能是内部效率工具最舒服的一种形态:小、快、克制,但能不断长出新的爪子。