@cscec3b-mcp/dop-portal-mcp
v0.1.3
Published
DOP门户MCP
Keywords
Readme
@cscec3b-mcp/dop-portal-mcp
DOP门户MCP
由
@cscec/create-dop-mcp脚手架生成,基于 DOP MCP 模板(扫码登录与 token 管理由@cscec/dop-mcp-tool提供)。
目录结构
dop-portal-mcp/
├── package.json # bin 指向 dist/index.cjs;发布自包含(dependencies 为空,esbuild 全内联)
├── scripts/
│ ├── lib.mjs # 构建共用路径:buildTool()(tsc 编译共享库) + buildMcp()(esbuild 打包 + 复制 help-doc.md)
│ ├── build.mjs # yarn build:产出自包含 dist/index.cjs
│ ├── inspect.mjs # yarn dev:MCP Inspector 调试 + esbuild watch(改源码自动 rebuild,Inspector 里重连即生效)
│ ├── typecheck.mjs # yarn typecheck:先编译共享库(.d.ts)再 tsc --noEmit
│ └── gen-json.mjs # yarn gen-json [--env test|pre|prod]:生成 .mcp.npm.json(MCP 客户端配置,npx 拉发布包)
└── src/
├── index.ts # 入口:第一行 import './base/env.js'(解析 --env/--dev-mode,必须最先);createServer() 里每行注册一个工具
├── help-doc.md # help 工具返回的文档:公共鉴权 + 各场景 SOP,新增业务场景时追加子章节
├── base/ # 基建层(被业务依赖、自身不依赖业务 service)
│ ├── env.ts # --env test|pre|prod + --dev-mode(日志)参数解析(Inspector 不继承父进程 env,用参数显式传;--dev 是 Inspector 保留字)
│ ├── config.ts # currentEnv() + 业务网关配置 ENV_BIZ_CONFIG(按环境切 apiBase/appId/tenant)
│ ├── logger.ts # JSON 追加日志到 cwd/mcp-dop-portal.log,仅 DEV_MODE=1 落盘(与 MCP_ENV 解耦)
│ ├── http.ts # 共用 axios 实例;拦截器统一注入 Authorization: bearer <iamToken>(实时读盘、临期自动续期)
│ └── request.ts # dopGet/dopPost/dopGetRaw:解 {code,success,message,data} 信封
├── service/ # 按接口域封装 REST 调用(纯函数,不碰 MCP)
│ └── common.ts # getUserInfo(biz /api/auth/getUserInfo)
└── tools/ # 每个工具一个文件,导出 registerXxx(server)
├── help.ts # 返回 help-doc.md(+共享库文档)
├── portalQrLoginStart.ts # 启动扫码登录(非阻塞,返回 loginUrl)
├── portalQrLoginWait.ts # 阻塞等待扫码完成(登录态加密落盘)
└── getUserInfo.ts # 查当前用户与组织(未登录返回 loggedIn:false + 扫码指引)快速开始
# 仓库根目录装依赖(yarn 1 workspaces,packages/* 自动纳入)
yarn install
# 调试:起 MCP Inspector(浏览器 UI)+ esbuild watch;改源码后 Inspector 里手动重连即可
yarn workspace @cscec3b-mcp/dop-portal-mcp dev
# 构建/类型检查
yarn workspace @cscec3b-mcp/dop-portal-mcp build
yarn workspace @cscec3b-mcp/dop-portal-mcp typecheck环境由 MCP_ENV 决定(test|pre|prod,默认 prod),只切换后端域名;yarn dev 默认 test 并带 DEV_MODE=1。日志开关独立:DEV_MODE=1 才落盘(见 base/logger.ts,空/0 静默)。裸 node dist/index.cjs 不带参数回落 prod 且不写日志。
如何新增一个工具
- service 层:
src/service/<域>.ts封装 REST 调用——用dopGet/dopPost(解{code,success,message,data}信封)或dopGetRaw(不包信封的接口)。不要显式传 token,http.ts 拦截器统一注入。 - tool 层:
src/tools/<工具名>.ts,导出registerXxx(server: McpServer),用 zod 声明inputSchema/outputSchema。声明了outputSchema就必须返回structuredContent(SDK 强制)。参考src/tools/getUserInfo.ts的写法(含「未登录不算工具错误」的降级模式)。 - 注册:
src/index.ts的createServer()里加一行registerXxx(server);。 - 文档:
src/help-doc.md追加场景章节(工具用途/入参约束/分页约定/展示要求)——这是给使用 MCP 的 AI 看的操作指引,工具描述里写「使用前先调用本 MCP 的 help_tool 查看文档」引导 AI 先读它。
改完跑 yarn typecheck;yarn dev 的 watch 会自动 rebuild(含 help-doc.md 复制),Inspector 里重连即生效。
关键约定 / 坑
- esbuild 必须打成 CJS:axios 有动态 require,ESM bundle 会炸;bundle 里
import.meta.url不可用——运行时定位文件用process.cwd()/__dirname(见logger.ts、tools/help.ts)。 - MCP Inspector 不继承父进程 env:server 支持
--env <env>与--dev-mode(日志开关,等价 DEV_MODE=1)参数(src/base/env.ts最先加载解析),inspect.mjs 把环境与日志开关写进启动命令。server 自定义参数避开 Inspector 保留字(--dev/-e/--config等会被 Inspector 吃掉不透传)。 - 19 位 snowflake ID 一律字符串透传(zod 用
z.string()):超 JS Number 安全整数,转数字精度丢失,后端查不到。 - 日志不含 token:请求日志只记 method/url/params/body,不记 headers;日志文件仅
DEV_MODE=1时落盘(与MCP_ENV解耦,prod 也可显式开)。 - 登录态:扫码一次全场景通用,token 临期由共享库自动续期;任何工具返回
HTTP 401/402或token 无效→ 重新扫码登录 → 重试。 - token 分工:业务工具只用 iamToken(拦截器注入);门户 token 只在共享库登录域内部使用,不出库。
- 发布自包含:依赖全在 devDependencies(esbuild 内联),
dependencies为空;files: ["dist"],help-doc.md靠 build 脚本复制进 dist——改文档要重新yarn build。 - yarn 1 的
.yarnrc必须放各 package 目录(不向上查找),registry 指https://registry.npmmirror.com。
发布
yarn workspace @cscec3b-mcp/dop-portal-mcp pub # 发 alpha/当前 tag
yarn workspace @cscec3b-mcp/dop-portal-mcp pub-alpha # 版本号 prerelease(alpha) + 发布
yarn workspace @cscec3b-mcp/dop-portal-mcp pub-release # 版本号 patch + 发布 latest发布前改 package.json 的 name/bin 为实际包名;registry 走 publishConfig.registry。
