https://github.com/convertigo/c8oprj-lib-flow-mcp
https://github.com/convertigo/c8oprj-lib-flow-mcp
Last synced: 15 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/convertigo/c8oprj-lib-flow-mcp
- Owner: convertigo
- Created: 2026-06-10T13:33:58.000Z (2 months ago)
- Default Branch: spike-flowscript
- Last Pushed: 2026-07-19T23:21:39.000Z (21 days ago)
- Last Synced: 2026-07-20T00:23:42.871Z (21 days ago)
- Language: JavaScript
- Size: 583 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# lib_flow_mcp
Flow-native MCP server for Convertigo Flow authoring.
This project intentionally keeps MCP tooling outside `lib_flow_engine`.
`lib_flow_engine` remains the standard runtime vocabulary; this library owns the
MCP surface and any blocks specific to Flow authoring automation.
HTTP entry point:
```text
http://localhost:18080/convertigo/api/flow-mcp
```
The UrlMapper path is `/flow-mcp`; the trailing-slash variant `/flow-mcp/` is
also mapped to the same requestable. The legacy Convertigo MCP project keeps
`/mcp`.
Runtime shape:
```text
lib_flow_mcp.McpServer
-> libs/flows/McpServer.flow.yaml
-> high-level MCP graph blocks (mcp.batch, mcp.handle, mcp.tools.call)
-> visible tools/call families (inspect, source, author, runtime)
-> private mcp.* blocks
-> reusable core/project blocks via ctx.callBlock(...)
-> explicit low-level libraries in libs/flow/lib/*.js, declared with uses
-> lib_flow_engine.Engine
```
The flow graph owns the visible protocol routing. Reusable protocol branches
that deserve a palette/catalog item are composite graph blocks in
`libs/flow/blocks/*.block.js`; for example `mcp.handle` routes one JSON-RPC
request, and `mcp.tools.call` is implemented with `mcp.tool.identify` and one
branch per tool family. Private native `mcp.*` blocks keep the low-level
JSON-RPC/Convertigo glue small. When reusable behavior has a clear contract,
expose it as a block and call it with `ctx.callBlock(...)`; keep
`libs/flow/lib/mcp.js` only for local algorithmic helpers that would add noise
to the Flow catalog, and declare that dependency with `uses: [mcp]`.
Scope naming convention for new Flow sources:
- `input.*` is the data received by the executable Flow or block implementation.
- `local.*` is the private working scope of the current execution.
- `config.*`, `current`, `result` keep their usual meanings.
- `props.*` and `flow.*` are not expression scopes. JS hooks/raw implementations
can inspect the raw node with `ctx.props(node)`.
For simple MCP tools, prefer this graph shape:
```yaml
nodes:
- id: run
block: mcp.tool.run
request: input.request
target: resource.search
out: local.response
- id: done
block: return
value: "{{ local.response }}"
```
`mcp.tool.run` prepares MCP arguments, resolves the target project, calls the
target block with `ctx.callBlock(...)`, and wraps either the result or the error
as a JSON-RPC tools/call response.
The simple read/test/introspection tools now use this shape and delegate to
core capability blocks such as `flow.list`, `flow.get`, `flow.run`,
`flow.test`, `flow.tree`, `flow.context`, `flow.outputSchema`, `flow.apply`,
`resource.*`, `block.*` and `type.*`. Keep JavaScript wrappers only when the
tool still owns MCP-specific behavior such as workspace search or Studio DBO
registration after writing a Flow.
When adding a new operation, ask whether the underlying behavior is:
- general Flow runtime behavior: add it to `lib_flow_engine`;
- MCP authoring behavior: keep it here;
- project-specific glue: keep it in the target project, preferably private.
Default authoring cycle for a blank agent context:
```text
resources/list
resources/read flow://guide/start
tools/list
flow-search to find flows, nodes, catalog entries and schemas; multi-word queries match unordered tokens
flow-code-rg / flow-code-get for FlowScript source
flow-tree / flow-get only for model-conversion debugging
flow-context when choosing paths or expressions
flow-output-schema when downstream nodes need the result shape
flow-node-output-schema when one HTTP/exec/parser producer needs schema inspection, adoption or removal
flow-schema-reset only for broader stale learned-schema cleanup
flow-code-set for broad Flow edits; it writes the FlowScript working copy
flow-code-patch for revision-checked maintenance edits on that working copy
flow-code-check / flow-code-run, then flow-code-promote once behavior is clean
flow-catalog only when search/examples are insufficient; it is summary by default
flow-block-code-rg / flow-block-code-get / flow-block-code-patch for project-local blocks
flow-block-code-set only when reusable vocabulary is needed
flow-block-create / flow-block-duplicate / flow-block-edit are compatibility facades that still write canonical .block.js
flow-type-create only for project-local property type definitions
flow-resource-search / flow-resource-get / flow-resource-patch for maintenance patches on project JS/HTML/CSS resources and Flow libraries
```
The default path remains catalog-first and sidecar-first. Custom blocks are
project vocabulary, not automatic core changes.
Project-local blocks use a canonical `*.block.js` source containing `_meta`
plus either one FlowScript function or one Rhino IIFE. Use Rhino blocks only for
JVM/Java integration code or low-level primitives. Rhino code may use Java classes
through `Packages`, but not Node.js APIs such as `require`, npm modules or
browser globals. Keep the Rhino IIFE to `run(ctx, node)` plus local helpers.
Put shared helper dependencies in `uses`; optional dynamic
`displayName(node)` / `analyze(ctx, node)` hooks stay separate from runtime code.
Executable Flows may declare request variables and reusable test inputs with a
top-level FlowScript contract:
```javascript
const _flow = {
inputs: {
city: { type: "string", description: "City name.", default: "Paris" }
},
tests: {
checkParis: { input: { city: "Paris" } }
}
}
```
`flow-code-*` tools expose this as `inputDefinitions`, `inputVariables`, and
`testCases`. Explicit `_flow.inputs` are synchronized to Convertigo request
variables so Studio, SDK callers and generated test cases see the same contract.
Without `_flow.inputs`, inputs are only inferred from `input.foo` reads and
authoring tools report a warning asking for the missing declarations.
Do not use a Rhino block as a shortcut for a whole backend feature. Keep HTTP
fetches in `http.get`/`http.request`, Convertigo calls in `requestable.call`,
array transforms in `list.*`, JSON shaping in `json.*`, and response assignment
in FlowScript. If only parsing or one Java bridge is missing, create that small
primitive and compose it visibly from the Flow.
Most tools accept either:
- `project`: Convertigo project name, resolved by the live engine;
- `projectDir`: direct filesystem path, mainly for standalone tests.
When omitted, tools operate on `lib_flow_mcp` itself. Agents should pass
`project` for application work, for example `AAAProject`.
Mutation tools accept semantic node targets first. Prefer this shape after
`flow-search` returns a `nodeId`:
```json
{
"name": "WeatherAlert",
"mutation": {
"op": "replace",
"nodeId": "setMessage",
"property": "value",
"value": "Done"
}
}
```
Use `afterNodeId`, `beforeNodeId` or `parentNodeId + slot` to insert nodes
without hard-coding array indexes:
```json
{
"name": "WeatherAlert",
"mutation": {
"op": "insert",
"afterNodeId": "setMessage",
"value": {
"id": "logDone",
"block": "log",
"message": "Done"
}
}
}
```
Low-level mutations can still use the same JSON Pointer syntax as the Flow
virtual tree:
```json
{
"name": "WeatherAlert",
"mutation": {
"op": "insert",
"path": "/nodes",
"index": "end",
"value": {
"id": "setMessage",
"block": "set",
"path": "result.message",
"value": "Done"
}
}
}
```
Use `flow-apply` to preview the updated YAML source. Use `flow-edit` to apply
the same mutation to a named project Flow sidecar.
For a broader edit, use the tree-like model round trip instead of inventing a
new command:
1. `flow-get` returns `source` and `definition`.
2. Modify `definition` as a JSON object.
3. Send it back with `flow-set` using the same `definition` property.
`flow-run`, `flow-test`, `flow-tree`, `flow-apply`, `flow-output-schema` and
`flow-block-test` also accept this `definition` shape. This is the preferred
KISS alternative to multiplying CRUD aliases such as create/update/replace.
`flow-catalog` intentionally returns summary block/type contracts by default.
Ask for `detail:"compact"` when property docs are useful. Ask for
`detail:"full"` only when icon paths, type usage lists or full descriptor
resources are useful.
When `flow-set` or `flow-edit` receives a live `project`, it also registers the
named sidecar as a minimal Flow DBO by default, saves the project, and refreshes
the Studio tree when Studio is available. Pass `register:false`, `autoSave:false`
or `refresh:false` only for deliberate tooling cases. With `projectDir` only,
registration is skipped and the tool stays a pure filesystem sidecar writer.
The common node wrappers are preferred when they fit:
- `flow-node-add`: add a node near another node or inside a parent slot.
- `flow-node-edit`: replace one property or merge several properties.
- `flow-node-move`: move a node by `nodeId`.
- `flow-node-delete`: delete a node by `nodeId`.
- `flow-node-duplicate`: duplicate a node and optionally patch the new copy.
`flow-node-add` requires a stable `id`. `flow-node-duplicate` requires `newId`
or `properties.id` to avoid creating duplicate ids.
Block authoring is intentionally explicit:
- `flow-block-get` reads any visible block source.
- `flow-block-code-set` writes canonical project-local `.block.js` source.
- `flow-block-create` is a compatibility facade that also writes `.block.js`.
- `flow-block-duplicate` copies a core/shared/project block to a new
project-local name.
- `flow-block-edit` replaces the source of an existing project-local block.
Core and shared blocks are read-only through this MCP surface. Duplicate them
first when an agent needs a custom variant.
## FlowScript spike tools
On the `spike-flowscript` branch, the MCP also exposes an experimental source
view for agents:
```text
flow-code-get
flow-code-set
flow-code-patch
flow-code-rg
flow-source-get
flow-source-validate
flow-source-patch
```
Prefer `flow-code-*` for normal agent work:
- `flow-code-get({qname})` returns only FlowScript `code` plus `revision`.
- `flow-code-set({qname, revision?, code})` writes and validates the FlowScript working copy.
- `flow-code-patch({qname, revision, codepatch|code})` applies a revision-checked edit to the working copy.
- `flow-code-run({qname, input?})` runs the current working copy without resending code.
- `flow-code-promote({qname, revision?})` saves the working copy to the official Flow.
- `flow-code-rg({qname?, pattern})` returns small FlowScript extracts.
The engine parses and validates the FlowScript, returns line-based diagnostics
when a block/property is invalid, and writes the canonical FlowScript sidecar
after validation succeeds. Legacy YAML sidecars are only fallback inputs during
the spike migration.
Keep `flow-source-*` for compiler/debug work where canonical definitions, YAML
or full analysis are intentionally needed.
This is a research path to measure whether agents transpose code instincts more
efficiently than direct block/tree MCP editing.
For iterative maintenance, prefer patching the project-local resource instead
of replacing a whole source file:
```text
flow-resource-search -> flow-resource-get -> flow-resource-patch(baseHash, unified diff)
```
The patch API is limited to Flow resources such as
`libs/flow/blocks/**/*.block.js`, `libs/flow/fragments/**/*.fragment.yaml`,
`libs/flow/lib/**/*.js`, `libs/flow/types/**/*.{type.yaml,js}` and
`libs/flow/types/editors/**/*.{html,css,js}`. It validates block/type/library
resources and parses Flow/fragment YAML by default.
Unified diff line numbers may be approximate when the surrounding context is
unique.
Search is the MCP equivalent of `rg` for Flow authoring:
```json
{
"project": "AAAProject",
"query": "temperature",
"kinds": ["node"],
"context": 1,
"limit": 20
}
```
Each node match returns `flowQName`, `nodeId` and a canonical JSON Pointer
`path`. Use `nodeId` for semantic edits and `path` for low-level mutations.
Pass `doc:false,hints:false` once the agent has learned the tool contract.
MCP responses are sanitized before they are sent to agents: internal `__flow*`
fields are removed, empty metadata fields such as `mode:""` are omitted, and
filesystem paths are shortened to project-relative paths or `engine:...`
references. Studio-only icon/resource paths may still exist in internal Flow
tree data, but they should not leak through the MCP JSON-RPC result payload.
Optional JSONL tracing can write one line per MCP request and response. Enable
it with the Convertigo symbol:
```text
flow.mcp.traceJsonl=true
flow.mcp.traceJsonl=/path/to/flow-mcp.jsonl
flow.mcp.traceJsonl.maxChars=30000
```
When set to `true`, the default file is
`/_private/flow-mcp-trace.jsonl`. Tracing is best-effort
and never fails the MCP request path. Each JSONL line includes a sanitized
payload plus a compact `summary`, `payloadChars`, `payloadTruncated` and, for
responses, `durationMs`. The optional `maxChars` symbol limits large payloads
while preserving their summary. Standalone tests may also pass
`config.mcp.traceJsonl` and `config.mcp.traceJsonlMaxChars`, but production
configuration should use symbols.
MCP resources provide the same guidance to agents that cannot read this repo:
- `flow://guide/start`
- `flow://guide/authoring`
- `flow://guide/search-and-edit`
- `flow://guide/custom-blocks`
- `flow://guide/fullstack-paperboard`
- `flow://guide/frontend-svelte`
- `flow://guide/tracing`