flycode-mcp 1.0.0

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 ADDED
@@ -0,0 +1,211 @@
1
+ # flycode-mcp
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for the **FlyCode** language platform
4
+ (Flycode / 玄瞳科技 APaaS 平台)。它把 APaaS 平台的实体、领域相关能力以 MCP 工具的形式暴露给支持 MCP 的客户端
5
+ (如各类 AI 编程助手),让模型能够直接检索实体、业务领域中的逻辑定义。
6
+
7
+ ---
8
+
9
+ ## 特性
10
+
11
+ - 基于官方 `@modelcontextprotocol/sdk` 实现,使用标准 `stdio` 传输协议。
12
+ - 启动即完成 APaaS IDE 平台鉴权链路:账号登录 → 获取租户信息 → 获取 IDE Token。
13
+ - 内置统一请求层 `request`,自动按请求类型(`center` / `tenant`)切换中心服务地址与当前租户连接地址,并自动注入鉴权 Token。
14
+ - 当前提供工具:业务逻辑类 `page_model_logic_list`(分页查询业务逻辑列表)、`get_model_logic`(查询单个业务逻辑详情);实体类 `page_entity_list`(分页查询实体列表)、`get_entity`(查询实体详情)。
15
+
16
+ ---
17
+
18
+ ## 环境要求
19
+
20
+ - Node.js ≥ 18(使用了 ES Module 与 `fetch`- 无关的现代语法,建议 18+)
21
+ - npm(或 pnpm / yarn)
22
+
23
+ ---
24
+
25
+ ## 安装与构建
26
+
27
+ ```bash
28
+ # 安装依赖
29
+ npm install
30
+
31
+ # 编译 TypeScript 到 dist/
32
+ npm run build
33
+
34
+ # 或者直接用 ts-node 启动开发模式(自动监听编译)
35
+ npm run dev
36
+ ```
37
+
38
+ 构建产物位于 `dist/`,入口为 `dist/index.js`(已在 `package.json` 的 `bin` 中注册为 `flycode-mcp`)。
39
+
40
+ ---
41
+
42
+ ## 配置
43
+
44
+ 服务启动时需要一组连接参数,格式为**命令行参数字符串**,以 `&` 拼接的 `key=value` 形式传入(与 `URLSearchParams` 解析一致):
45
+
46
+ ```
47
+ username=<账号>&password=<密码>&tenantCode=<租户号>&envName=<环境名>
48
+ ```
49
+
50
+ 参数说明:
51
+
52
+ | 参数 | 必填 | 说明 |
53
+ | ------------- | ---- |-------------------|
54
+ | `username` | ✅ | APaaS IDE平台登录账号 |
55
+ | `password` | ✅ | APaaS IDE平台登录密码 |
56
+ | `tenantCode` | ✅ | 租户号(用于匹配目标租户) |
57
+ | `envName` | ✅ | 环境名(用于在租户下定位具体环境) |
58
+
59
+ > ⚠️ **安全提示**:密码为明文传入,请勿在公开环境或版本库中泄露。仓库已通过 `.gitignore` 忽略 `mcp.json` 等含凭据文件。
60
+
61
+ ---
62
+
63
+ ## 客户端接入示例
64
+
65
+ 在你的 MCP 客户端(如 Cursor、Claude Desktop、VS Code 插件等)的配置中添加一个 server。
66
+ **请使用你自己真实的账号信息替换占位符**,不要提交真实凭据:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "flycode-mcp": {
72
+ "command": "node",
73
+ "args": [
74
+ "/绝对路径/flycode-mcp/dist/index.js",
75
+ "username=你的账号&password=你的密码&tenantCode=你的租户号&envName=你的环境名"
76
+ ]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 提供的工具
85
+
86
+ ### `page_model_logic_list` — 分页查询业务逻辑列表
87
+
88
+ 根据查询条件分页查询 IDE 领域中的业务逻辑定义(只读、幂等)。
89
+
90
+ **输入参数:**
91
+
92
+ | 参数 | 类型 | 必填 | 说明 |
93
+ | --------- | -------- | ---- | -------------------------- |
94
+ | `keys` | string | 否 | 业务逻辑名,模糊匹配 |
95
+ | `keywords`| string | 否 | 代码中的关键字,模糊匹配 |
96
+ | `page` | string | 否 | 当前页,从 1 开始,默认 1 |
97
+ | `rows` | string | 否 | 每页记录数,默认 20 |
98
+
99
+ **返回:** 业务逻辑列表的 JSON(`resp_data`),包含 `modellogiccode`、`modellogicname`、`modelname`、`functionname`、`status` 等字段。
100
+
101
+ ### `get_model_logic` — 查询业务逻辑详情
102
+
103
+ 根据业务逻辑的编码 `modellogiccode` 查询其完整详情(只读、幂等),包括 flycode 脚本、输入输出参数结构、操作步骤等。底层调用 `logicClient.getModelLogic`。
104
+
105
+ **输入参数:**
106
+
107
+ | 参数 | 类型 | 必填 | 说明 |
108
+ | --------- | -------- | ---- | -------------------------- |
109
+ | `modellogiccode` | string | 是 | 要查详情的业务逻辑的编码 |
110
+
111
+ **返回:** 业务逻辑详情的 JSON(`resp_data`),主要字段:
112
+
113
+ - `modellogicname` / `modellogiccode` / `modelcode`:业务逻辑标题、编码、所属业务模型编码
114
+ - `actiontype` / `actioncategory` / `execmode` / `executionmode`:操作/行为类型、执行方式
115
+ - `input` / `output`:flycode 内置对象 `IN` / `OUT` 的结构与字段属性(含 `propertytypecode` 字段类型映射)
116
+ - `operations`:操作步骤,其中 `templateparams.flycode` 内含具体的 flycode 脚本代码
117
+ - `description` / `message` / `status` 等
118
+
119
+ ### `page_entity_list` — 分页查询实体列表
120
+
121
+ 根据查询条件分页查询 APaaS 平台中的实体定义(只读、幂等)。底层调用 `entityClient.pageEntityList`。
122
+
123
+ **输入参数:**
124
+
125
+ | 参数 | 类型 | 必填 | 说明 |
126
+ | --------------- | ------ | ---- | -------------------------------------------------------------------------- |
127
+ | `objectname` | string | 否 | 实体中文名、实体表名、实体 objectcode,模糊匹配 |
128
+ | `page` | string | 否 | 当前页,从 1 开始,默认 1 |
129
+ | `rows` | string | 否 | 每页记录数,默认 20 |
130
+ | `updatetimestart` | string | 否 | 修改开始时间,格式 `yyyy-MM-dd hh:mm:ss`,与结束时间成对出现 |
131
+ | `updatetimeend` | string | 否 | 修改结束时间,格式 `yyyy-MM-dd hh:mm:ss`,与开始时间成对出现 |
132
+ | `datapattern` | string | 否 | 实体类型(整数):1-原生实体;2-衍生实体;3-视图;4-超级表单实体 |
133
+ | `objecttypecode`| string | 否 | 实体分类(整数):1-基本实体;2-树形实体;3-数据字典;4-关联实体 |
134
+
135
+ **返回:** 实体列表的 JSON(`resp_data`),包含分页信封 `pageCount` / `pageIndex` / `pageSize` / `resultCount`,以及 `data` 数组中每个实体的基本信息:`objectcode`、`objectname`、`objectmark`、`tablename`、`directorytypename`、`datapattern`、`publishstatus`、`status`、`marktype`、`confsource`、`createaccountname`、`updateaccountname`、`createtime`、`updatetime` 等。
136
+
137
+ > 注:接口另有固定默认参数(`centercode`、`directorytypecode`、`starttime`),已在工具内部硬编码传入,不对外暴露。
138
+
139
+ ### `get_entity` — 查询实体详情
140
+
141
+ 根据实体的编码 `objectcode` 查询其完整详情(只读、幂等),包含实体的基本信息与属性数组 `properties`(每个属性的列名、类型、关联实体、索引方式等)。底层调用 `entityClient.getEntity`。
142
+
143
+ **输入参数:**
144
+
145
+ | 参数 | 类型 | 必填 | 说明 |
146
+ | ----------- | ------ | ---- | -------------------------- |
147
+ | `objectcode` | string | 是 | 要查详情的实体的编码 |
148
+
149
+ **返回:** 实体详情的 JSON(`resp_data`),主要字段:
150
+
151
+ - `objectcode` / `objectname` / `objectmark` / `tablename` / `metamodelcode`:实体编码、中文名、标记、数据库表名、领域模型编码
152
+ - `objectdescr` / `objecttypecode` / `datapattern` / `directorytypecode`:描述、实体分类、数据模式、所属目录编码
153
+ - `marktype` / `confsource` / `publishstatus` / `status` / `storagelevel`:创建标记、配置来源、发布状态、状态、存储等级
154
+ - `parentobjectcode`:父实体的 objectcode
155
+ - `message`:保存时编写的提交信息(作用类似 git 提交信息)
156
+ - `properties`:实体属性数组,每个属性含 `propertyname` / `columnname` / `propertytypecode`(字段类型映射)/ `relationobjectcode`(关联实体)/ `idxtype`(索引方式)/ `publishstatus` / `status` 等
157
+
158
+ ---
159
+
160
+ ## 项目结构
161
+
162
+ ```
163
+ flycode-mcp/
164
+ ├── src/
165
+ │ ├── index.ts # 入口:启动鉴权、注册工具、启动 stdio 服务
166
+ │ ├── service/ # 鉴权流程封装
167
+ │ │ └── auth.ts # 登录 → 获取租户信息 → 获取 IDE Token
168
+ │ ├── clients/ # 平台接口封装
169
+ │ │ ├── authClient.ts # 登录 / 获取租户信息 / 获取 IDE Token 请求
170
+ │ │ ├── logicClient.ts # 业务逻辑分页/详情查询请求
171
+ │ │ ├── entityClient.ts # 实体列表 pageEntityList / 详情 getEntity 请求
172
+ │ │ └── types.ts # 公共响应信封类型(DataResponse / PageResponse / CommonResponse)
173
+ │ ├── store/ # 运行时内存存储
174
+ │ │ ├── token.ts # 鉴权 Token 存储器
175
+ │ │ └── tenant.ts # 当前租户及连接信息存储器
176
+ │ ├── tools/ # 按能力分类的 MCP 工具定义
177
+ │ │ ├── logic/
178
+ │ │ │ └── read.ts # 只读类工具(page_model_logic_list / get_model_logic)
179
+ │ │ └── entity/
180
+ │ │ └── read.ts # 只读类工具(page_entity_list / get_entity)
181
+ │ └── utils/
182
+ │ ├── config.ts # 从命令行参数解析连接配置(BASE_URL / 账号等)
183
+ │ ├── request.ts # 统一 axios 请求层(中心/租户地址切换 + Token 注入)
184
+ │ └── mcp-types.ts # MCP 工具统一定义类型 ToolDef
185
+ ├── dist/ # 编译产物(由 tsc 生成,已忽略)
186
+ ├── package.json
187
+ ├── tsconfig.json
188
+ └── .gitignore
189
+ ```
190
+
191
+ ---
192
+
193
+ ## 工作原理
194
+
195
+ 1. **解析配置**:从 `process.argv` 读取 `username / password / tenantCode / envName`(由 `utils/config.ts` 解析)。
196
+ 2. **登录**:`service/auth.ts` 中的 `authenticate()` 调用 `/api/multiplat/auth/login` 获取 Token,存入内存。
197
+ 3. **定位租户与环境**:`authenticate()` 调用 `/api/multiplat/auth/getTenantInfo`,依据 `tenantCode + envName` 匹配出目标租户,保存其连接信息(`conn.config`)到内存。
198
+ 4. **获取 IDE Token**:`authenticate()` 调用 `/api/multiplat/auth/getIdeToken` 换取 IDE 专用 Token 并覆盖内存中的 Token。
199
+ 5. **注册并启动**:创建 `McpServer`,注册 `page_model_logic_list`、`get_model_logic`、`page_entity_list`、`get_entity` 等工具,通过 `StdioServerTransport` 监听请求。
200
+ 6. **业务请求**:工具被调用时,`logicClient` / `entityClient` 通过 `request({ type: "tenant" })` 自动切换到当前租户连接地址,并携带已注入的 Token 发起查询。
201
+
202
+ ---
203
+
204
+ ## 许可证
205
+
206
+ 本项目采用 **GNU Affero 通用公共许可证 v3(AGPL-3.0-or-later)**。
207
+
208
+ - 完整许可证文本见仓库根目录的 [LICENSE](./LICENSE) 文件。
209
+ - AGPL 是强 Copyleft 许可证:如果你修改本项目、或将本项目作为网络服务(SaaS)提供给他人使用,你必须以相同许可证开源并免费提供对应版本的完整源代码(含你服务端运行的修改版本)。
210
+
211
+ Copyright © 2026 Trust会长
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 获取到 IDE Token 保存到 Storage 里
3
+ * 以便后续正常发送请求
4
+ */
5
+ import request from "../utils/request.js";
6
+ export const APP_ID = "1";
7
+ /**
8
+ * IDE 登录接口
9
+ *
10
+ * @param data
11
+ */
12
+ export function login(data) {
13
+ return request({
14
+ url: `/api/multiplat/auth/login`,
15
+ method: 'post',
16
+ data
17
+ });
18
+ }
19
+ /**
20
+ * 获取租户信息接口
21
+ */
22
+ export function getTenantInfo() {
23
+ return request({
24
+ url: `/api/multiplat/auth/getTenantInfo`,
25
+ method: 'post',
26
+ data: {
27
+ appid: APP_ID,
28
+ }
29
+ });
30
+ }
31
+ /**
32
+ * 获取 IDE Token 接口
33
+ *
34
+ * @param data
35
+ */
36
+ export function getIdeToken(data) {
37
+ return request({
38
+ url: `/api/multiplat/auth/getIdeToken`,
39
+ method: 'post',
40
+ data
41
+ });
42
+ }
43
+ //# sourceMappingURL=authClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authClient.js","sourceRoot":"","sources":["../../src/clients/authClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,OAAO,MAAM,qBAAqB,CAAA;AAGzC,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;AAkCzB;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,IAAiB;IACrC,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,2BAA2B;QAChC,MAAM,EAAE,MAAM;QACd,IAAI;KACL,CAAC,CAAA;AACJ,CAAC;AAiCD;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,mCAAmC;QACxC,MAAM,EAAE,MAAM;QACd,IAAI,EAAE;YACJ,KAAK,EAAE,MAAM;SACd;KACF,CAAC,CAAA;AACJ,CAAC;AA0BD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAoB;IAC9C,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,iCAAiC;QACtC,MAAM,EAAE,MAAM;QACd,IAAI;KACL,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ import request from "../utils/request.js";
2
+ /**
3
+ * 分页列表查询实体
4
+ *
5
+ * @param data 请求参数
6
+ */
7
+ export function pageEntityList(data) {
8
+ return request({
9
+ url: '/bizserv/biz/pageList',
10
+ method: 'post',
11
+ data,
12
+ type: 'tenant'
13
+ });
14
+ }
15
+ /**
16
+ * 根据实体的编码 objectcode 查询实体详情
17
+ *
18
+ * @param data 请求参数
19
+ */
20
+ export function getEntity(data) {
21
+ return request({
22
+ url: '/bizserv/biz/getBizObject',
23
+ method: 'post',
24
+ data,
25
+ type: 'tenant'
26
+ });
27
+ }
28
+ //# sourceMappingURL=entityClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityClient.js","sourceRoot":"","sources":["../../src/clients/entityClient.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,qBAAqB,CAAA;AA6FzC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAsB;IACnD,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,uBAAuB;QAC5B,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,IAAI,EAAE,QAAQ;KACf,CAAC,CAAA;AACJ,CAAC;AAmGD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAkB;IAC1C,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,2BAA2B;QAChC,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,IAAI,EAAE,QAAQ;KACf,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ import request from "../utils/request.js";
2
+ /**
3
+ * 分页列表查询业务领域
4
+ *
5
+ * @param data 请求参数
6
+ */
7
+ export function pageModelLogicList(data) {
8
+ return request({
9
+ url: '/bizserv/bizmodel/pageModelLogicList',
10
+ method: 'post',
11
+ data,
12
+ type: 'tenant'
13
+ });
14
+ }
15
+ /**
16
+ * 查询业务领域详情
17
+ *
18
+ * @param data
19
+ */
20
+ export function getModelLogic(data) {
21
+ return request({
22
+ url: '/bizserv/bizmodel/getModelLogic',
23
+ method: 'post',
24
+ data,
25
+ type: 'tenant'
26
+ });
27
+ }
28
+ //# sourceMappingURL=logicClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logicClient.js","sourceRoot":"","sources":["../../src/clients/logicClient.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,qBAAqB,CAAA;AA8GzC;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAqB;IACtD,OAAO,OAAO,CAAoB;QAChC,GAAG,EAAE,sCAAsC;QAC3C,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,IAAI,EAAE,QAAQ;KACf,CAAC,CAAA;AACJ,CAAC;AAqND;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,OAAO,OAAO,CAAC;QACb,GAAG,EAAE,iCAAiC;QACtC,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,IAAI,EAAE,QAAQ;KACf,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * API 层公共响应信封
3
+ *
4
+ * 本文件集中定义可复用的信封泛型,避免各接口重复手写同一层包装。
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/clients/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/dist/index.js ADDED
@@ -0,0 +1,40 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { authenticate } from './service/auth.js';
4
+ import { readTools as logicReadTools } from './tools/logic/read.js';
5
+ import { readTools as entityReadTools } from './tools/entity/read.js';
6
+ // 鉴权初始化(登录 -> 获取租户信息 -> 获取 IDE Token)
7
+ await authenticate();
8
+ // 创建 MCP 服务器
9
+ const server = new McpServer({
10
+ name: 'flycode-mcp',
11
+ version: '1.0.0',
12
+ });
13
+ // 注册工具:所有工具定义集中在 tools/ 下,按能力分类后在此统一注册
14
+ const allTools = [
15
+ ...logicReadTools,
16
+ ...entityReadTools,
17
+ ];
18
+ for (const t of allTools) {
19
+ server.registerTool(t.name, t.config, t.handler);
20
+ }
21
+ // 启动服务
22
+ async function main() {
23
+ const transport = new StdioServerTransport();
24
+ await server.connect(transport);
25
+ // 清理资源,发送关闭消息给客户端
26
+ process.on('SIGINT', async () => {
27
+ await server.close();
28
+ process.exit(0);
29
+ });
30
+ process.on('SIGTERM', async () => {
31
+ await server.close();
32
+ process.exit(0);
33
+ });
34
+ console.error('flycode-mcp listening on stdio');
35
+ }
36
+ main().catch((e) => {
37
+ console.error(e);
38
+ process.exit(1);
39
+ });
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAErE,sCAAsC;AACtC,MAAM,YAAY,EAAE,CAAA;AAEpB,aAAa;AACb,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAA;AAEF,uCAAuC;AACvC,MAAM,QAAQ,GAAc;IAC1B,GAAG,cAAc;IACjB,GAAG,eAAe;CACnB,CAAA;AAED,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;IACzB,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;AAClD,CAAC;AAED,OAAO;AACP,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/B,kBAAkB;IAClB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAA;AACjD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 鉴权初始化流程
3
+ *
4
+ * 依次完成:登录 -> 获取租户信息并匹配目标环境 -> 获取 IDE Token,
5
+ * 中间结果写入内存存储(token / tenant),供后续请求使用。
6
+ */
7
+ import { config } from '../utils/config.js';
8
+ import { login, getTenantInfo, getIdeToken, APP_ID } from '../clients/authClient.js';
9
+ import { setToken } from "../store/token.js";
10
+ import { getTenant, setTenant } from "../store/tenant.js";
11
+ /**
12
+ * 执行完整鉴权流程,将 token 与租户信息写入内存存储。
13
+ *
14
+ * @throws 登录失败、未匹配到租户、获取 IDE Token 失败时抛出错误。
15
+ */
16
+ export async function authenticate() {
17
+ // 登录
18
+ const loginResponse = await login({
19
+ appid: APP_ID,
20
+ username: config.username,
21
+ password: config.password,
22
+ });
23
+ if (loginResponse.code !== 200) {
24
+ throw new Error(`Unable to login: ${loginResponse.msg}`);
25
+ }
26
+ // 将 token 放到 storage
27
+ setToken(loginResponse.data.token);
28
+ console.error('flycode-mcp 登录完成');
29
+ // 获取租户信息
30
+ const tenantInfoResponse = await getTenantInfo();
31
+ // 匹配目标环境
32
+ const targetTenant = tenantInfoResponse.resp_data.find((tenant) => tenant.tenantcode === config.tenantCode);
33
+ const targetEnv = targetTenant?.envs.find((env) => env.envname === config.envName);
34
+ if (targetTenant && targetEnv) {
35
+ // env.conn 为 JSON 字符串,解析为连接信息;解析失败抛出明确错误而非裸异常
36
+ let conn;
37
+ try {
38
+ conn = JSON.parse(targetEnv.conn);
39
+ }
40
+ catch {
41
+ throw new Error(`租户环境连接信息解析失败, 租户号: ${config.tenantCode}, 环境名: ${config.envName}, ` +
42
+ `env.conn 不是合法的 JSON: ${targetEnv.conn}`);
43
+ }
44
+ // 保存信息到内存
45
+ setTenant({
46
+ metaModelCode: targetTenant.metamodelcode,
47
+ metaModelType: targetTenant.metamodeltype,
48
+ productCode: targetTenant.productcode,
49
+ productName: targetTenant.productname,
50
+ productSeriesCode: targetTenant.productseriescode,
51
+ productVersionCode: targetTenant.productversioncode,
52
+ productVersionName: targetTenant.productversionname,
53
+ tenantCode: targetTenant.tenantcode,
54
+ tenantName: targetTenant.tenantname,
55
+ conn,
56
+ envId: targetEnv.envid,
57
+ envName: targetEnv.envname,
58
+ tenantEnvName: targetEnv.tenantenvname
59
+ });
60
+ }
61
+ const tenantInfo = getTenant();
62
+ if (!tenantInfo) {
63
+ throw new Error(`没有找到匹配的租户信息, 租户号: ${config.tenantCode}, 环境名: ${config.envName}`);
64
+ }
65
+ console.error('flycode-mcp 获取租户信息完成');
66
+ // 获取 IDE Token
67
+ const ideTokenResponse = await getIdeToken({
68
+ appid: APP_ID,
69
+ envid: tenantInfo.envId,
70
+ metamodeltype: tenantInfo.metaModelType,
71
+ productcode: tenantInfo.productCode,
72
+ productversioncode: tenantInfo.productVersionCode,
73
+ tenantcode: tenantInfo.tenantCode
74
+ });
75
+ // resp_data / IDE token 缺失时给出明确报错
76
+ const ideToken = ideTokenResponse.resp_data?.idetoken;
77
+ if (!ideToken) {
78
+ throw new Error(`获取 IDE Token 失败, 租户号: ${tenantInfo.tenantCode}, 环境名: ${tenantInfo.envName}, ` +
79
+ `返回体中未包含有效的 idetoken`);
80
+ }
81
+ setToken(ideToken);
82
+ console.error('flycode-mcp 获取 IDE Token 完成');
83
+ }
84
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/service/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 租户信息存储器
3
+ *
4
+ * 租户信息:包含选择的租户的信息,以及租户具体环境的信息
5
+ */
6
+ /** 内存中的租户信息缓存 */
7
+ let tenantInfo = null;
8
+ /** 设置内存中的租户信息缓存 */
9
+ export function setTenant(tenant) {
10
+ tenantInfo = tenant;
11
+ }
12
+ /** 返回内存中的租户信息缓存 */
13
+ export function getTenant() {
14
+ return tenantInfo;
15
+ }
16
+ /** 清除内存中的租户信息缓存 */
17
+ export function clearTenant() {
18
+ tenantInfo = null;
19
+ }
20
+ //# sourceMappingURL=tenant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.js","sourceRoot":"","sources":["../../src/store/tenant.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA8BH,iBAAiB;AACjB,IAAI,UAAU,GAAkB,IAAI,CAAA;AAEpC,mBAAmB;AACnB,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,UAAU,GAAG,MAAM,CAAA;AACrB,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,WAAW;IACzB,UAAU,GAAG,IAAI,CAAA;AACnB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 服务端 token 存储器。
3
+ *
4
+ * Node.js 环境没有浏览器里的 localStorage,用「内存」托管
5
+ * 鉴权 token:
6
+ * - 内存:适合运行时通过登录接口拿到 token 后缓存(例如 MCP 服务启动
7
+ * 时用 config 里的账号密码换取 token,再 setToken 写入)。
8
+ *
9
+ * 如需跨进程/重启持久化,建议把 token 放进环境变量,而非写本地文件。
10
+ */
11
+ /** 内存中的 token 缓存 */
12
+ let memoryToken = null;
13
+ /** 写入/刷新运行时获取到的 token(登录成功后调用) */
14
+ export function setToken(token) {
15
+ memoryToken = token;
16
+ }
17
+ /** 读取 token:环境变量优先,其次内存缓存 */
18
+ export function getToken() {
19
+ return memoryToken;
20
+ }
21
+ /** 清除内存中的 token(登出/过期时调用) */
22
+ export function clearToken() {
23
+ memoryToken = null;
24
+ }
25
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/store/token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oBAAoB;AACpB,IAAI,WAAW,GAAkB,IAAI,CAAA;AAErC,kCAAkC;AAClC,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,WAAW,GAAG,KAAK,CAAA;AACrB,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,QAAQ;IACtB,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,UAAU;IACxB,WAAW,GAAG,IAAI,CAAA;AACpB,CAAC"}