flow-codeblock-rust-mcp 0.1.9 → 0.1.10

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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +198 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@ Local stdio MCP server for Flow Codeblock Rust+Bun. It calls the server-side Rus
7
7
  Bun 1.4.0 or newer is required:
8
8
 
9
9
  ```bash
10
- bunx --bun flow-codeblock-rust-mcp@0.1.9
10
+ bunx --bun flow-codeblock-rust-mcp@0.1.10
11
11
  ```
12
12
 
13
13
  Configure the environment:
@@ -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.9"],
29
+ "args": ["--bun", "flow-codeblock-rust-mcp@0.1.10"],
30
30
  "env": {
31
31
  "FLOW_CODEBLOCK_BASE_URL": "https://flow.example.com",
32
32
  "FLOW_CODEBLOCK_TOKEN": "<YOUR_INTERNAL_ACCESS_TOKEN>"
@@ -56,6 +56,8 @@ At initialization the server sends complete tool selection, preview/confirmation
56
56
 
57
57
  Interface documents must include `schema_version`, `title`, `summary`, `endpoint`, `request`, `responses`, and `logic_description`. `endpoint` requires `methods` and `description`; `request.query` and `request.headers` must exist (use `[]` when empty). POST documents also require `request.body` with `content_type`, `schema`, and `example`. Every response requires `status`, `description`, `content_type`, `schema`, and `example`; every query/header parameter requires `name`, `type`, `required`, `description`, and `example`.
58
58
 
59
+ Every nested schema property, array item, and object-form `additionalProperties` node requires `type`, `description`, and `example`, with examples covering declared fields. MCP accepts one legacy JSON-text parse, fills examples only from matching parent examples, and uses a neutral description when one is omitted.
60
+
59
61
  `endpoint.path` is relative: omit it when creating and use `/flow/codeblock/<actual-script-id>` when updating. Public request URLs combine the caller-provided service origin with `/flow/codeblock/{{script_id}}`. Never put real tokens, passwords, cookies, or Authorization values in code, documents, examples, or URLs.
60
62
 
61
63
  `interface_doc_patch` and `document_patch` require a positive integer `expected_version` and support at most 256 `add/remove/replace/move/copy/test` operations. Patch previews return operation counts, JSON Pointer paths, warnings, and version information, never the complete merged document.
package/dist/index.js CHANGED
@@ -27896,6 +27896,7 @@ var interfaceDocNestedRules = [
27896
27896
  ];
27897
27897
  var interfaceDocInputDescription = [
27898
27898
  "A complete script-interface-doc.v1 document is required when creating a script, changing code, or saving documentation.",
27899
+ "Submit interface_doc/document as a JSON object. Legacy JSON text is accepted and parsed once by MCP for compatibility. MCP fills nested examples only from matching parent examples, uses a neutral description when one is omitted, and never guesses a missing type.",
27899
27900
  `Required structure: ${Object.entries(interfaceDocRequiredFields).map(([key, fields]) => `${key}=[${fields.join(", ")}]`).join("; ")}.`,
27900
27901
  ...interfaceDocNestedRules,
27901
27902
  "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>.",
@@ -27934,6 +27935,73 @@ var interfaceDocPatchJsonSchema = {
27934
27935
  ]
27935
27936
  }
27936
27937
  };
