flow-codeblock-rust-mcp 0.1.1 → 0.1.3
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 +9 -3
- package/dist/index.js +199 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Flow Codeblock Rust+Bun 的本地 stdio MCP Server。它只调用服务端 Rust
|
|
|
7
7
|
需要 Bun 1.4.0 或更高版本:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
bunx --bun flow-codeblock-rust-mcp@0.1.
|
|
10
|
+
bunx --bun flow-codeblock-rust-mcp@0.1.3
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
配置环境变量:
|
|
@@ -26,7 +26,7 @@ export FLOW_CODEBLOCK_TOKEN='<YOUR_INTERNAL_ACCESS_TOKEN>'
|
|
|
26
26
|
"mcpServers": {
|
|
27
27
|
"flow-codeblock-rust": {
|
|
28
28
|
"command": "bunx",
|
|
29
|
-
"args": ["--bun", "flow-codeblock-rust-mcp@0.1.
|
|
29
|
+
"args": ["--bun", "flow-codeblock-rust-mcp@0.1.3"],
|
|
30
30
|
"env": {
|
|
31
31
|
"FLOW_CODEBLOCK_BASE_URL": "https://flow.example.com",
|
|
32
32
|
"FLOW_CODEBLOCK_TOKEN": "<YOUR_INTERNAL_ACCESS_TOKEN>"
|
|
@@ -38,7 +38,11 @@ export FLOW_CODEBLOCK_TOKEN='<YOUR_INTERNAL_ACCESS_TOKEN>'
|
|
|
38
38
|
|
|
39
39
|
## 工具边界
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
代码契约使用当前 Rust+Bun 模块白名单:`crypto-js` 已移除,加密应使用 `node:crypto`。Excel 仅允许 `read-excel-file/node`、`read-excel-file/universal`、`write-excel-file/node`、`write-excel-file/universal` 和 `write-excel-file/utility` 入口;这些模块由服务端共享重型执行池承载。
|
|
42
|
+
|
|
43
|
+
工具覆盖代码生成、未发布代码测试、脚本列表、版本读取、接口文档校验/预览/保存、脚本创建/更新、锁定/解锁和已发布脚本执行。创建或代码更新可提交完整接口文档或 RFC 6902 `interface_doc_patch`;预览会调用 `/flow/scripts/validate`,应用时会再次透传补丁并使用事务级 `expected_version` 检测并发冲突。所有脚本写操作需要预览后显式 `confirm: true`。
|
|
44
|
+
|
|
45
|
+
最终用户交付按模式区分:`non_script` 输出完整 JavaScript、接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和完整 `execution_url`;`script` 默认不主动回显 JavaScript 或原始 `interface_doc`,只输出接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和发布后的完整 `script_url`。脚本代码与 `interface_doc` 仍由 MCP 内部用于预览、校验和发布,除非用户明确索要源码或原始文档。
|
|
42
46
|
|
|
43
47
|
MCP 不提供脚本删除、紧急恢复解锁、Token 查询、执行统计、所有权转移或任意 HTTP 代理工具。当前版本只提供本地 stdio 连接,不提供远程 HTTP、SSE 或 Streamable HTTP 连接。
|
|
44
48
|
|
|
@@ -50,6 +54,8 @@ MCP 不提供脚本删除、紧急恢复解锁、Token 查询、执行统计、
|
|
|
50
54
|
|
|
51
55
|
接口文档的 `endpoint.path` 使用相对路径:创建时省略,更新时使用 `/flow/codeblock/<实际脚本ID>`。对外展示完整请求地址时,将用户提供的服务域名与 `/flow/codeblock/{{脚本ID}}` 拼接;不要把真实令牌、密码、Cookie 或 Authorization 值写入代码、文档、示例或 URL。
|
|
52
56
|
|
|
57
|
+
`interface_doc_patch` / `document_patch` 必须携带正整数 `expected_version`,最多 256 个 `add/remove/replace/move/copy/test` 操作。补丁预览仅返回操作数量、JSON Pointer 路径、警告和版本信息,不返回完整合并文档。
|
|
58
|
+
|
|
53
59
|
## 安全行为
|
|
54
60
|
|
|
55
61
|
- 管理请求使用 `Authorization: Bearer`,执行已发布脚本时不会把 MCP Token 转发到脚本输入。
|
package/dist/index.js
CHANGED
|
@@ -27901,6 +27901,44 @@ var interfaceDocInputDescription = [
|
|
|
27901
27901
|
"endpoint.path is relative and must be /flow/codeblock/<actual-script-id> on update; the final public URL is the caller-provided domain followed by /flow/codeblock/<script-id>.",
|
|
27902
27902
|
"Never include real tokens, passwords, cookies, Authorization values, or other credentials in the document or examples."
|
|
27903
27903
|
].join(" ");
|
|
27904
|
+
var patchPathSchema = exports_external.string().describe("RFC 6901 JSON Pointer 路径;数组路径使用当前 canonical 文档的索引。");
|
|
27905
|
+
var interfaceDocPatchOperationSchema = exports_external.union([
|
|
27906
|
+
exports_external.object({ op: exports_external.literal("add"), path: patchPathSchema, value: exports_external.unknown() }).strict(),
|
|
27907
|
+
exports_external.object({ op: exports_external.literal("remove"), path: patchPathSchema }).strict(),
|
|
27908
|
+
exports_external.object({ op: exports_external.literal("replace"), path: patchPathSchema, value: exports_external.unknown() }).strict(),
|
|
27909
|
+
exports_external.object({ op: exports_external.literal("move"), from: patchPathSchema, path: patchPathSchema }).strict(),
|
|
27910
|
+
exports_external.object({ op: exports_external.literal("copy"), from: patchPathSchema, path: patchPathSchema }).strict(),
|
|
27911
|
+
exports_external.object({ op: exports_external.literal("test"), path: patchPathSchema, value: exports_external.unknown() }).strict()
|
|
27912
|
+
]).superRefine((operation, context) => {
|
|
27913
|
+
if (["add", "replace", "test"].includes(operation.op) && !Object.prototype.hasOwnProperty.call(operation, "value")) {
|
|
27914
|
+
context.addIssue({ code: "custom", path: ["value"], message: "value is required for this operation" });
|
|
27915
|
+
}
|
|
27916
|
+
});
|
|
27917
|
+
var interfaceDocPatchSchema = exports_external.array(interfaceDocPatchOperationSchema).min(1).max(256).describe("RFC 6902 JSON Patch 操作数组;按顺序应用,不能与完整 interface_doc 同时提供。");
|
|
27918
|
+
var interfaceDocPatchJsonSchema = {
|
|
27919
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
27920
|
+
$id: "https://flow-codeblock.local/schemas/script-interface-doc.patch.v1.json",
|
|
27921
|
+
title: "script-interface-doc.v1 JSON Patch",
|
|
27922
|
+
description: "RFC 6902 operations applied in order to an existing canonical script interface document.",
|
|
27923
|
+
type: "array",
|
|
27924
|
+
minItems: 1,
|
|
27925
|
+
maxItems: 256,
|
|
27926
|
+
items: {
|
|
27927
|
+
oneOf: [
|
|
27928
|
+
{ type: "object", additionalProperties: false, required: ["op", "path", "value"], properties: { op: { const: "add" }, path: { type: "string" }, value: {} } },
|
|
27929
|
+
{ type: "object", additionalProperties: false, required: ["op", "path"], properties: { op: { const: "remove" }, path: { type: "string" } } },
|
|
27930
|
+
{ type: "object", additionalProperties: false, required: ["op", "path", "value"], properties: { op: { const: "replace" }, path: { type: "string" }, value: {} } },
|
|
27931
|
+
{ type: "object", additionalProperties: false, required: ["op", "from", "path"], properties: { op: { const: "move" }, from: { type: "string" }, path: { type: "string" } } },
|
|
27932
|
+
{ type: "object", additionalProperties: false, required: ["op", "from", "path"], properties: { op: { const: "copy" }, from: { type: "string" }, path: { type: "string" } } },
|
|
27933
|
+
{ type: "object", additionalProperties: false, required: ["op", "path", "value"], properties: { op: { const: "test" }, path: { type: "string" }, value: {} } }
|
|
27934
|
+
]
|
|
27935
|
+
}
|
|
27936
|
+
};
|
|
27937
|
+
function assertInterfaceDocPatch(patch) {
|
|
27938
|
+
const parsed = interfaceDocPatchSchema.safeParse(patch);
|
|
27939
|
+
if (!parsed.success)
|
|
27940
|
+
throw new Error(`interface_doc_patch 格式无效: ${parsed.error.message}`);
|
|
27941
|
+
}
|
|
27904
27942
|
function isObject2(value) {
|
|
27905
27943
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27906
27944
|
}
|
|
@@ -28108,14 +28146,16 @@ Rules:
|
|
|
28108
28146
|
var allowedModules = [
|
|
28109
28147
|
"axios",
|
|
28110
28148
|
"cheerio",
|
|
28111
|
-
"crypto-js",
|
|
28112
28149
|
"csv-parser",
|
|
28113
28150
|
"fast-xml-parser",
|
|
28114
28151
|
"form-data",
|
|
28115
28152
|
"lodash",
|
|
28153
|
+
"pinyin-pro",
|
|
28116
28154
|
"qs",
|
|
28155
|
+
"read-excel-file",
|
|
28117
28156
|
"sm-crypto-v2",
|
|
28118
28157
|
"uuid",
|
|
28158
|
+
"write-excel-file",
|
|
28119
28159
|
"xlsx",
|
|
28120
28160
|
"dayjs"
|
|
28121
28161
|
];
|
|
@@ -28235,7 +28275,7 @@ function codeWriterContext(mode, requirement, inputExample, includeFullSchema, b
|
|
|
28235
28275
|
code_rules: [
|
|
28236
28276
|
"所有业务数据只从全局 input 读取,不从环境变量、持久化全局变量或其他外部状态读取;返回值必须可 JSON 序列化。",
|
|
28237
28277
|
"默认使用顶层 return;只有事件式/异步流程或用户明确要求时才使用 qf_output,且必须是裸的 qf_output = { ... } 对象字面量赋值,不能与顶层 return 混用,也不能遮蔽该标识符。",
|
|
28238
|
-
"优先标准 JavaScript
|
|
28278
|
+
"优先标准 JavaScript、服务端原生 fetch 和 node:crypto;只有原生能力确实无法满足且用户明确要求时,才使用白名单 CommonJS 字面量 require。crypto-js 已移除,不得生成该模块调用。",
|
|
28239
28279
|
"禁止 import/export、动态 require、浏览器 API、定时器、阻止标识符、阻止成员和黑名单 Node 模块,不得写入真实凭据。",
|
|
28240
28280
|
"业务逻辑和异步操作放在 try-catch 中;错误转换为字符串或普通对象后返回。",
|
|
28241
28281
|
"不得创建无界循环、未 settle 的 Promise 或执行结束后仍运行的后台任务;所有请求必须 await 或 return。",
|
|
@@ -28248,7 +28288,7 @@ function codeWriterContext(mode, requirement, inputExample, includeFullSchema, b
|
|
|
28248
28288
|
require_policy: {
|
|
28249
28289
|
allowed_call_form: "仅允许单个字符串字面量 require('模块名'),不得间接调用、动态模块名或使用 import/export。",
|
|
28250
28290
|
dayjs_exception: "日期处理优先原生 Date;复杂日期解析/格式化/时区才允许使用 dayjs。",
|
|
28251
|
-
other_modules: "除 dayjs
|
|
28291
|
+
other_modules: "除 dayjs 外,只有原生能力确实无法实现且用户明确要求时才允许白名单模块;Excel 仅允许 read-excel-file/node、read-excel-file/universal、write-excel-file/node、write-excel-file/universal 和 write-excel-file/utility。"
|
|
28252
28292
|
},
|
|
28253
28293
|
forbidden: {
|
|
28254
28294
|
identifiers: [
|
|
@@ -28334,7 +28374,7 @@ function codeWriterContext(mode, requirement, inputExample, includeFullSchema, b
|
|
|
28334
28374
|
rule: "除非用户明确要求测试,否则不要调用 flow_execute_code。",
|
|
28335
28375
|
response_format: [
|
|
28336
28376
|
"除非用户明确要求只返回代码,否则先说明模式和输出方式,再给 JavaScript 代码块,最后给请求/响应示例。",
|
|
28337
|
-
"
|
|
28377
|
+
"非脚本接口交付完整 JavaScript、接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和 execution_url。"
|
|
28338
28378
|
]
|
|
28339
28379
|
};
|
|
28340
28380
|
}
|
|
@@ -28356,13 +28396,24 @@ function codeWriterContext(mode, requirement, inputExample, includeFullSchema, b
|
|
|
28356
28396
|
},
|
|
28357
28397
|
endpoint_url_template: normalizedBaseUrl ? `${normalizedBaseUrl}${endpointPathTemplate}` : endpointPathTemplate,
|
|
28358
28398
|
endpoint_url_rule: "提供域名时,输出该域名 + /flow/codeblock/{{script_id}};URL 中不得包含凭据。",
|
|
28359
|
-
|
|
28399
|
+
internal_artifacts: [
|
|
28400
|
+
"只含可执行代码的 JavaScript 代码块(提交预览、校验和发布;默认不回显)",
|
|
28401
|
+
"独立且完整的 script-interface-doc.v1 JSON 对象(提交预览、校验和发布;默认不回显)"
|
|
28402
|
+
],
|
|
28403
|
+
final_deliverables: [
|
|
28404
|
+
"接口调用说明",
|
|
28405
|
+
"请求参数及示例",
|
|
28406
|
+
"执行逻辑",
|
|
28407
|
+
"成功/错误输出示例",
|
|
28408
|
+
"发布后的完整 script_url"
|
|
28409
|
+
],
|
|
28360
28410
|
interface_doc_contract: {
|
|
28361
28411
|
strict_preview_gate: true,
|
|
28362
28412
|
required_fields: interfaceDocRequiredFields,
|
|
28363
28413
|
nested_rules: interfaceDocNestedRules,
|
|
28364
28414
|
body_example_schema: schemaFromExample(bodyExample),
|
|
28365
28415
|
full_json_schema: includeFullSchema ? interfaceDocSchema : undefined,
|
|
28416
|
+
patch_json_schema: includeFullSchema ? interfaceDocPatchJsonSchema : undefined,
|
|
28366
28417
|
separation: [
|
|
28367
28418
|
"javascript 代码块只含可执行代码,不写接口文档注释。",
|
|
28368
28419
|
"json 代码块只含一个合法 script-interface-doc.v1 对象,不混入 Markdown、注释或尾随逗号。"
|
|
@@ -28381,9 +28432,9 @@ function codeWriterContext(mode, requirement, inputExample, includeFullSchema, b
|
|
|
28381
28432
|
"版本冲突、预览过期或校验失败时停止并重新读取、重新预览,不重试旧 preview_id。"
|
|
28382
28433
|
],
|
|
28383
28434
|
response_format: [
|
|
28384
|
-
"
|
|
28385
|
-
"
|
|
28386
|
-
"
|
|
28435
|
+
"脚本模式默认不回显 JavaScript 或原始 interface_doc;只展示接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和发布后的 script_url,除非用户明确索要源码或原始文档。",
|
|
28436
|
+
"脚本代码与 interface_doc 仍必须在内部提交给预览、校验和发布工具。",
|
|
28437
|
+
"说明请求参数、主要业务行为、响应和错误处理,并提供与模式匹配的 HTTP 方法、路径、Headers/Query/Body/Cookie 和响应示例。"
|
|
28387
28438
|
]
|
|
28388
28439
|
};
|
|
28389
28440
|
}
|
|
@@ -28454,19 +28505,41 @@ var serverInstructions = [
|
|
|
28454
28505
|
"这是 Flow Codeblock Rust+Bun MCP Server。除 flow_write_code 外,工具直接调用服务端 REST API;flow_write_code 只返回生成契约。不要猜测 REST 路径,也不要把 MCP 内部令牌放入业务参数。",
|
|
28455
28506
|
"代码运行于服务端 Bun 异步函数上下文,支持现代 JavaScript、async/await、Promise、箭头函数和顶层 return;默认最小超时 100ms、最大超时 15000ms,代码 65535 字节、输入 2MiB、结果 10MiB。",
|
|
28456
28507
|
"工具选择:flow_write_code 只生成代码与契约,不执行、不写库;flow_execute_code 只用于用户明确要求的未发布非脚本测试;flow_execute_script 只执行已发布脚本。",
|
|
28457
|
-
"脚本读取、创建和更新流程:更新前先 flow_get_script 读取当前 version
|
|
28508
|
+
"脚本读取、创建和更新流程:更新前先 flow_get_script 读取当前 version;创建必须提供完整 interface_doc,更新代码或文档可提供完整 interface_doc 或 RFC 6902 interface_doc_patch(二选一,补丁必须带 expected_version);先 flow_preview_script_change,再在用户明确确认后 flow_apply_script_change(confirm=true)。文档单独修改使用 flow_preview_script_documentation -> flow_apply_script_documentation。",
|
|
28458
28509
|
"预览 ID 是一次性且有时效的;版本冲突、预览过期或校验失败时停止,重新读取并预览,不要重试旧 preview_id。flow_apply_* 永远需要 confirm=true。",
|
|
28459
|
-
"script-interface-doc.v1 必须包含 schema_version、title、summary、endpoint、request、responses、logic_description。endpoint 必须包含 methods 和 description;request.query、request.headers 必须存在(没有参数用 []);POST 必须有 request.body,GET-only
|
|
28510
|
+
"script-interface-doc.v1 必须包含 schema_version、title、summary、endpoint、request、responses、logic_description。endpoint 必须包含 methods 和 description;request.query、request.headers 必须存在(没有参数用 []);POST 必须有 request.body,GET-only 必须省略。增量文档使用最多 256 项的 add/remove/replace/move/copy/test JSON Patch,预览只回显操作数量和路径,不回显合并文档。",
|
|
28460
28511
|
"查询参数和请求头的每一项必须有 name、type、required、description、example。请求体和每个响应必须有 content_type=application/json、schema、example;每个响应还必须有 status、description。JSON Schema 的每个节点必须声明 type,数组必须有 items,对象和 example 必须互相覆盖。",
|
|
28461
28512
|
"接口文档 endpoint.path 只写相对路径:创建时省略,更新时写 /flow/codeblock/<实际脚本ID>。对外展示的完整调用地址必须使用用户提供的域名拼接 /flow/codeblock/{{脚本ID}};不要把真实 Token、密码、Cookie 或 Authorization 值写入代码、文档、示例或 URL。",
|
|
28462
28513
|
"脚本模式输入来自 input.query、input.header、input.body、input.cookies;即时非脚本模式 POST /flow/codeblock 的 body.input 原样成为全局 input。代码默认使用顶层 return;只有事件式/异步流程或用户明确要求时才使用裸 qf_output 赋值,不能混用。",
|
|
28463
|
-
"
|
|
28514
|
+
"最终用户交付按模式区分:non_script 输出完整 JavaScript、接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和完整 execution_url;script 默认不主动回显 JavaScript 或原始 interface_doc,只输出接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和发布后的完整 script_url,除非用户明确索要源码或原始文档。script 的代码与 interface_doc 仍必须内部提交给预览、校验和发布工具。",
|
|
28515
|
+
"优先原生 JavaScript、URL/URLSearchParams、fetch 和 node:crypto;crypto-js 已移除,禁止生成该模块调用。禁止浏览器 API、定时器、动态模块加载、黑名单 Node 模块和危险标识符。Excel 仅允许 read-excel-file/node、read-excel-file/universal、write-excel-file/node、write-excel-file/universal 和 write-excel-file/utility。HTTP 请求必须检查状态并处理 JSON/文本/空响应,所有异步任务必须显式 await 或 return。",
|
|
28464
28516
|
"执行脚本时 method 只能是 GET 或 POST;MCP 认证、Cookie、CSRF、代理来源头和测试工具标识会被过滤。不要使用删除脚本、紧急恢复解锁或任意 HTTP 代理能力,本 MCP 不提供这些工具。"
|
|
28465
28517
|
].join(`
|
|
28466
28518
|
`);
|
|
28467
28519
|
function encodedScriptId(scriptId) {
|
|
28468
28520
|
return encodeURIComponent(scriptId);
|
|
28469
28521
|
}
|
|
28522
|
+
function scriptUrl(api2, scriptId) {
|
|
28523
|
+
return new URL(`/flow/codeblock/${encodedScriptId(scriptId)}`, api2.baseUrl).toString();
|
|
28524
|
+
}
|
|
28525
|
+
function executionUrl(api2) {
|
|
28526
|
+
return new URL("/flow/codeblock", api2.baseUrl).toString();
|
|
28527
|
+
}
|
|
28528
|
+
function withScriptUrl(api2, payload, fallbackScriptId) {
|
|
28529
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
28530
|
+
return payload;
|
|
28531
|
+
const data = responseData(payload);
|
|
28532
|
+
const scriptId = typeof data.script_id === "string" && data.script_id.length > 0 ? data.script_id : fallbackScriptId;
|
|
28533
|
+
if (!scriptId)
|
|
28534
|
+
return payload;
|
|
28535
|
+
return {
|
|
28536
|
+
...payload,
|
|
28537
|
+
data: {
|
|
28538
|
+
...data,
|
|
28539
|
+
script_url: scriptUrl(api2, scriptId)
|
|
28540
|
+
}
|
|
28541
|
+
};
|
|
28542
|
+
}
|
|
28470
28543
|
function encodeCode(code, codeBase64) {
|
|
28471
28544
|
if (code === undefined && codeBase64 === undefined)
|
|
28472
28545
|
return;
|
|
@@ -28541,9 +28614,11 @@ function withApiErrors(handler) {
|
|
|
28541
28614
|
};
|
|
28542
28615
|
}
|
|
28543
28616
|
var documentationFields = {
|
|
28544
|
-
document: exports_external.unknown().optional().describe(`规范化的 script-interface-doc.v1 JSON 对象。与 raw_document
|
|
28545
|
-
raw_document: exports_external.string().optional().describe("待服务端解析的 JSON/OpenAPI 文档文本。与 document
|
|
28546
|
-
format: exports_external.literal("json").optional().describe("raw_document 的格式,目前只支持 json。")
|
|
28617
|
+
document: exports_external.unknown().optional().describe(`规范化的 script-interface-doc.v1 JSON 对象。与 raw_document、document_patch 三选一;保存或代码更新时必须完整。${interfaceDocInputDescription}`),
|
|
28618
|
+
raw_document: exports_external.string().optional().describe("待服务端解析的 JSON/OpenAPI 文档文本。与 document、document_patch 三选一;format=json 时按 JSON 解析。"),
|
|
28619
|
+
format: exports_external.literal("json").optional().describe("raw_document 的格式,目前只支持 json。"),
|
|
28620
|
+
document_patch: interfaceDocPatchSchema.optional().describe("仅已有脚本使用的 RFC 6902 增量补丁;与 document、raw_document 三选一。"),
|
|
28621
|
+
expected_version: exports_external.number().int().positive().optional().describe("补丁的当前脚本版本;提交补丁时必填,必须来自刚读取的当前版本。")
|
|
28547
28622
|
};
|
|
28548
28623
|
var changeSchema = exports_external.object({
|
|
28549
28624
|
operation: exports_external.enum(["create", "update"]).describe("create 创建脚本;update 更新已有脚本。"),
|
|
@@ -28553,18 +28628,26 @@ var changeSchema = exports_external.object({
|
|
|
28553
28628
|
description: exports_external.string().optional().describe("脚本说明。可在不改代码时单独更新。"),
|
|
28554
28629
|
ip_whitelist: exports_external.array(exports_external.string()).nullable().optional().describe("来源 IP/CIDR 白名单。省略表示更新时保持原值;null 或 [] 表示清除限制。"),
|
|
28555
28630
|
interface_doc: exports_external.unknown().optional().describe(interfaceDocInputDescription),
|
|
28556
|
-
|
|
28631
|
+
interface_doc_patch: interfaceDocPatchSchema.optional().describe("仅 update 使用的 RFC 6902 JSON Patch;与完整 interface_doc 互斥,create 禁止使用。"),
|
|
28632
|
+
rollback_to_version: exports_external.number().int().positive().optional().describe("回滚到的历史版本号。只能与单独的 update 操作使用,不能和 code、interface_doc 或 interface_doc_patch 同时传入。"),
|
|
28557
28633
|
expected_version: exports_external.number().int().positive().optional().describe("更新时必填的当前版本号。必须来自刚读取的 flow_get_script,用于并发冲突保护;create 不得传入。")
|
|
28558
28634
|
});
|
|
28559
28635
|
var documentationSchema = exports_external.object({
|
|
28560
28636
|
script_id: exports_external.string().min(1).describe("目标脚本 ID,不是完整 URL;校验或预览现有脚本的文档。"),
|
|
28561
28637
|
...documentationFields
|
|
28562
28638
|
}).superRefine((input, context) => {
|
|
28563
|
-
|
|
28564
|
-
|
|
28639
|
+
const supplied = [input.document, input.raw_document, input.document_patch].filter((value) => value !== undefined).length;
|
|
28640
|
+
if (supplied !== 1) {
|
|
28641
|
+
context.addIssue({ code: "custom", message: "Provide exactly one of document, raw_document, or document_patch" });
|
|
28642
|
+
}
|
|
28643
|
+
if (input.document_patch !== undefined && input.expected_version === undefined) {
|
|
28644
|
+
context.addIssue({ code: "custom", message: "document_patch requires expected_version" });
|
|
28565
28645
|
}
|
|
28566
28646
|
});
|
|
28567
28647
|
function documentationBody(input) {
|
|
28648
|
+
if (input.document_patch !== undefined) {
|
|
28649
|
+
return { document_patch: input.document_patch, expected_version: input.expected_version };
|
|
28650
|
+
}
|
|
28568
28651
|
if (input.raw_document !== undefined) {
|
|
28569
28652
|
return { raw_document: input.raw_document, ...input.format ? { format: input.format } : {} };
|
|
28570
28653
|
}
|
|
@@ -28584,6 +28667,9 @@ function assertScriptChangeInput(input) {
|
|
|
28584
28667
|
}
|
|
28585
28668
|
if (!hasCode)
|
|
28586
28669
|
throw new Error("Create preview requires code or code_base64");
|
|
28670
|
+
if (input.interface_doc_patch !== undefined) {
|
|
28671
|
+
throw new Error("Create preview cannot use interface_doc_patch; submit a complete interface_doc");
|
|
28672
|
+
}
|
|
28587
28673
|
if (input.interface_doc === undefined) {
|
|
28588
28674
|
throw new Error("Create preview requires a complete interface_doc");
|
|
28589
28675
|
}
|
|
@@ -28594,34 +28680,64 @@ function assertScriptChangeInput(input) {
|
|
|
28594
28680
|
if (!input.script_id || input.expected_version === undefined) {
|
|
28595
28681
|
throw new Error("Update preview requires script_id and expected_version");
|
|
28596
28682
|
}
|
|
28597
|
-
if (!hasCode && input.description === undefined && input.ip_whitelist === undefined && input.interface_doc === undefined && input.rollback_to_version === undefined) {
|
|
28598
|
-
throw new Error("Update preview requires code, description, ip_whitelist, interface_doc, or rollback_to_version");
|
|
28683
|
+
if (!hasCode && input.description === undefined && input.ip_whitelist === undefined && input.interface_doc === undefined && input.interface_doc_patch === undefined && input.rollback_to_version === undefined) {
|
|
28684
|
+
throw new Error("Update preview requires code, description, ip_whitelist, interface_doc, interface_doc_patch, or rollback_to_version");
|
|
28599
28685
|
}
|
|
28600
|
-
if (hasCode && input.interface_doc === undefined) {
|
|
28601
|
-
throw new Error("Updating code requires a complete interface_doc");
|
|
28686
|
+
if (hasCode && input.interface_doc === undefined && input.interface_doc_patch === undefined) {
|
|
28687
|
+
throw new Error("Updating code requires a complete interface_doc or interface_doc_patch");
|
|
28602
28688
|
}
|
|
28603
28689
|
}
|
|
28604
|
-
if (input.
|
|
28605
|
-
throw new Error("
|
|
28690
|
+
if (input.interface_doc !== undefined && input.interface_doc_patch !== undefined) {
|
|
28691
|
+
throw new Error("interface_doc and interface_doc_patch are mutually exclusive");
|
|
28692
|
+
}
|
|
28693
|
+
if (input.rollback_to_version !== undefined && (hasCode || input.interface_doc !== undefined || input.interface_doc_patch !== undefined)) {
|
|
28694
|
+
throw new Error("rollback_to_version cannot be combined with code, interface_doc, or interface_doc_patch");
|
|
28606
28695
|
}
|
|
28607
28696
|
if (input.interface_doc !== undefined) {
|
|
28608
28697
|
assertCompleteInterfaceDoc(input.interface_doc, input.operation);
|
|
28609
28698
|
}
|
|
28699
|
+
if (input.interface_doc_patch !== undefined)
|
|
28700
|
+
assertInterfaceDocPatch(input.interface_doc_patch);
|
|
28610
28701
|
}
|
|
28611
28702
|
async function validateScriptChange(api2, operation, payload) {
|
|
28612
28703
|
const hasCode = typeof payload.code_base64 === "string";
|
|
28613
28704
|
const hasDocument = payload.interface_doc !== undefined && payload.interface_doc !== null;
|
|
28705
|
+
const hasPatch = payload.interface_doc_patch !== undefined;
|
|
28614
28706
|
const hasWhitelist = payload.ip_whitelist !== undefined;
|
|
28615
|
-
if (!hasCode && !hasDocument && !hasWhitelist) {
|
|
28707
|
+
if (!hasCode && !hasDocument && !hasPatch && !hasWhitelist) {
|
|
28616
28708
|
return { success: true, data: { valid: true, warnings: [] } };
|
|
28617
28709
|
}
|
|
28618
28710
|
return api2.post("/flow/scripts/validate", {
|
|
28619
28711
|
...hasCode ? { code_base64: payload.code_base64 } : {},
|
|
28620
28712
|
...operation === "update" ? { script_id: payload.script_id } : {},
|
|
28713
|
+
...hasPatch ? { expected_version: payload.expected_version } : {},
|
|
28621
28714
|
...hasWhitelist ? { ip_whitelist: payload.ip_whitelist } : {},
|
|
28622
|
-
...hasDocument ? { interface_doc: payload.interface_doc } : {}
|
|
28715
|
+
...hasDocument ? { interface_doc: payload.interface_doc } : {},
|
|
28716
|
+
...hasPatch ? { interface_doc_patch: payload.interface_doc_patch } : {}
|
|
28623
28717
|
});
|
|
28624
28718
|
}
|
|
28719
|
+
function compactPatchValidation(validation, patch, currentVersion2) {
|
|
28720
|
+
const data = responseData(validation);
|
|
28721
|
+
const operations = Array.isArray(patch) ? patch : [];
|
|
28722
|
+
return {
|
|
28723
|
+
valid: data.valid ?? true,
|
|
28724
|
+
warnings: Array.isArray(data.warnings) ? data.warnings : [],
|
|
28725
|
+
current_version: currentVersion2,
|
|
28726
|
+
interface_doc_patch: {
|
|
28727
|
+
operation_count: operations.length,
|
|
28728
|
+
paths: operations.map((operation) => {
|
|
28729
|
+
if (!operation || typeof operation !== "object")
|
|
28730
|
+
return null;
|
|
28731
|
+
const value = operation;
|
|
28732
|
+
return {
|
|
28733
|
+
op: typeof value.op === "string" ? value.op : "unknown",
|
|
28734
|
+
path: typeof value.path === "string" ? value.path : "",
|
|
28735
|
+
...typeof value.from === "string" ? { from: value.from } : {}
|
|
28736
|
+
};
|
|
28737
|
+
})
|
|
28738
|
+
}
|
|
28739
|
+
};
|
|
28740
|
+
}
|
|
28625
28741
|
function assertPreview(record3, operation) {
|
|
28626
28742
|
if (!record3 || record3.operation !== operation)
|
|
28627
28743
|
throw new Error("Preview is missing, expired, or has the wrong operation");
|
|
@@ -28631,9 +28747,9 @@ function assertPreview(record3, operation) {
|
|
|
28631
28747
|
return record3;
|
|
28632
28748
|
}
|
|
28633
28749
|
function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
28634
|
-
const server = new McpServer({ name: "flow-codeblock-rust", version: "0.1.
|
|
28750
|
+
const server = new McpServer({ name: "flow-codeblock-rust", version: "0.1.3" }, { instructions: serverInstructions });
|
|
28635
28751
|
server.registerTool("flow_write_code", {
|
|
28636
|
-
description: "生成代码实现契约,不写数据库、不发布脚本、不执行代码。mode=non_script 生成即时 POST /flow/codeblock
|
|
28752
|
+
description: "生成代码实现契约,不写数据库、不发布脚本、不执行代码。mode=non_script 生成即时 POST /flow/codeblock 代码,最终交付包含完整 execution_url;mode=script 生成 GET/POST /flow/codeblock/{{script_id}} 代码,内部生成完整 script-interface-doc.v1 供预览、校验和发布,最终交付默认只展示接口调用说明、请求参数及示例、执行逻辑、成功/错误输出示例和 script_url,除非用户明确索要源码或原始 interface_doc。脚本文档的 title、summary、endpoint.methods、endpoint.description、request、responses、logic_description 必须齐全;query/header 参数必须有 name/type/description/example,body 和 response 字段必须有 content_type/schema/example。需要展示用户指定的完整地址时可传入 base_url。",
|
|
28637
28753
|
inputSchema: {
|
|
28638
28754
|
mode: exports_external.enum(["non_script", "script"]).describe("执行模式:non_script 为即时接口;script 为按脚本 ID 发布的 GET/POST 接口。未明确要求重定向时使用 non_script。"),
|
|
28639
28755
|
requirement: exports_external.string().min(1).max(20000).describe("用户的业务需求、输入字段、同步/异步要求和错误行为。只写与本次代码有关的需求。"),
|
|
@@ -28648,7 +28764,10 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28648
28764
|
}
|
|
28649
28765
|
}, "base_url must be an http(s) URL without credentials or control characters").optional().describe("可选的用户服务域名,例如 https://flow.example.com。仅用于输出完整调用地址,会拼接 /flow/codeblock/{{script_id}};不得包含用户名、密码或 Token。")
|
|
28650
28766
|
}
|
|
28651
|
-
}, async ({ mode, requirement, input_example, include_full_schema, base_url }) =>
|
|
28767
|
+
}, async ({ mode, requirement, input_example, include_full_schema, base_url }) => {
|
|
28768
|
+
const context = codeWriterContext(mode, requirement, input_example, include_full_schema ?? false, base_url);
|
|
28769
|
+
return result(mode === "non_script" ? { ...context, execution_url: executionUrl(api2) } : context);
|
|
28770
|
+
});
|
|
28652
28771
|
server.registerTool("flow_list_scripts", {
|
|
28653
28772
|
description: "只读分页查询脚本。默认使用 page/size 偏移分页;需要连续遍历时使用 pagination=cursor,首次不传 cursor,后续使用响应中的游标。不会创建、更新或执行脚本。",
|
|
28654
28773
|
inputSchema: {
|
|
@@ -28699,22 +28818,28 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28699
28818
|
return result(await api2.get(`/flow/scripts/${encodedScriptId(script_id)}/documentation${query}`));
|
|
28700
28819
|
}));
|
|
28701
28820
|
server.registerTool("flow_validate_script_documentation", {
|
|
28702
|
-
description: "
|
|
28821
|
+
description: "只读校验并规范化指定脚本的接口文档,不写入数据库。完整 document、raw_document 和 RFC 6902 document_patch 三选一;补丁必须带 expected_version,响应不会用于发布确认。",
|
|
28703
28822
|
inputSchema: documentationSchema.shape
|
|
28704
28823
|
}, withApiErrors(async (input) => {
|
|
28705
28824
|
const parsed = documentationSchema.parse(input);
|
|
28706
28825
|
if (parsed.document !== undefined) {
|
|
28707
28826
|
assertCompleteInterfaceDoc(parsed.document, "update");
|
|
28708
28827
|
}
|
|
28709
|
-
|
|
28710
|
-
|
|
28828
|
+
if (parsed.document_patch !== undefined)
|
|
28829
|
+
assertInterfaceDocPatch(parsed.document_patch);
|
|
28830
|
+
const validation = parsed.document_patch !== undefined ? await api2.post("/flow/scripts/validate", {
|
|
28831
|
+
script_id: parsed.script_id,
|
|
28832
|
+
expected_version: parsed.expected_version,
|
|
28833
|
+
interface_doc_patch: parsed.document_patch
|
|
28834
|
+
}) : await api2.post(`/flow/scripts/${encodedScriptId(parsed.script_id)}/documentation`, documentationBody(parsed));
|
|
28835
|
+
const document = responseData(validation).document ?? responseData(validation).interface_doc;
|
|
28711
28836
|
if (document === undefined)
|
|
28712
28837
|
throw new Error("Documentation validation response did not include document");
|
|
28713
28838
|
assertCompleteInterfaceDoc(document, "update");
|
|
28714
28839
|
return result(validation);
|
|
28715
28840
|
}));
|
|
28716
28841
|
server.registerTool("flow_preview_script_change", {
|
|
28717
|
-
description: "预览脚本创建或更新,不写数据库。create 必须传 code/code_base64 和完整 interface_doc,且不得传 script_id/expected_version;update 必须传 script_id 和刚读取的 expected_version,修改代码时必须同时传完整 interface_doc。预览成功后只能在用户明确确认时调用 flow_apply_script_change(confirm=true)。",
|
|
28842
|
+
description: "预览脚本创建或更新,不写数据库。create 必须传 code/code_base64 和完整 interface_doc,且不得传 script_id/expected_version;update 必须传 script_id 和刚读取的 expected_version,修改代码时必须同时传完整 interface_doc 或 interface_doc_patch。预览成功后只能在用户明确确认时调用 flow_apply_script_change(confirm=true)。",
|
|
28718
28843
|
inputSchema: changeSchema.shape
|
|
28719
28844
|
}, withApiErrors(async (input) => {
|
|
28720
28845
|
const parsed = changeSchema.parse(input);
|
|
@@ -28727,6 +28852,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28727
28852
|
...parsed.description !== undefined ? { description: parsed.description } : {},
|
|
28728
28853
|
...parsed.ip_whitelist !== undefined ? { ip_whitelist: parsed.ip_whitelist } : {},
|
|
28729
28854
|
...parsed.interface_doc !== undefined ? { interface_doc: parsed.operation === "create" ? removeCreatePath(parsed.interface_doc) : parsed.interface_doc } : {},
|
|
28855
|
+
...parsed.interface_doc_patch !== undefined ? { interface_doc_patch: parsed.interface_doc_patch } : {},
|
|
28730
28856
|
...parsed.rollback_to_version !== undefined ? { rollback_to_version: parsed.rollback_to_version } : {}
|
|
28731
28857
|
};
|
|
28732
28858
|
const expectedVersion = parsed.operation === "update" ? await fetchCurrentVersion(api2, parsed.script_id) : undefined;
|
|
@@ -28735,8 +28861,9 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28735
28861
|
}
|
|
28736
28862
|
const validation = await validateScriptChange(api2, parsed.operation, payload);
|
|
28737
28863
|
const normalized = responseData(validation).interface_doc;
|
|
28738
|
-
if (normalized !== undefined)
|
|
28864
|
+
if (parsed.interface_doc !== undefined && normalized !== undefined)
|
|
28739
28865
|
payload.interface_doc = normalized;
|
|
28866
|
+
const previewValidation = parsed.interface_doc_patch !== undefined ? compactPatchValidation(validation, parsed.interface_doc_patch, expectedVersion) : { valid: true, server_validation: validation };
|
|
28740
28867
|
const previewId = randomUUID();
|
|
28741
28868
|
const storedPreview = previews.put(previewId, "script_change", payload, expectedVersion);
|
|
28742
28869
|
return result({
|
|
@@ -28745,20 +28872,20 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28745
28872
|
expected_version: expectedVersion,
|
|
28746
28873
|
expires_at: new Date(storedPreview.expiresAt).toISOString(),
|
|
28747
28874
|
validation: {
|
|
28748
|
-
|
|
28749
|
-
server_validation: validation
|
|
28875
|
+
...previewValidation
|
|
28750
28876
|
},
|
|
28751
28877
|
changes: {
|
|
28752
28878
|
code: codeBase64 !== undefined,
|
|
28753
28879
|
description: parsed.description !== undefined,
|
|
28754
28880
|
ip_whitelist: parsed.ip_whitelist !== undefined,
|
|
28755
28881
|
interface_doc: parsed.interface_doc !== undefined,
|
|
28882
|
+
interface_doc_patch: parsed.interface_doc_patch !== undefined,
|
|
28756
28883
|
rollback_to_version: parsed.rollback_to_version !== undefined
|
|
28757
28884
|
}
|
|
28758
28885
|
});
|
|
28759
28886
|
}));
|
|
28760
28887
|
server.registerTool("flow_apply_script_change", {
|
|
28761
|
-
description: "应用 flow_preview_script_change 返回的一次性 preview_id。必须传 confirm=true
|
|
28888
|
+
description: "应用 flow_preview_script_change 返回的一次性 preview_id。必须传 confirm=true;工具会再次检查版本并在成功或失败后销毁预览。发布成功时返回由 FLOW_CODEBLOCK_BASE_URL 生成的完整 script_url。不支持删除脚本。版本冲突时重新读取并预览。",
|
|
28762
28889
|
inputSchema: {
|
|
28763
28890
|
preview_id: exports_external.string().uuid().describe("最近一次脚本变更预览返回的 UUID;不能复用过期或已应用的 ID。"),
|
|
28764
28891
|
confirm: exports_external.literal(true).describe("必须为 true,表示用户已明确确认写入。")
|
|
@@ -28774,44 +28901,56 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28774
28901
|
if (actualVersion !== record3.expectedVersion) {
|
|
28775
28902
|
throw new Error(`Script version changed from ${record3.expectedVersion} to ${actualVersion}; read and preview again`);
|
|
28776
28903
|
}
|
|
28904
|
+
if (payload.interface_doc_patch !== undefined) {
|
|
28905
|
+
await validateScriptChange(api2, "update", payload);
|
|
28906
|
+
}
|
|
28777
28907
|
const body = { ...payload };
|
|
28778
28908
|
delete body.script_id;
|
|
28779
28909
|
response = await api2.put(`/flow/scripts/${encodedScriptId(scriptId)}`, body);
|
|
28780
28910
|
} else {
|
|
28781
28911
|
response = await api2.post("/flow/scripts", payload);
|
|
28782
28912
|
}
|
|
28783
|
-
return result(response);
|
|
28913
|
+
return result(withScriptUrl(api2, response, scriptId));
|
|
28784
28914
|
} finally {
|
|
28785
28915
|
previews.delete(preview_id);
|
|
28786
28916
|
}
|
|
28787
28917
|
}));
|
|
28788
28918
|
server.registerTool("flow_preview_script_documentation", {
|
|
28789
|
-
description: "预览接口文档保存,不写数据库。先读取脚本当前版本;document
|
|
28919
|
+
description: "预览接口文档保存,不写数据库。先读取脚本当前版本;document、raw_document 和 document_patch 三选一,补丁必须带 expected_version,完整文档必须包含所有强制字段。成功后只有 flow_apply_script_documentation(confirm=true) 才会写入。",
|
|
28790
28920
|
inputSchema: documentationSchema.shape
|
|
28791
28921
|
}, withApiErrors(async (input) => {
|
|
28792
28922
|
const parsed = documentationSchema.parse(input);
|
|
28793
28923
|
const expectedVersion = await fetchCurrentVersion(api2, parsed.script_id);
|
|
28924
|
+
if (parsed.document_patch !== undefined && parsed.expected_version !== expectedVersion) {
|
|
28925
|
+
throw new Error(`Script version changed from ${parsed.expected_version} to ${expectedVersion}; read and preview again`);
|
|
28926
|
+
}
|
|
28794
28927
|
const rawBody = documentationBody(parsed);
|
|
28795
|
-
const validation = parsed.
|
|
28928
|
+
const validation = parsed.document_patch !== undefined ? await api2.post("/flow/scripts/validate", {
|
|
28929
|
+
script_id: parsed.script_id,
|
|
28930
|
+
expected_version: parsed.expected_version,
|
|
28931
|
+
interface_doc_patch: parsed.document_patch
|
|
28932
|
+
}) : parsed.raw_document !== undefined ? await api2.post(`/flow/scripts/${encodedScriptId(parsed.script_id)}/documentation`, rawBody) : await api2.post("/flow/scripts/validate", {
|
|
28796
28933
|
script_id: parsed.script_id,
|
|
28797
28934
|
interface_doc: rawBody.document
|
|
28798
28935
|
});
|
|
28799
28936
|
const document = responseData(validation).interface_doc ?? responseData(validation).document;
|
|
28800
|
-
if (
|
|
28801
|
-
|
|
28802
|
-
|
|
28937
|
+
if (parsed.document_patch === undefined) {
|
|
28938
|
+
if (document === undefined)
|
|
28939
|
+
throw new Error("Documentation validation response did not include document");
|
|
28940
|
+
assertCompleteInterfaceDoc(document, "update");
|
|
28941
|
+
}
|
|
28803
28942
|
const previewId = randomUUID();
|
|
28804
|
-
const storedPreview = previews.put(previewId, "documentation", { script_id: parsed.script_id, document }, expectedVersion);
|
|
28943
|
+
const storedPreview = previews.put(previewId, "documentation", parsed.document_patch !== undefined ? { script_id: parsed.script_id, expected_version: expectedVersion, document_patch: parsed.document_patch } : { script_id: parsed.script_id, document }, expectedVersion);
|
|
28805
28944
|
return result({
|
|
28806
28945
|
preview_id: previewId,
|
|
28807
28946
|
script_id: parsed.script_id,
|
|
28808
28947
|
expected_version: expectedVersion,
|
|
28809
28948
|
expires_at: new Date(storedPreview.expiresAt).toISOString(),
|
|
28810
|
-
validation: responseData(validation)
|
|
28949
|
+
validation: parsed.document_patch !== undefined ? compactPatchValidation(validation, parsed.document_patch, expectedVersion) : responseData(validation)
|
|
28811
28950
|
});
|
|
28812
28951
|
}));
|
|
28813
28952
|
server.registerTool("flow_apply_script_documentation", {
|
|
28814
|
-
description: "应用 flow_preview_script_documentation 返回的一次性预览。必须传 confirm=true
|
|
28953
|
+
description: "应用 flow_preview_script_documentation 返回的一次性预览。必须传 confirm=true;工具会再次检查脚本版本并在成功或失败后销毁预览。发布成功时返回完整 script_url。版本冲突时重新读取、预览。",
|
|
28815
28954
|
inputSchema: {
|
|
28816
28955
|
preview_id: exports_external.string().uuid().describe("最近一次接口文档预览返回的 UUID;不能复用过期或已应用的 ID。"),
|
|
28817
28956
|
confirm: exports_external.literal(true).describe("必须为 true,表示用户已明确确认写入。")
|
|
@@ -28824,11 +28963,20 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28824
28963
|
if (actualVersion !== record3.expectedVersion) {
|
|
28825
28964
|
throw new Error(`Script version changed from ${record3.expectedVersion} to ${actualVersion}; read and preview again`);
|
|
28826
28965
|
}
|
|
28966
|
+
const patch = record3.payload.document_patch;
|
|
28967
|
+
if (patch !== undefined) {
|
|
28968
|
+
assertInterfaceDocPatch(patch);
|
|
28969
|
+
await api2.post("/flow/scripts/validate", {
|
|
28970
|
+
script_id: scriptId,
|
|
28971
|
+
expected_version: record3.expectedVersion,
|
|
28972
|
+
interface_doc_patch: patch
|
|
28973
|
+
});
|
|
28974
|
+
}
|
|
28827
28975
|
const response = await api2.put(`/flow/scripts/${encodedScriptId(scriptId)}/documentation`, {
|
|
28828
|
-
document: record3.payload.document,
|
|
28976
|
+
...patch !== undefined ? { document_patch: patch } : { document: record3.payload.document },
|
|
28829
28977
|
expected_version: record3.expectedVersion
|
|
28830
28978
|
});
|
|
28831
|
-
return result(response);
|
|
28979
|
+
return result(withScriptUrl(api2, response, scriptId));
|
|
28832
28980
|
} finally {
|
|
28833
28981
|
previews.delete(preview_id);
|
|
28834
28982
|
}
|
|
@@ -28843,7 +28991,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28843
28991
|
server.registerTool("flow_unlock_script", { description: "使用所有者名称和口令正常解锁脚本。必须显式传入 confirm=true;锁定口令不会被 MCP 保存。不提供紧急恢复解锁。", inputSchema: lockSchema }, withApiErrors(async ({ script_id, owner_name, lock_password }) => result(await api2.post(`/flow/scripts/${encodedScriptId(script_id)}/unlock`, { owner_name, lock_password }))));
|
|
28844
28992
|
const queryValueSchema = exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean(), exports_external.array(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()]))]);
|
|
28845
28993
|
server.registerTool("flow_execute_script", {
|
|
28846
|
-
description: "执行已发布脚本,仅用于用户明确要求测试或调用时。method 只能是 GET/POST
|
|
28994
|
+
description: "执行已发布脚本,仅用于用户明确要求测试或调用时。method 只能是 GET/POST;结果包含由 FLOW_CODEBLOCK_BASE_URL 和 script_id 生成的完整 script_url。query 数组会生成重复参数,POST 的 body 作为 JSON 发送。MCP 认证、Cookie、CSRF、代理来源头和测试工具标识会被过滤,qingcodeToken/qingcodeTimeout 不能作为业务参数传入。",
|
|
28847
28995
|
inputSchema: {
|
|
28848
28996
|
script_id: exports_external.string().min(1).describe("已发布脚本 ID;工具会调用 /flow/codeblock/{script_id}。"),
|
|
28849
28997
|
method: exports_external.enum(["GET", "POST"]).default("POST").describe("脚本请求方法,只能是 GET 或 POST;默认 POST。"),
|
|
@@ -28876,12 +29024,10 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28876
29024
|
authenticated: false,
|
|
28877
29025
|
execution: true
|
|
28878
29026
|
});
|
|
28879
|
-
|
|
28880
|
-
requestUrl.search = "";
|
|
28881
|
-
return result({ request_url: requestUrl.toString(), method, response });
|
|
29027
|
+
return result({ script_url: scriptUrl(api2, script_id), method, response });
|
|
28882
29028
|
}));
|
|
28883
29029
|
server.registerTool("flow_execute_code", {
|
|
28884
|
-
description: "执行未发布的非脚本 JavaScript
|
|
29030
|
+
description: "执行未发布的非脚本 JavaScript,仅在用户明确要求测试时使用。结果包含完整 execution_url。请求固定为 POST /flow/codeblock,body.input 原样注入全局 input;代码和 code_base64 二选一。不会把 MCP 认证信息写入用户脚本输入,也不会创建或发布脚本。",
|
|
28885
29031
|
inputSchema: {
|
|
28886
29032
|
code: exports_external.string().min(1).optional().describe("UTF-8 JavaScript 源码,与 code_base64 二选一。"),
|
|
28887
29033
|
code_base64: exports_external.string().min(1).optional().describe("JavaScript 源码的非空 Base64,与 code 二选一。"),
|
|
@@ -28897,6 +29043,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
|
|
|
28897
29043
|
};
|
|
28898
29044
|
return result({
|
|
28899
29045
|
mode: "non_script",
|
|
29046
|
+
execution_url: executionUrl(api2),
|
|
28900
29047
|
response: await api2.request("/flow/codeblock", {
|
|
28901
29048
|
method: "POST",
|
|
28902
29049
|
body: JSON.stringify(body),
|