tyc-cli 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -42
- package/dist/commands/capabilities.js +32 -0
- package/dist/commands/category.js +15 -75
- package/dist/commands/coreCall.js +23 -0
- package/dist/commands/init.js +21 -38
- package/dist/commands/layers.js +13 -1
- package/dist/commands/login.js +129 -55
- package/dist/commands/output.js +60 -0
- package/dist/config.js +111 -11
- package/dist/coreClient.js +2 -2
- package/dist/index.js +73 -85
- package/dist/oauth.js +70 -0
- package/dist/oauthPending.js +41 -0
- package/dist/types.js +3 -4
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/dist/mcpClient.js +0 -146
- package/dist/session.js +0 -47
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
## 📖 项目简介
|
|
13
13
|
|
|
14
|
-
`tyc-cli` 是天眼查 MCP Server
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
`tyc-cli` 是天眼查 MCP Server 的官方命令行客户端。CLI 通过 shared core HTTP 入口
|
|
15
|
+
调用天眼查 162 个业务语义聚合工具,覆盖企业工商、知产、司法风险、董监高等全维度
|
|
16
|
+
商业数据。
|
|
17
17
|
|
|
18
18
|
当前 MCP Server 的 `tools/list` 默认只公开少量 AI Agent 入口工具
|
|
19
19
|
(搜索、公司画像、能力目录、`call_tool` / `call_tools_batch` 等),但 162 个业务语义
|
|
@@ -22,11 +22,11 @@ Streamable HTTP)调用天眼查 162 个业务语义聚合工具,覆盖企业
|
|
|
22
22
|
|
|
23
23
|
**核心特点**:
|
|
24
24
|
|
|
25
|
-
- 🧠 **
|
|
25
|
+
- 🧠 **Shared Core 客户端架构**:CLI 只做命令解析与参数透传;多源合并、时间戳格式化、
|
|
26
26
|
空结果归一化、`_summary` 注入等业务逻辑由 MCP Server 完成
|
|
27
27
|
- 🔌 **公网优先**:默认连接天眼查 MCP 端点 `https://mcp.tianyancha.com/mcp`;
|
|
28
28
|
支持 `--url` 指向本地或私有部署
|
|
29
|
-
- 🔄
|
|
29
|
+
- 🔄 **无本地 MCP Session**:CLI 调用 stateless shared core endpoint,不缓存 `Mcp-Session-Id`
|
|
30
30
|
- 🎯 **6 大业务分类 / 162 个工具**:企业基础信息 · 风险合规 · 知识产权 · 经营与公示 · 历史信息 · 董监高
|
|
31
31
|
- 🤖 **AI Agent 友好**:tyc 英文 key 透传 / 时间戳格式化 / `_summary / _empty / _warnings` 元数据
|
|
32
32
|
|
|
@@ -61,6 +61,10 @@ tyc login
|
|
|
61
61
|
tyc login --url "https://ai-mcp-pre.tianyancha.com/mcp"
|
|
62
62
|
tyc login --url "http://localhost:8080/mcp" --issuer "http://localhost:8080/oauth"
|
|
63
63
|
|
|
64
|
+
# 非阻塞 OAuth:只打印授权 URL,不占住命令行;授权后手动传回 callback code
|
|
65
|
+
tyc login --no-open --no-block
|
|
66
|
+
tyc login --callback-token "<callback_code_or_full_callback_url>"
|
|
67
|
+
|
|
64
68
|
# API key 兼容路径:已有天眼查 OpenAPI token 时可直接写入
|
|
65
69
|
tyc init --authorization "YOUR_API_TOKEN"
|
|
66
70
|
|
|
@@ -70,9 +74,14 @@ tyc init --authorization "YOUR_API_TOKEN" --no-verify
|
|
|
70
74
|
|
|
71
75
|
> `tyc login` 会从 MCP protected-resource metadata 发现 OAuth 授权服务器,启动本地
|
|
72
76
|
> loopback 回调,使用 PKCE 授权码流程打开浏览器登录;成功后自动把
|
|
73
|
-
> `Authorization: Bearer <access_token>`
|
|
74
|
-
>
|
|
75
|
-
>
|
|
77
|
+
> `Authorization: Bearer <access_token>` 和 refresh token 上下文写入
|
|
78
|
+
> `~/.tyc/config.json`。后续业务命令会在 access token 临期时自动刷新;
|
|
79
|
+
> 如果服务端返回 401,会强制刷新一次并重试当前请求。
|
|
80
|
+
> `tyc login --no-open --no-block` 会打印授权 URL 后立即退出,并把一次性 PKCE 上下文
|
|
81
|
+
> 保存到 `~/.tyc/oauth_pending.json`;完成浏览器授权后,页面会跳转到天眼查 CLI
|
|
82
|
+
> 回调页并展示可复制命令,执行 `tyc login --callback-token "<...>"` 完成换 token。
|
|
83
|
+
> `tyc init` 保存配置后会立即校验 shared core endpoint;失败则退出码 1 并提示连通性问题。
|
|
84
|
+
> 加 `--no-verify` 可跳过校验。
|
|
76
85
|
|
|
77
86
|
配置存于 `~/.tyc/config.json`(权限 600):
|
|
78
87
|
|
|
@@ -81,13 +90,24 @@ tyc init --authorization "YOUR_API_TOKEN" --no-verify
|
|
|
81
90
|
"url": "https://mcp.tianyancha.com/mcp",
|
|
82
91
|
"headers": {
|
|
83
92
|
"Authorization": "Bearer <OAuth_ACCESS_TOKEN>"
|
|
93
|
+
},
|
|
94
|
+
"oauth": {
|
|
95
|
+
"tokenEndpoint": "https://ai.tianyancha.com/oauth/token",
|
|
96
|
+
"clientId": "<OAUTH_CLIENT_ID>",
|
|
97
|
+
"refreshToken": "<OAUTH_REFRESH_TOKEN>",
|
|
98
|
+
"resource": "https://mcp.tianyancha.com/mcp",
|
|
99
|
+
"accessTokenExpiresAt": 1760000000000
|
|
84
100
|
}
|
|
85
101
|
}
|
|
86
102
|
```
|
|
87
103
|
|
|
104
|
+
`tyc init --authorization <token>` 会切回 API key 兼容路径,并清除 `oauth` 刷新上下文。
|
|
105
|
+
|
|
88
106
|
### 4. 开始查询
|
|
89
107
|
|
|
90
108
|
```bash
|
|
109
|
+
tyc company companies "北京百度网讯科技有限公司" --head 40
|
|
110
|
+
tyc company capabilities 2319755677 --company-name "北京百度网讯科技有限公司"
|
|
91
111
|
tyc company registration-info "北京百度网讯科技有限公司"
|
|
92
112
|
tyc risk dishonest-info "..." --md
|
|
93
113
|
tyc executive personnel-dishonest "..." --humanName "张三"
|
|
@@ -101,13 +121,14 @@ tyc executive personnel-dishonest "..." --humanName "张三"
|
|
|
101
121
|
|
|
102
122
|
| 命令 | 说明 |
|
|
103
123
|
|------|------|
|
|
104
|
-
| `tyc login` | 浏览器 OAuth 登录;自动发现 metadata、使用 PKCE 获取 access token 并写入配置 |
|
|
124
|
+
| `tyc login` | 浏览器 OAuth 登录;自动发现 metadata、使用 PKCE 获取 access token / refresh token 并写入配置 |
|
|
125
|
+
| `tyc login --no-open --no-block` | 打印 OAuth 授权 URL 并立即退出,授权完成后按天眼查 CLI 回调页提示继续 |
|
|
126
|
+
| `tyc login --callback-token <code-or-url>` | 使用上一步保存的 PKCE 上下文,把回调 code 换成 access token |
|
|
105
127
|
| `tyc login --url <url>` | 对指定 MCP endpoint 发起 OAuth 登录 |
|
|
106
|
-
| `tyc init --authorization <token>` | 写入 `headers.Authorization
|
|
128
|
+
| `tyc init --authorization <token>` | 写入 `headers.Authorization`;保存后会立即校验 shared core 连通性 |
|
|
107
129
|
| `tyc init --url <url>` | 设置 MCP endpoint |
|
|
108
130
|
| `tyc init --header K=V` | 注入自定义 header(可重复);值留空则删除该 key |
|
|
109
131
|
| `tyc init --no-verify` | 仅写配置,跳过连通性校验(离线配置场景) |
|
|
110
|
-
| `tyc init --clear-session` | 清除本地 session 缓存 |
|
|
111
132
|
| `tyc --help` | 显示 6 个分类总览 |
|
|
112
133
|
| `tyc <category> --help` | 显示某分类下全部命令 |
|
|
113
134
|
| `tyc <category> <method> --help` | 显示具体命令的入参说明 |
|
|
@@ -122,7 +143,7 @@ tyc executive personnel-dishonest "..." --humanName "张三"
|
|
|
122
143
|
| `--pretty` | 同默认;保留 flag 以保持向后兼容 / 显式声明意图 |
|
|
123
144
|
| `--compact` | 紧凑单行 JSON(旧默认行为;管道 / `jq` 场景) |
|
|
124
145
|
| `--md` | Markdown 表格化输出(人类阅读 / Agent 上屏) |
|
|
125
|
-
| `--verbose` | 打印
|
|
146
|
+
| `--verbose` | 打印 shared core 请求详情到 stderr(与上述输出格式正交) |
|
|
126
147
|
|
|
127
148
|
#### 输出截断 / 落盘(与上述输出格式正交,可叠加任意子命令)
|
|
128
149
|
|
|
@@ -181,6 +202,8 @@ tyc company registration-info "百度" --compact | jq .name
|
|
|
181
202
|
### 企业基础信息(company,49)
|
|
182
203
|
|
|
183
204
|
```bash
|
|
205
|
+
tyc company companies "北京百度网讯科技有限公司" # 先锚定主体,复制返回 items[*].id
|
|
206
|
+
tyc company capabilities 2319755677 --company-name "北京百度网讯科技有限公司" # 获取可调用 tool_name 白名单
|
|
184
207
|
tyc company registration-info "北京百度网讯科技有限公司" # 工商登记
|
|
185
208
|
tyc company actual-controller "..." # 实际控制人
|
|
186
209
|
tyc company beneficial-owners "..." # UBO
|
|
@@ -254,22 +277,21 @@ tyc executive person-risk-overview "..." --humanName "张三"
|
|
|
254
277
|
|
|
255
278
|
```
|
|
256
279
|
┌─────────────┐ ┌─────────────────────────────┐ ┌─────────────────┐
|
|
257
|
-
│ tyc-cli │ ──
|
|
258
|
-
│ (npm / TS) │
|
|
280
|
+
│ tyc-cli │ ──HTTP──▶│ 天眼查 MCP Server │ ──HTTP──▶│ tyc OpenAPI │
|
|
281
|
+
│ (npm / TS) │ ◀──JSON──│ (/v1/core/tools/call) │ ◀───────│ │
|
|
259
282
|
└─────────────┘ └─────────────────────────────┘ └─────────────────┘
|
|
260
283
|
│ │
|
|
261
284
|
│ └─ 多源并发聚合 · 时间戳格式化 · _summary 注入 · 空结果归一化
|
|
262
285
|
│
|
|
263
|
-
└─ 仅命令树 · 参数透传 ·
|
|
286
|
+
└─ 仅命令树 · 参数透传 · pretty/--md/--compact 呈现 · --head/--tail/--full/--threshold/--output-file 截断与落盘
|
|
264
287
|
```
|
|
265
288
|
|
|
266
289
|
**CLI 的职责**:
|
|
267
290
|
|
|
268
291
|
1. 解析命令行(commander)
|
|
269
|
-
2. 组装
|
|
270
|
-
3.
|
|
271
|
-
4.
|
|
272
|
-
5. 格式化输出(默认 pretty / `--compact` / `--md`),并可叠加 `--head/--tail/--full/--threshold/--output-file` 做截断与落盘
|
|
292
|
+
2. 组装 shared core `/v1/core/tools/call` 请求,透传 `Authorization` header
|
|
293
|
+
3. 解析 shared core 响应并包装成统一输出结构
|
|
294
|
+
4. 格式化输出(默认 pretty / `--compact` / `--md`),并可叠加 `--head/--tail/--full/--threshold/--output-file` 做截断与落盘
|
|
273
295
|
|
|
274
296
|
**CLI 不做**:
|
|
275
297
|
|
|
@@ -292,10 +314,9 @@ tyc-cli/
|
|
|
292
314
|
│
|
|
293
315
|
└── src/
|
|
294
316
|
├── index.ts # CLI 入口(commander 注册)
|
|
295
|
-
├── types.ts # Catalog /
|
|
317
|
+
├── types.ts # Catalog / tool result 类型
|
|
296
318
|
├── config.ts # ~/.tyc/config.json 读写 · 环境变量兜底
|
|
297
|
-
├──
|
|
298
|
-
├── mcpClient.ts # MCP JSON-RPC client · SSE 解析 · 失效重建
|
|
319
|
+
├── coreClient.ts # Shared Core HTTP client · ready/auth 校验 · tools/call
|
|
299
320
|
├── oauth.ts # OAuth metadata discovery · PKCE · loopback callback
|
|
300
321
|
├── registry.ts # 读取打包内 catalog.json(命令树元数据)
|
|
301
322
|
├── catalog.json # 命令元数据:name / group / cliMethod / params
|
|
@@ -310,26 +331,11 @@ tyc-cli/
|
|
|
310
331
|
|
|
311
332
|
---
|
|
312
333
|
|
|
313
|
-
## ⚙️
|
|
314
|
-
|
|
315
|
-
| 场景 | 行为 |
|
|
316
|
-
|------|------|
|
|
317
|
-
| 首次调用 | `initialize` → 读 `Mcp-Session-Id` header → 存 `~/.tyc/session.json` |
|
|
318
|
-
| 24h 内复用 | 直接用缓存 `sessionId`,跳过 `initialize` |
|
|
319
|
-
| 缓存过期(>24h) | 自动 re-initialize,用户无感 |
|
|
320
|
-
| 服务端主动失效(404/410/"session not found") | 删缓存 → 重建 → 重试 1 次 |
|
|
321
|
-
| `tyc init` 变更 url / Authorization | 配置写入后自动清掉旧 session |
|
|
322
|
-
|
|
323
|
-
`~/.tyc/session.json` 示例:
|
|
334
|
+
## ⚙️ Shared Core 调用
|
|
324
335
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
"sessionId": "mcp-session-xxx",
|
|
329
|
-
"initializedAt": 1777272039739,
|
|
330
|
-
"protocolVersion": "2024-11-05"
|
|
331
|
-
}
|
|
332
|
-
```
|
|
336
|
+
CLI 不直接维护 MCP session,也不会写 `~/.tyc/session.json`。所有业务命令统一调用
|
|
337
|
+
`/v1/core/tools/call`;`tyc init` 用 `/v1/core/ready` 做连通性校验,`tyc login`
|
|
338
|
+
用 `/v1/core/auth/ready` 做鉴权校验。
|
|
333
339
|
|
|
334
340
|
---
|
|
335
341
|
|
|
@@ -355,7 +361,7 @@ tyc-cli/
|
|
|
355
361
|
|
|
356
362
|
| 维度 | `tyc-cli` | 天眼查 MCP Server |
|
|
357
363
|
|-----|---------|-------------------|
|
|
358
|
-
| 协议 |
|
|
364
|
+
| 协议 | Shared Core HTTP client | MCP server |
|
|
359
365
|
| 实现 | TypeScript | Go |
|
|
360
366
|
| 职责 | 命令树 · 参数透传 · 格式化输出 | 多源聚合 · 时间戳格式化 · 元数据注入 · Authorization 透传至 OpenAPI |
|
|
361
367
|
| 运维 | 用户本地安装 | 默认连接官方托管 `mcp.tianyancha.com`,也可切到本机 apimcp 或用户自建 |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { callCoreToolWithOAuthRefresh } from "./coreCall.js";
|
|
2
|
+
import { emitToolResult } from "./output.js";
|
|
3
|
+
const TOOL_NAME = "get_company_capabilities";
|
|
4
|
+
export function registerCompanyCapabilitiesCommand(companyCmd, program) {
|
|
5
|
+
companyCmd
|
|
6
|
+
.command("capabilities")
|
|
7
|
+
.description("[DISCOVER] 输入 search_companies 候选表 company_id,返回该企业可直接执行的 tyc CLI 工具清单")
|
|
8
|
+
.argument("<company_id>", "search_companies 候选表 items[*].id 中的数字企业 ID")
|
|
9
|
+
.option("--company-name <value>", "企业名称(可选,仅用于展示和后续主体参数示例)")
|
|
10
|
+
.action(async (companyID, options) => {
|
|
11
|
+
const args = {
|
|
12
|
+
company_id: companyID,
|
|
13
|
+
};
|
|
14
|
+
if (options.companyName !== undefined) {
|
|
15
|
+
args.company_name = options.companyName;
|
|
16
|
+
}
|
|
17
|
+
const opts = program.opts();
|
|
18
|
+
const verbose = !!opts.verbose;
|
|
19
|
+
try {
|
|
20
|
+
const result = await callCoreToolWithOAuthRefresh(TOOL_NAME, args, {
|
|
21
|
+
verbose,
|
|
22
|
+
format: "markdown",
|
|
23
|
+
});
|
|
24
|
+
emitToolResult(result, opts, TOOL_NAME);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28
|
+
console.error(`请求失败: ${msg}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { getCategories, getToolsByGroup } from "../registry.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { registerCompanyCapabilitiesCommand } from "./capabilities.js";
|
|
3
|
+
import { callCoreToolWithOAuthRefresh } from "./coreCall.js";
|
|
4
|
+
import { emitToolResult } from "./output.js";
|
|
5
|
+
const GLOBAL_OUTPUT_HELP = `
|
|
6
|
+
全局输出选项:所有工具命令均支持 --md / --compact / --pretty / --head / --tail / --full / --threshold / --output-file / --verbose;完整说明见 tyc --help。
|
|
7
|
+
`;
|
|
7
8
|
export function registerCategoryCommands(program) {
|
|
8
9
|
for (const cat of getCategories()) {
|
|
9
10
|
const tools = getToolsByGroup(cat.group);
|
|
@@ -11,6 +12,9 @@ export function registerCategoryCommands(program) {
|
|
|
11
12
|
const catCmd = program
|
|
12
13
|
.command(cat.group)
|
|
13
14
|
.description(`${cat.name_zh}(${cat.tool_count} 个 · L0=${layerCounts.L0} L1=${layerCounts.L1} L2=${layerCounts.L2} L3=${layerCounts.L3})`);
|
|
15
|
+
if (cat.group === "company") {
|
|
16
|
+
registerCompanyCapabilitiesCommand(catCmd, program);
|
|
17
|
+
}
|
|
14
18
|
for (const tool of tools) {
|
|
15
19
|
bindMethod(catCmd, tool, program);
|
|
16
20
|
}
|
|
@@ -37,11 +41,12 @@ function bindMethod(catCmd, tool, program) {
|
|
|
37
41
|
methodCmd = methodCmd.argument(`<${positional.name}>`, positional.description);
|
|
38
42
|
}
|
|
39
43
|
for (const p of remainingRequired) {
|
|
40
|
-
methodCmd = methodCmd.requiredOption(`--${p.name} <value>`, p.description);
|
|
44
|
+
methodCmd = methodCmd.requiredOption(`--${p.name} <value>`, `必填;${p.description}`);
|
|
41
45
|
}
|
|
42
46
|
for (const p of optionalParams) {
|
|
43
47
|
methodCmd = methodCmd.option(`--${p.name} <value>`, p.description);
|
|
44
48
|
}
|
|
49
|
+
methodCmd.addHelpText("after", GLOBAL_OUTPUT_HELP);
|
|
45
50
|
methodCmd.action(async (posVal, options) => {
|
|
46
51
|
const args = {};
|
|
47
52
|
if (positional && posVal)
|
|
@@ -50,14 +55,11 @@ function bindMethod(catCmd, tool, program) {
|
|
|
50
55
|
if (options[p.name] !== undefined)
|
|
51
56
|
args[p.name] = options[p.name];
|
|
52
57
|
}
|
|
53
|
-
const cfg = resolveConfig();
|
|
54
58
|
const opts = program.opts();
|
|
55
59
|
const verbose = !!opts.verbose;
|
|
56
60
|
try {
|
|
57
|
-
const result =
|
|
58
|
-
|
|
59
|
-
: await callCoreTool(cfg, tool.name, args, { verbose });
|
|
60
|
-
emit(result, opts, tool.name);
|
|
61
|
+
const result = await callToolWithOAuthRefresh(tool.name, args, verbose);
|
|
62
|
+
emitToolResult(result, opts, tool.name);
|
|
61
63
|
}
|
|
62
64
|
catch (err) {
|
|
63
65
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -66,68 +68,6 @@ function bindMethod(catCmd, tool, program) {
|
|
|
66
68
|
}
|
|
67
69
|
});
|
|
68
70
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
if (result.isError) {
|
|
72
|
-
const errText = extractText(result) || "未知业务错误";
|
|
73
|
-
console.error(errText);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const text = extractText(result);
|
|
77
|
-
if (text === null) {
|
|
78
|
-
console.error("tools/call 返回无可解析内容");
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
// MCP Server 返回的是 JSON 字符串(已完成多源合并 / 时间戳格式化 /
|
|
82
|
-
// _summary / _empty / _warnings 注入)。CLI 只做呈现格式化。
|
|
83
|
-
let payload;
|
|
84
|
-
try {
|
|
85
|
-
payload = JSON.parse(text);
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
// 非 JSON 文本:直接原样输出(仍走截断 / 落盘)
|
|
89
|
-
emitWithTruncation(text.endsWith("\n") ? text.slice(0, -1) : text, opts);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// 输出模式选择(互斥优先级 --md > --compact > --pretty / 默认 = pretty)
|
|
93
|
-
let rendered;
|
|
94
|
-
if (opts.md) {
|
|
95
|
-
rendered = jsonToMarkdown(payload, toolName);
|
|
96
|
-
}
|
|
97
|
-
else if (opts.compact) {
|
|
98
|
-
rendered = JSON.stringify(payload);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
// 默认 = pretty(含 --pretty 显式情况)
|
|
102
|
-
rendered = JSON.stringify(payload, null, 2);
|
|
103
|
-
}
|
|
104
|
-
emitWithTruncation(rendered, opts);
|
|
105
|
-
}
|
|
106
|
-
function emitWithTruncation(rendered, opts) {
|
|
107
|
-
// 解析 head / tail / threshold:commander 对 `[n]` 在未传值时给 true
|
|
108
|
-
const head = resolveOptionalInt(opts.head, TRUNCATE_DEFAULTS.HEAD);
|
|
109
|
-
const tail = resolveOptionalInt(opts.tail, TRUNCATE_DEFAULTS.TAIL);
|
|
110
|
-
const threshold = resolveOptionalInt(opts.threshold, TRUNCATE_DEFAULTS.THRESHOLD);
|
|
111
|
-
const outputFile = typeof opts.outputFile === "string" && opts.outputFile.length > 0
|
|
112
|
-
? opts.outputFile
|
|
113
|
-
: undefined;
|
|
114
|
-
const r = applyTruncation(rendered, {
|
|
115
|
-
full: !!opts.full,
|
|
116
|
-
head,
|
|
117
|
-
tail,
|
|
118
|
-
threshold,
|
|
119
|
-
outputFile,
|
|
120
|
-
});
|
|
121
|
-
console.log(r.rendered);
|
|
122
|
-
for (const n of r.notices) {
|
|
123
|
-
process.stderr.write(n + "\n");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function extractText(result) {
|
|
127
|
-
const content = result.content || [];
|
|
128
|
-
for (const c of content) {
|
|
129
|
-
if (typeof c.text === "string" && c.text.length > 0)
|
|
130
|
-
return c.text;
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
71
|
+
async function callToolWithOAuthRefresh(name, args, verbose) {
|
|
72
|
+
return callCoreToolWithOAuthRefresh(name, args, { verbose });
|
|
133
73
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { refreshResolvedConfigOAuth, resolveConfigWithFreshOAuth, } from "../config.js";
|
|
2
|
+
import { callCoreTool } from "../coreClient.js";
|
|
3
|
+
function isUnauthorizedError(err) {
|
|
4
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5
|
+
return /\bHTTP 401\b/i.test(msg) || /invalid_token|expired_token|token expired/i.test(msg);
|
|
6
|
+
}
|
|
7
|
+
export async function callCoreToolWithOAuthRefresh(name, args, opts) {
|
|
8
|
+
let refreshResult = await resolveConfigWithFreshOAuth({ verbose: !!opts?.verbose });
|
|
9
|
+
try {
|
|
10
|
+
return await callCoreTool(refreshResult.config, name, args, opts);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
if (!isUnauthorizedError(err))
|
|
14
|
+
throw err;
|
|
15
|
+
refreshResult = await refreshResolvedConfigOAuth(refreshResult.config, {
|
|
16
|
+
force: true,
|
|
17
|
+
verbose: !!opts?.verbose,
|
|
18
|
+
});
|
|
19
|
+
if (!refreshResult.refreshed)
|
|
20
|
+
throw err;
|
|
21
|
+
return callCoreTool(refreshResult.config, name, args, opts);
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,45 +1,36 @@
|
|
|
1
1
|
import { DEFAULT_MCP_URL, defaultCoreURL, loadConfig, resolveConfig, saveConfig, } from "../config.js";
|
|
2
2
|
import { verifyCoreEndpoint } from "../coreClient.js";
|
|
3
|
-
import { clearSession } from "../session.js";
|
|
4
|
-
import { ensureSession } from "../mcpClient.js";
|
|
5
|
-
function parseTransport(raw) {
|
|
6
|
-
if (!raw)
|
|
7
|
-
return undefined;
|
|
8
|
-
const v = raw.trim().toLowerCase();
|
|
9
|
-
if (v === "core" || v === "mcp")
|
|
10
|
-
return v;
|
|
11
|
-
throw new Error(`invalid --transport: ${raw} (expected core or mcp)`);
|
|
12
|
-
}
|
|
13
3
|
export function registerInitCommand(program) {
|
|
14
4
|
program
|
|
15
5
|
.command("init")
|
|
16
|
-
.description("配置
|
|
6
|
+
.description("配置 endpoint / Authorization(保存到 ~/.tyc/config.json,并校验 shared core 连通性)")
|
|
17
7
|
.option("--authorization <token>", "tyc OpenAPI Authorization(写入 headers.Authorization)")
|
|
18
8
|
.option("--url <url>", `MCP endpoint(默认 ${DEFAULT_MCP_URL})`)
|
|
19
9
|
.option("--core-url <url>", "Shared Core HTTP endpoint(默认从 --url 推导 /v1/core/tools/call)")
|
|
20
|
-
.option("--transport <core|mcp>", "默认调用通道(默认 core;mcp 保留旧 session 链路)")
|
|
21
10
|
.option("--header <kv...>", "自定义 header,格式 K=V,可重复;留空清除该 key")
|
|
22
|
-
.option("--
|
|
23
|
-
.option("--no-verify", "跳过连通性校验(不发 initialize)")
|
|
11
|
+
.option("--no-verify", "跳过 shared core 连通性校验")
|
|
24
12
|
.action(async (opts) => {
|
|
25
13
|
const cfg = loadConfig() || {};
|
|
26
14
|
if (!cfg.headers)
|
|
27
15
|
cfg.headers = {};
|
|
28
|
-
|
|
16
|
+
let resetOAuth = false;
|
|
17
|
+
if (opts.url) {
|
|
29
18
|
cfg.url = opts.url;
|
|
19
|
+
resetOAuth = true;
|
|
20
|
+
}
|
|
30
21
|
if (!cfg.url)
|
|
31
22
|
cfg.url = DEFAULT_MCP_URL;
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
cfg.transport = transport;
|
|
35
|
-
if (!cfg.transport)
|
|
36
|
-
cfg.transport = "core";
|
|
37
|
-
if (opts.coreUrl)
|
|
23
|
+
delete cfg.transport;
|
|
24
|
+
if (opts.coreUrl) {
|
|
38
25
|
cfg.coreUrl = opts.coreUrl;
|
|
39
|
-
|
|
26
|
+
}
|
|
27
|
+
else if (opts.url || !cfg.coreUrl) {
|
|
40
28
|
cfg.coreUrl = defaultCoreURL(cfg.url);
|
|
41
|
-
|
|
29
|
+
}
|
|
30
|
+
if (opts.authorization) {
|
|
42
31
|
cfg.headers.Authorization = opts.authorization;
|
|
32
|
+
resetOAuth = true;
|
|
33
|
+
}
|
|
43
34
|
if (opts.header && opts.header.length > 0) {
|
|
44
35
|
for (const kv of opts.header) {
|
|
45
36
|
const idx = kv.indexOf("=");
|
|
@@ -51,34 +42,26 @@ export function registerInitCommand(program) {
|
|
|
51
42
|
const v = kv.slice(idx + 1);
|
|
52
43
|
if (!k)
|
|
53
44
|
continue;
|
|
45
|
+
if (k.toLowerCase() === "authorization")
|
|
46
|
+
resetOAuth = true;
|
|
54
47
|
if (v === "")
|
|
55
48
|
delete cfg.headers[k];
|
|
56
49
|
else
|
|
57
50
|
cfg.headers[k] = v;
|
|
58
51
|
}
|
|
59
52
|
}
|
|
53
|
+
if (resetOAuth)
|
|
54
|
+
delete cfg.oauth;
|
|
60
55
|
saveConfig(cfg);
|
|
61
|
-
|
|
62
|
-
clearSession();
|
|
63
|
-
if (opts.clearSession) {
|
|
64
|
-
console.log("已清除 ~/.tyc/session.json");
|
|
65
|
-
}
|
|
66
|
-
console.log(`已保存 ~/.tyc/config.json (transport=${cfg.transport}, url=${cfg.url}, coreUrl=${cfg.coreUrl})`);
|
|
56
|
+
console.log(`已保存 ~/.tyc/config.json (url=${cfg.url}, coreUrl=${cfg.coreUrl})`);
|
|
67
57
|
if (opts.verify === false) {
|
|
68
58
|
return;
|
|
69
59
|
}
|
|
70
|
-
// core 模式只探活 stateless endpoint;mcp 模式才 initialize 并缓存 session。
|
|
71
60
|
try {
|
|
72
61
|
const resolved = resolveConfig();
|
|
73
62
|
const verbose = !!program.opts().verbose;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.log(`已建立 MCP session(sessionId=${sess.sessionId.slice(0, 16)}…)`);
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
await verifyCoreEndpoint(resolved, verbose);
|
|
80
|
-
console.log("Shared Core endpoint 校验通过");
|
|
81
|
-
}
|
|
63
|
+
await verifyCoreEndpoint(resolved, verbose);
|
|
64
|
+
console.log("Shared Core endpoint 校验通过");
|
|
82
65
|
}
|
|
83
66
|
catch (err) {
|
|
84
67
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/commands/layers.js
CHANGED
|
@@ -82,7 +82,7 @@ function registerLayerCmd(program, spec) {
|
|
|
82
82
|
const layer = spec.layer;
|
|
83
83
|
const cmd = program
|
|
84
84
|
.command(layer)
|
|
85
|
-
.description(
|
|
85
|
+
.description(layerCommandDescription(spec));
|
|
86
86
|
cmd
|
|
87
87
|
.command("list")
|
|
88
88
|
.description(`列出 ${layer} 全部 ${getLayerCount(layer)} 个工具(默认按分类分组文本表格)`)
|
|
@@ -120,6 +120,18 @@ function registerLayerCmd(program, spec) {
|
|
|
120
120
|
console.log(renderLayerListText(spec, tools));
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
|
+
function layerCommandDescription(spec) {
|
|
124
|
+
switch (spec.layer) {
|
|
125
|
+
case "L0":
|
|
126
|
+
return `实体锚定层(${getLayerCount("L0")} 个)— 把简称、曾用名或模糊指代解析成精确企业。`;
|
|
127
|
+
case "L1":
|
|
128
|
+
return `概要总览层(${getLayerCount("L1")} 个)— 每个业务面 1 个总览工具,用 _summary 指引下一跳。`;
|
|
129
|
+
case "L2":
|
|
130
|
+
return `维度下钻层(${getLayerCount("L2")} 个)— 股权、诉讼、招投标、年报、知识产权等明细维度。`;
|
|
131
|
+
case "L3":
|
|
132
|
+
return `专项工具层(${getLayerCount("L3")} 个)— id 详情、search_*、垂直场景和专业查询。`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
123
135
|
// ─────────────────────────── 渲染:tyc layers ───────────────────────────
|
|
124
136
|
function renderLayersText() {
|
|
125
137
|
const total = getTotalCount();
|