27938
+ var schemaNodeInputSchema;
27939
+ schemaNodeInputSchema = exports_external.lazy(() => exports_external.looseObject({
27940
+ type: exports_external.string().min(1).describe("JSON Schema type such as object, array, string, integer, number, or boolean."),
27941
+ description: exports_external.string().min(1).describe("Description of this nested field or data structure."),
27942
+ example: exports_external.unknown().describe("A concrete value matching this nested schema node."),
27943
+ properties: exports_external.record(exports_external.string(), schemaNodeInputSchema).optional().describe("Map fixed object field names to child schema nodes."),
27944
+ items: schemaNodeInputSchema.optional().describe("Child schema node for each array item."),
27945
+ additionalProperties: exports_external.union([exports_external.boolean(), schemaNodeInputSchema]).optional().describe("Boolean or child schema for dynamic object keys."),
27946
+ required: exports_external.array(exports_external.string()).optional().describe("Runtime-required object field names.")
27947
+ }));
27948
+ var schemaRootInputSchema = exports_external.looseObject({
27949
+ type: exports_external.string().min(1).describe("JSON Schema type such as object, array, string, integer, number, or boolean."),
27950
+ description: exports_external.string().optional().describe("Description of the root data structure."),
27951
+ example: exports_external.unknown().optional().describe("Complete example matching this root schema."),
27952
+ properties: exports_external.record(exports_external.string(), schemaNodeInputSchema).optional().describe("Map fixed object field names to child schema nodes."),
27953
+ items: schemaNodeInputSchema.optional().describe("Child schema node for each array item."),
27954
+ additionalProperties: exports_external.union([exports_external.boolean(), schemaNodeInputSchema]).optional().describe("Boolean or child schema for dynamic object keys."),
27955
+ required: exports_external.array(exports_external.string()).optional().describe("Runtime-required object field names.")
27956
+ });
27957
+ var parameterInputSchema = exports_external.looseObject({
27958
+ name: exports_external.string().min(1).describe("Caller-facing query parameter or HTTP header name."),
27959
+ type: exports_external.enum(["string", "integer", "number", "boolean", "array", "object"]).describe("Parameter JSON type."),
27960
+ required: exports_external.boolean().describe("Whether the parameter is required at runtime."),
27961
+ description: exports_external.string().min(1).describe("Purpose and constraints of the parameter."),
27962
+ example: exports_external.unknown().describe("Concrete example value for the parameter."),
27963
+ default: exports_external.unknown().optional().describe("Optional default value."),
27964
+ format: exports_external.string().optional().describe("Optional format hint."),
27965
+ enum_values: exports_external.array(exports_external.unknown()).optional().describe("Optional allowed values.")
27966
+ });
27967
+ var interfaceDocToolInputObjectSchema = exports_external.looseObject({
27968
+ schema_version: exports_external.literal("script-interface-doc.v1").describe("Fixed document contract version."),
27969
+ title: exports_external.string().min(1).describe("Interface document title."),
27970
+ summary: exports_external.string().min(1).describe("One-sentence caller-facing summary."),
27971
+ endpoint: exports_external.looseObject({
27972
+ methods: exports_external.array(exports_external.enum(["GET", "POST"])).min(1).describe("HTTP methods supported by the endpoint."),
27973
+ path: exports_external.string().optional().describe("Actual /flow/codeblock/{script_id} path on update."),
27974
+ description: exports_external.string().min(1).describe("What the callable endpoint does.")
27975
+ }).describe("HTTP endpoint contract."),
27976
+ request: exports_external.looseObject({
27977
+ query: exports_external.array(parameterInputSchema).describe("Caller-facing URL query parameters; use [] when empty."),
27978
+ headers: exports_external.array(parameterInputSchema).describe("Caller-facing HTTP headers; use [] when empty."),
27979
+ body: exports_external.looseObject({
27980
+ content_type: exports_external.literal("application/json").describe("Must be application/json."),
27981
+ schema: schemaRootInputSchema.describe("Request body JSON Schema."),
27982
+ example: exports_external.unknown().describe("Complete request body example matching schema.")
27983
+ }).optional().describe("POST request body.")
27984
+ }).describe("Caller request contract."),
27985
+ responses: exports_external.array(exports_external.looseObject({
27986
+ status: exports_external.number().int().min(100).max(599).describe("HTTP status code from 100 through 599."),
27987
+ description: exports_external.string().min(1).describe("Business meaning of this response branch."),
27988
+ content_type: exports_external.literal("application/json").describe("Must be application/json."),
27989
+ schema: schemaRootInputSchema.describe("Response body JSON Schema."),
27990
+ example: exports_external.unknown().describe("Complete response example matching schema.")
27991
+ })).min(1).describe("Complete response branches at the document root."),
27992
+ logic_description: exports_external.string().min(20).describe("Endpoint processing logic; at least 20 characters."),
27993
+ usage_refs: exports_external.array(exports_external.unknown()).optional().describe("Real application references only.")
27994
+ });
27995
+ function parseLegacyJsonDocument(value) {
27996
+ if (typeof value !== "string")
27997
+ return value;
27998
+ try {
27999
+ return JSON.parse(value);
28000
+ } catch {
28001
+ return value;
28002
+ }
28003
+ }
28004
+ var interfaceDocToolInputSchema = exports_external.preprocess(parseLegacyJsonDocument, interfaceDocToolInputObjectSchema).describe(interfaceDocInputDescription);
27937
28005
  function assertInterfaceDocPatch(patch) {
27938
28006
  const parsed = interfaceDocPatchSchema.safeParse(patch);
27939
28007
  if (!parsed.success)
@@ -27945,18 +28013,82 @@ function isObject2(value) {
27945
28013
  function hasOwn(object4, key) {
27946
28014
  return Object.prototype.hasOwnProperty.call(object4, key);
27947
28015
  }
28016
+ function normalizeSchemaNode(schema, parentExample, fieldName, root = false) {
28017
+ if (!root) {
28018
+ if (typeof schema.description !== "string" || !schema.description.trim()) {
28019
+ schema.description = `Value for field "${fieldName}".`;
28020
+ }
28021
+ if (!hasOwn(schema, "example") && parentExample !== undefined) {
28022
+ schema.example = structuredClone(parentExample);
28023
+ }
28024
+ }
28025
+ const example = hasOwn(schema, "example") ? schema.example : parentExample;
28026
+ if (schema.type === "array") {
28027
+ if (isObject2(schema.items)) {
28028
+ const itemExample = Array.isArray(example) && example.length > 0 ? example[0] : undefined;
28029
+ normalizeSchemaNode(schema.items, itemExample, `${fieldName} item`);
28030
+ }
28031
+ return;
28032
+ }
28033
+ if (schema.type !== "object")
28034
+ return;
28035
+ const properties = isObject2(schema.properties) ? schema.properties : undefined;
28036
+ if (properties) {
28037
+ for (const [key, propertySchema] of Object.entries(properties)) {
28038
+ if (!isObject2(propertySchema))
28039
+ continue;
28040
+ const childExample = isObject2(example) && hasOwn(example, key) ? example[key] : undefined;
28041
+ normalizeSchemaNode(propertySchema, childExample, key);
28042
+ }
28043
+ }
28044
+ if (isObject2(schema.additionalProperties)) {
28045
+ const knownKeys = new Set(properties ? Object.keys(properties) : []);
28046
+ const dynamicExample = isObject2(example) ? Object.entries(example).find(([key]) => !knownKeys.has(key))?.[1] : undefined;
28047
+ normalizeSchemaNode(schema.additionalProperties, dynamicExample, `${fieldName} value`);
28048
+ }
28049
+ }
28050
+ function normalizeInterfaceDocument(document) {
28051
+ if (!isObject2(document))
28052
+ return document;
28053
+ const normalized = structuredClone(document);
28054
+ const containers = [];
28055
+ if (isObject2(normalized.request) && isObject2(normalized.request.body)) {
28056
+ containers.push(normalized.request.body);
28057
+ }
28058
+ if (Array.isArray(normalized.responses)) {
28059
+ containers.push(...normalized.responses.filter(isObject2));
28060
+ }
28061
+ for (const container of containers) {
28062
+ const schema = isObject2(container.schema) ? container.schema : undefined;
28063
+ if (!schema)
28064
+ continue;
28065
+ if (!hasOwn(container, "example") && hasOwn(schema, "example")) {
28066
+ container.example = structuredClone(schema.example);
28067
+ }
28068
+ normalizeSchemaNode(schema, container.example, "root", true);
28069
+ }
28070
+ return normalized;
28071
+ }
27948
28072
  function requireText(object4, key, path, issues, minLength = 1) {
27949
28073
  const value = object4[key];
27950
28074
  if (typeof value !== "string" || value.trim().length < minLength) {
27951
28075
  issues.push(`${path}.${key} must be a non-empty string with at least ${minLength} characters`);
27952
28076
  }
27953
28077
  }
27954
- function validateSchemaExampleCoverage(schema, example, schemaPath, examplePath, issues) {
28078
+ function validateSchemaExampleCoverage(schema, example, schemaPath, examplePath, issues, requireMetadata = false) {
27955
28079
  if (typeof schema.type !== "string") {
27956
28080
  issues.push(`${schemaPath}.type is required`);
27957
- return;
28081
+ }
28082
+ if (requireMetadata) {
28083
+ requireText(schema, "description", schemaPath, issues);
28084
+ if (!hasOwn(schema, "example")) {
28085
+ issues.push(`${schemaPath}.example is required`);
28086
+ }
27958
28087
  }
27959
28088
  if (schema.type === "array") {
28089
+ if (!isObject2(schema.items)) {
28090
+ issues.push(`${schemaPath}.items is required for array schemas`);
28091
+ }
27960
28092
  if (!Array.isArray(example)) {
27961
28093
  issues.push(`${examplePath} must be an array`);
27962
28094
  return;
@@ -27965,14 +28097,15 @@ function validateSchemaExampleCoverage(schema, example, schemaPath, examplePath,
27965
28097
  issues.push(`${schemaPath}.items must fully describe array elements`);
27966
28098
  return;
27967
28099
  }
27968
- example.forEach((item, index) => validateSchemaExampleCoverage(schema.items, item, `${schemaPath}.items`, `${examplePath}[${index}]`, issues));
28100
+ example.forEach((item, index) => validateSchemaExampleCoverage(schema.items, item, `${schemaPath}.items`, `${examplePath}[${index}]`, issues, true));
27969
28101
  return;
27970
28102
  }
27971
28103
  if (schema.type !== "object")
27972
28104
  return;
27973
28105
  const properties = isObject2(schema.properties) ? schema.properties : undefined;
27974
28106
  const additionalProperties = isObject2(schema.additionalProperties) ? schema.additionalProperties : undefined;
27975
- if (!properties && !additionalProperties) {
28107
+ const allowsAdditionalProperties = additionalProperties !== undefined || schema.additionalProperties === true;
28108
+ if (!properties && !allowsAdditionalProperties) {
27976
28109
  issues.push(`${schemaPath} must define properties or an additionalProperties schema`);
27977
28110
  return;
27978
28111
  }
@@ -27985,7 +28118,7 @@ function validateSchemaExampleCoverage(schema, example, schemaPath, examplePath,
27985
28118
  if (!hasOwn(example, key)) {
27986
28119
  issues.push(`${examplePath} is missing ${key}`);
27987
28120
  } else if (isObject2(propertySchema)) {
27988
- validateSchemaExampleCoverage(propertySchema, example[key], `${schemaPath}.properties.${key}`, `${examplePath}.${key}`, issues);
28121
+ validateSchemaExampleCoverage(propertySchema, example[key], `${schemaPath}.properties.${key}`, `${examplePath}.${key}`, issues, true);
27989
28122
  } else {
27990
28123
  issues.push(`${schemaPath}.properties.${key} must be a JSON Schema object`);
27991
28124
  }
@@ -27995,8 +28128,8 @@ function validateSchemaExampleCoverage(schema, example, schemaPath, examplePath,
27995
28128
  if (properties && hasOwn(properties, key))
27996
28129
  continue;
27997
28130
  if (additionalProperties) {
27998
- validateSchemaExampleCoverage(additionalProperties, value, `${schemaPath}.additionalProperties`, `${examplePath}.${key}`, issues);
27999
- } else {
28131
+ validateSchemaExampleCoverage(additionalProperties, value, `${schemaPath}.additionalProperties`, `${examplePath}.${key}`, issues, true);
28132
+ } else if (schema.additionalProperties !== true) {
28000
28133
  issues.push(`${schemaPath}.properties does not define example field ${key}`);
28001
28134
  }
28002
28135
  }
@@ -28128,7 +28261,7 @@ function interfaceDocCompletenessIssues(document, operation) {
28128
28261
  validateSchemaAndExample(response, path, issues);
28129
28262
  });
28130
28263
  }
28131
- return issues;
28264
+ return [...new Set(issues)];
28132
28265
  }
28133
28266
  function assertCompleteInterfaceDoc(document, operation) {
28134
28267
  const issues = interfaceDocCompletenessIssues(document, operation);
@@ -28222,7 +28355,7 @@ var interfaceDocSchema = {
28222
28355
  additionalProperties: false,
28223
28356
  properties: {
28224
28357
  content_type: { const: "application/json" },
28225
- schema: {},
28358
+ schema: { $ref: "#/$defs/schema_root" },
28226
28359
  example: {}
28227
28360
  }
28228
28361
  },
@@ -28234,30 +28367,68 @@ var interfaceDocSchema = {
28234
28367
  status: { type: "integer", minimum: 100, maximum: 599 },
28235
28368
  description: { type: "string", minLength: 1, maxLength: 4000 },
28236
28369
  content_type: { const: "application/json" },
28237
- schema: {},
28370
+ schema: { $ref: "#/$defs/schema_root" },
28238
28371
  example: {}
28239
28372
  }
28373
+ },
28374
+ schema_root: {
28375
+ type: "object",
28376
+ required: ["type"],
28377
+ additionalProperties: true,
28378
+ properties: {
28379
+ type: { type: "string", minLength: 1 },
28380
+ description: { type: "string", minLength: 1 },
28381
+ example: {},
28382
+ properties: { type: "object", additionalProperties: { $ref: "#/$defs/schema_node" } },
28383
+ items: { $ref: "#/$defs/schema_node" },
28384
+ additionalProperties: { anyOf: [{ $ref: "#/$defs/schema_node" }, { type: "boolean" }] },
28385
+ required: { type: "array", items: { type: "string" } }
28386
+ }
28387
+ },
28388
+ schema_node: {
28389
+ type: "object",
28390
+ required: ["type", "description", "example"],
28391
+ additionalProperties: true,
28392
+ properties: {
28393
+ type: { type: "string", minLength: 1 },
28394
+ description: { type: "string", minLength: 1 },
28395
+ example: {},
28396
+ properties: { type: "object", additionalProperties: { $ref: "#/$defs/schema_node" } },
28397
+ items: { $ref: "#/$defs/schema_node" },
28398
+ additionalProperties: { anyOf: [{ $ref: "#/$defs/schema_node" }, { type: "boolean" }] },
28399
+ required: { type: "array", items: { type: "string" } }
28400
+ }
28240
28401
  }
28241
28402
  }
28242
28403
  };
28243
- function schemaFromExample(value) {
28404
+ function schemaFromExample(value, fieldName = "value") {
28405
+ const metadata = { description: `Value for ${fieldName}.`, example: structuredClone(value) };
28244
28406
  if (Array.isArray(value)) {
28245
- return { type: "array", items: value.length > 0 ? schemaFromExample(value[0]) : { type: "string" } };
28407
+ return {
28408
+ type: "array",
28409
+ ...metadata,
28410
+ items: value.length > 0 ? schemaFromExample(value[0], `${fieldName} item`) : {
28411
+ type: "string",
28412
+ description: `Value for ${fieldName} item.`,
28413
+ example: ""
28414
+ }
28415
+ };
28246
28416
  }
28247
28417
  if (value !== null && typeof value === "object") {
28248
28418
  const entries = Object.entries(value);
28249
28419
  return {
28250
28420
  type: "object",
28251
- properties: Object.fromEntries(entries.map(([key, item]) => [key, schemaFromExample(item)])),
28421
+ ...metadata,
28422
+ properties: Object.fromEntries(entries.map(([key, item]) => [key, schemaFromExample(item, key)])),
28252
28423
  required: entries.map(([key]) => key),
28253
28424
  additionalProperties: false
28254
28425
  };
28255
28426
  }
28256
28427
  if (value === null)
28257
- return { type: "null" };
28428
+ return { type: "null", ...metadata };
28258
28429
  if (typeof value === "number")
28259
- return { type: Number.isInteger(value) ? "integer" : "number" };
28260
- return { type: typeof value };
28430
+ return { type: Number.isInteger(value) ? "integer" : "number", ...metadata };
28431
+ return { type: typeof value, ...metadata };
28261
28432
  }
28262
28433
  function codeWriterContext(mode, requirement, inputExample, includeFullSchema, baseUrl) {
28263
28434
  const common = {
@@ -28535,8 +28706,9 @@ var serverInstructions = [
28535
28706
  "Tool routing: flow_write_code only generates code and its contract; flow_execute_code tests unpublished generated code; flow_execute_script runs published scripts. When the generated code and available safe input are sufficient for a meaningful runtime test, execute it immediately without waiting for user confirmation. If required input or credentials are missing, report that runtime verification was not performed instead of inventing them.",
28536
28707
  "Script workflow: read the current version with flow_get_script before updates; creates require a complete interface_doc, while code or document updates may use a complete interface_doc or an RFC 6902 interface_doc_patch (never both, and patches require expected_version). Preview with flow_preview_script_change, then call flow_apply_script_change(confirm=true) only after explicit user confirmation. Documentation-only changes use flow_preview_script_documentation -> flow_apply_script_documentation.",
28537
28708
  "Preview IDs are single-use and time-limited. On a version conflict, expired preview, or validation failure, stop, read again, and preview again; never retry an old preview_id. Every flow_apply_* call requires confirm=true.",
28709
+ "Interface-document validation reports every discovered nested schema issue in one response; fix the complete list before making another preview call.",
28538
28710
  "script-interface-doc.v1 requires schema_version, title, summary, endpoint, request, responses, and logic_description. endpoint requires methods and description; request.query and request.headers are required arrays (use [] when empty); POST requires request.body and GET-only documents must omit it. JSON Patch supports at most 256 add/remove/replace/move/copy/test operations; preview responses show operation counts and paths, not merged documents.",
28539
- "Every query parameter and request header requires name, type, required, description, and example. Request bodies and responses require content_type=application/json, schema, and example; every response also requires status and description. Every JSON Schema node declares type, and object schemas and examples must cover each other.",
28711
+ "Every query parameter and request header requires name, type, required, description, and example. Request bodies and responses require content_type=application/json, schema, and example; every response also requires status and description. Every root JSON Schema node declares type; every nested property, array item, and object-form additionalProperties node declares type, description, and example. Arrays must define items, fixed object properties must be covered by the complete example, and additionalProperties=true is reserved for opaque upstream JSON.",
28540
28712
  "Keep endpoint.path relative: omit it on create and use /flow/codeblock/<actual-script-id> on update. Public call URLs use the caller-provided domain plus /flow/codeblock/{{script_id}}; never put real tokens, passwords, cookies, or Authorization values in code, documents, examples, or URLs.",
28541
28713
  "Script input comes from input.query, input.header, input.body, and input.cookies; for immediate non-script POST /flow/codeblock, body.input becomes global input unchanged. Treat input as a reserved, read-only runtime binding: never declare, redeclare, rebind, or destructure a local binding named input in any scope, including function parameters and nested callbacks. Use an alias such as const payload = input when a local name is needed. Use top-level return by default; use a bare qf_output assignment only for event-style/asynchronous flows or when explicitly requested, never both.",
28542
28714
  "For every initial generation and every later revision in non-script mode, final delivery always includes the complete latest generated JavaScript, even after runtime verification; never return only a patch, diff, or partial snippet. Also include caller-facing invocation instructions, parameters/examples, logic, success/error examples, and execution_url. Script delivery omits JavaScript and raw interface_doc by default and includes invocation instructions, parameters/examples, logic, success/error examples, and the published script_url unless the user asks for source or raw documentation. Code and interface_doc remain internal preview/validation/publication inputs.",
@@ -28642,7 +28814,7 @@ function withApiErrors(handler) {
28642
28814
  };
28643
28815
  }
28644
28816
  var documentationFields = {
28645
- document: exports_external.unknown().optional().describe(`Normalized script-interface-doc.v1 JSON. Choose exactly one of document, raw_document, or document_patch; complete documents are required for saves and code updates. ${interfaceDocInputDescription}`),
28817
+ document: interfaceDocToolInputSchema.optional().describe(`Normalized script-interface-doc.v1 JSON object. Choose exactly one of document, raw_document, or document_patch; complete documents are required for saves and code updates. ${interfaceDocInputDescription}`),
28646
28818
  raw_document: exports_external.string().optional().describe("JSON/OpenAPI document text for server parsing. Choose exactly one of document, raw_document, or document_patch; format=json parses JSON."),
28647
28819
  format: exports_external.literal("json").optional().describe("Format of raw_document; only json is supported."),
28648
28820
  document_patch: interfaceDocPatchSchema.optional().describe("RFC 6902 patch for an existing script only. Choose exactly one of document, raw_document, or document_patch."),
@@ -28655,7 +28827,7 @@ var changeSchema = exports_external.object({
28655
28827
  code_base64: exports_external.string().optional().describe("Non-empty Base64-encoded JavaScript, mutually exclusive with code."),
28656
28828
  description: exports_external.string().optional().describe("Script description. Can be updated without changing code."),
28657
28829
  ip_whitelist: exports_external.array(exports_external.string()).nullable().optional().describe("Source IP/CIDR allowlist. Omit on update to keep the current value; null or [] clears the restriction."),
28658
- interface_doc: exports_external.unknown().optional().describe(interfaceDocInputDescription),
28830
+ interface_doc: interfaceDocToolInputSchema.optional().describe(interfaceDocInputDescription),
28659
28831
  interface_doc_patch: interfaceDocPatchSchema.optional().describe("RFC 6902 patch for update only; mutually exclusive with interface_doc and forbidden for create."),
28660
28832
  rollback_to_version: exports_external.number().int().positive().optional().describe("Historical version to restore. Use only as a standalone update and never with code, interface_doc, or interface_doc_patch."),
28661
28833
  expected_version: exports_external.number().int().positive().optional().describe("Required for update and must be the current_version from flow_get_script for concurrency protection; forbidden for create.")
@@ -28722,6 +28894,7 @@ function assertScriptChangeInput(input) {
28722
28894
  throw new Error("rollback_to_version cannot be combined with code, interface_doc, or interface_doc_patch");
28723
28895
  }
28724
28896
  if (input.interface_doc !== undefined) {
28897
+ input.interface_doc = normalizeInterfaceDocument(input.interface_doc);
28725
28898
  assertCompleteInterfaceDoc(input.interface_doc, input.operation);
28726
28899
  }
28727
28900
  if (input.interface_doc_patch !== undefined)
@@ -28775,7 +28948,7 @@ function assertPreview(record3, operation) {
28775
28948
  return record3;
28776
28949
  }
28777
28950
  function createMcpServer({ api: api2, previews = new PreviewStore }) {
28778
- const server = new McpServer({ name: "flow-codeblock-rust", version: "0.1.9" }, { instructions: serverInstructions });
28951
+ const server = new McpServer({ name: "flow-codeblock-rust", version: "0.1.10" }, { instructions: serverInstructions });
28779
28952
  server.registerTool("flow_write_code", {
28780
28953
  title: "Get the Flow JavaScript authoring contract",
28781
28954
  description: "Call this before writing or revising Flow Codeblock JavaScript. It returns the mode-specific authoring contract, including forbidden-identifier and reserved-input replacement rules, and never writes the database, publishes a script, or executes code. For every non_script generation or revision, always deliver the complete latest generated JavaScript plus execution_url, never only a patch or partial snippet; use script for persistent GET/POST /flow/codeblock/{{script_id}} code with a complete script-interface-doc.v1 for preview, validation, and publication. Script delivery includes invocation instructions, parameters/examples, logic, success/error examples, and script_url rather than source or raw interface_doc unless requested. Set base_url only when a caller-facing URL template is needed.",
@@ -28783,7 +28956,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
28783
28956
  mode: exports_external.enum(["non_script", "script"]).describe("Generation mode. Use non_script for immediate, non-persistent execution; use script for a persistent GET/POST endpoint or HTTP redirects."),
28784
28957
  requirement: exports_external.string().min(1).max(20000).describe("Complete business requirements, input fields, expected output, external APIs, synchronization/async needs, and error behavior. Include only requirements relevant to this code."),
28785
28958
  input_example: exports_external.unknown().optional().describe("Business input example. In script mode it helps generate request.body/schema/example; in non_script mode it supplies flow_execute_code test input. Never include real credentials."),
28786
- include_full_schema: exports_external.boolean().optional().describe("Whether to include the complete JSON Schema in the response; defaults to false. The generated interface document still contains all required fields."),
28959
+ include_full_schema: exports_external.boolean().optional().describe("Whether to include the complete recursive JSON Schema in the response; defaults to true for script mode to avoid a follow-up schema call. Set false only when the caller already has the schema."),
28787
28960
  base_url: exports_external.string().url().refine((value) => {
28788
28961
  try {
28789
28962
  const parsed = new URL(value);
@@ -28794,7 +28967,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
28794
28967
  }, "base_url must be an http(s) URL without credentials or control characters").optional().describe("Optional caller service origin such as https://flow.example.com. Used only to render /flow/codeblock/{{script_id}}; credentials and control characters are forbidden.")
28795
28968
  }
28796
28969
  }, async ({ mode, requirement, input_example, include_full_schema, base_url }) => {
28797
- const context = codeWriterContext(mode, requirement, input_example, include_full_schema ?? false, base_url);
28970
+ const context = codeWriterContext(mode, requirement, input_example, include_full_schema ?? mode === "script", base_url);
28798
28971
  return result(mode === "non_script" ? { ...context, execution_url: executionUrl(api2) } : context);
28799
28972
  });
28800
28973
  server.registerTool("flow_list_scripts", {
@@ -28856,6 +29029,7 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
28856
29029
  }, withApiErrors(async (input) => {
28857
29030
  const parsed = documentationSchema.parse(input);
28858
29031
  if (parsed.document !== undefined) {
29032
+ parsed.document = normalizeInterfaceDocument(parsed.document);
28859
29033
  assertCompleteInterfaceDoc(parsed.document, "update");
28860
29034
  }
28861
29035
  if (parsed.document_patch !== undefined)
@@ -28956,6 +29130,8 @@ function createMcpServer({ api: api2, previews = new PreviewStore }) {
28956
29130
  inputSchema: documentationSchema.shape
28957
29131
  }, withApiErrors(async (input) => {
28958
29132
  const parsed = documentationSchema.parse(input);
29133
+ if (parsed.document !== undefined)
29134
+ parsed.document = normalizeInterfaceDocument(parsed.document);
28959
29135
  const expectedVersion = await fetchCurrentVersion(api2, parsed.script_id);
28960
29136
  if (parsed.document_patch !== undefined && parsed.expected_version !== expectedVersion) {
28961
29137
  throw new Error(`Script version changed from ${parsed.expected_version} to ${expectedVersion}; read and preview again`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flow-codeblock-rust-mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Flow Codeblock Rust+Bun stdio MCP Server for script management and execution.",
5
5
  "license": "MIT",
6
6
  "repository": {