qingflow-mcp
v0.3.24
Published
MCP server for Qingflow CRUD workflows
Maintainers
Readme
Qingflow MCP (CRUD)
This MCP server wraps Qingflow OpenAPI for:
qf_apps_listqf_apps_info_listqf_app_info_getqf_app_packages_listqf_departments_listqf_department_users_listqf_users_listqf_user_getqf_form_getqf_field_resolveqf_query_planqf_write_planqf_records_listqf_record_getqf_records_batch_getqf_export_csvqf_export_jsonqf_query(unified read entry: list / record / summary)qf_records_aggregate(deterministic grouped metrics)qf_apply_audit_records_listqf_apply_audit_record_getqf_record_createqf_record_updateqf_operation_get
It intentionally excludes delete for now.
Setup
Runtime requirement:
- Node.js
>=18
- Install dependencies:
npm install- Set environment variables:
export QINGFLOW_BASE_URL="https://api.qingflow.com"
export QINGFLOW_ACCESS_TOKEN="your_access_token"Optional:
export QINGFLOW_FORM_CACHE_TTL_MS=300000
export QINGFLOW_REQUEST_TIMEOUT_MS=18000
export QINGFLOW_EXECUTION_BUDGET_MS=20000
export QINGFLOW_ADAPTIVE_PAGING=1
export QINGFLOW_ADAPTIVE_MIN_PAGE_SIZE=20
export QINGFLOW_ADAPTIVE_TARGET_PAGE_MS=1200
export QINGFLOW_EXPORT_MAX_ROWS=10000
export QINGFLOW_EXPORT_DIR="/tmp/qingflow-mcp-exports"Run
Development:
npm run devBuild and run:
npm run build
npm startRun tests:
npm testCommand Line Usage
qingflow-mcp still defaults to MCP stdio mode:
qingflow-mcpUse CLI mode for quick local invocation:
# list all available tools
qingflow-mcp cli tools
# machine-readable tool list
qingflow-mcp cli tools --json
# call one tool with JSON args
qingflow-mcp cli call qf_apps_list --args '{"limit":5}'
# call from stdin
echo '{"app_key":"your_app_key","mode":"all","select_columns":[1001]}' \
| qingflow-mcp cli call qf_queryCLI Install
Global install from GitHub:
npm i -g git+https://github.com/853046310/qingflow-mcp.gitInstall from npm (pinned version):
npm i -g [email protected]Or one-click installer:
curl -fsSL https://raw.githubusercontent.com/853046310/qingflow-mcp/main/install.sh | bashSafer (review script before execution):
curl -fsSL https://raw.githubusercontent.com/853046310/qingflow-mcp/main/install.sh -o install.sh
less install.sh
bash install.shMCP client config example:
{
"mcpServers": {
"qingflow": {
"command": "qingflow-mcp",
"env": {
"QINGFLOW_BASE_URL": "https://api.qingflow.com",
"QINGFLOW_ACCESS_TOKEN": "your_access_token"
}
}
}
}Recommended Flow
qf_apps_listto pick app.qf_form_getto inspect field ids/titles andfield_summaries[].write_format.- For complex forms, run
qf_write_planfirst to surface static blockers and linked-field risks. qf_record_createorqf_record_update.- If create/update returns only
request_id, callqf_operation_getto resolve async result.
Directory / org flow:
qf_departments_listto inspect department tree.qf_department_users_listto inspect one department's members.qf_users_listfor workspace-wide pagination.qf_user_getfor one exact user.
Admin app flow:
qf_apps_listfor lightweight visible app listing.qf_apps_info_listfor admin-level app detail listing.qf_app_info_getfor one exact app.qf_app_packages_listfor user-visible app packages.
Audit flow:
qf_apply_audit_records_listto inspect one record's workflow history.qf_apply_audit_record_getto inspect one audit record's field modifications.
Full calling contract (Chinese):
Write Format Discovery
For create/update, 0.3.24 now makes special write formats explicit:
qf_form_getfield_summaries[].write_formatis populated for member/department fields.
qf_tool_spec_getqf_record_create/qf_record_updateinclude member/department examples inlimits.special_field_write_formats.
qf_record_create/qf_record_update- invalid member/department values fail fast with
FIELD_VALUE_FORMAT_ERROR.
- invalid member/department values fail fast with
qf_write_plan- performs static preflight only
- detects obvious missing required fields, option-linked fields, readonly/system field writes
- warns when
questionRelationsmeans final submit may still fail
Examples:
{
"fields": {
"归属销售": [
{ "userId": "u_123", "userName": "张三" }
],
"归属部门": [
{ "deptId": 111, "deptName": "销售部" }
]
}
}Invalid examples that will now fail:
{
"fields": {
"归属销售": "张三",
"归属部门": "销售部"
}
}qf_write_plan example:
{
"app_key": "21b3d559",
"operation": "create",
"fields": {
"客户名称": "测试客户",
"归属销售": [{ "userId": "u_123", "userName": "张三" }],
"报销类型": [{ "optionId": 1, "value": "出差" }]
}
}Use it when:
- the form has conditional required fields
- selected options may reveal linked fields
- you need a static blocker list before actual submit
Do not treat qf_write_plan as a guarantee of successful submit. It is a static preflight built from OpenAPI-visible form metadata, not the full Qingflow frontend validation engine.
Unified Query (qf_query)
qf_query is the recommended read entry for agents.
query_mode=auto:- if
apply_idis set, route to single-record query. - if summary params are set (
amount_column/time_range/stat_policy/scan_max_pages), route to summary query. - otherwise route to list query.
- if
query_mode=list|record|summaryforces explicit behavior.- In
listmode,time_rangeis translated to list filters whenfromortois provided. - In
listmode,select_columnsis required. - In
listmode, row cap defaults to 200 whenmax_rowsandmax_itemsare omitted. - In
recordmode,select_columnsis required. - In
summarymode,select_columnsis optional and can be auto-derived fromamount_column/time_range(max_rowsdefaults to 200 when omitted).
Summary mode output:
summary: aggregated stats (total_count,total_amount,by_day,missing_count).rows: strict column rows (requestedselect_columns, or auto-derived preview columns when omitted).meta: field mapping, filter scope, stat policy, execution limits (output_profile=verboseonly).
Directory / Org Tools
These tools expose department and member APIs without routing through qf_query:
qf_departments_list- optional
dept_id - local
keyword,limit,offset - aliases:
deptId,department_id,departmentId
- optional
qf_department_users_list- required
dept_id,fetch_child - local
keyword,limit,offset - aliases:
deptId,department_id,departmentId,fetchChild
- required
qf_users_list- required
page_num,page_size - aliases:
pageNum,pageSize
- required
qf_user_get- required
user_id - alias:
userId
- required
CLI examples:
qingflow-mcp cli call qf_departments_list --args '{"keyword":"销售","limit":20}'
qingflow-mcp cli call qf_department_users_list --args '{"deptId":111,"fetchChild":true}'
qingflow-mcp cli call qf_users_list --args '{"pageNum":1,"pageSize":100}'
qingflow-mcp cli call qf_user_get --args '{"userId":"u_123"}'Admin App Tools
These tools expose admin-facing app and package metadata without routing through qf_query:
qf_apps_info_list- required
page_num,page_size - optional
app_key - aliases:
pageNum,pageSize,appKey
- required
qf_app_info_get- required
app_key - alias:
appKey
- required
qf_app_packages_list- required
user_id - optional
tag_id,keyword,limit,offset - aliases:
userId,tagId
- required
CLI examples:
qingflow-mcp cli call qf_apps_info_list --args '{"pageNum":1,"pageSize":50}'
qingflow-mcp cli call qf_app_info_get --args '{"appKey":"app_demo"}'
qingflow-mcp cli call qf_app_packages_list --args '{"userId":"u_123","tagId":1001}'Audit Tools
These tools expose workflow log history as read-only MCP tools:
qf_apply_audit_records_list- required
apply_id - alias:
applyId
- required
qf_apply_audit_record_get- required
apply_id,audit_rcd_id - aliases:
applyId,auditRcdId
- required
CLI examples:
qingflow-mcp cli call qf_apply_audit_records_list --args '{"applyId":"50001234"}'
qingflow-mcp cli call qf_apply_audit_record_get --args '{"applyId":"50001234","auditRcdId":"1111"}'Return shape:
- success: structured payload
{ "ok": true, "data": ... }(metaonly inoutput_profile=verbose) - failure: MCP
isError=true, and text content is JSON payload like{ "ok": false, "message": ..., ... } - incomplete strict queries fail with
{ "code": "NEED_MORE_DATA", "status": "need_more_data", ... }
Deterministic read protocol (list/summary/aggregate):
- output profile:
- default
output_profile=compact: return core data only (rows/row/groups/summary+next_page_token) output_profile=verbose: include full contract (completeness+evidence+meta)- exception:
qf_query(summary)andqf_records_aggregatealways returncompleteness, even incompact, so agents can block on incomplete statistics
- default
- when
output_profile=verbose,completenessfields are:result_amountreturned_itemsfetched_pagesrequested_pagesactual_scanned_pageshas_morenext_page_tokenis_completepartialomitted_itemsomitted_chars
- when
output_profile=verbose,evidencefields are:query_idapp_keyfiltersselected_columnstime_rangesource_pages
strict_full=truemakes incomplete results fail fast withNEED_MORE_DATA.- for
qf_query(summary),strict_fullenforces raw source scan completeness; sample rows may still be capped bymax_rows, which is reflected byoutput_page_complete=false
- for
- Error payloads expose
error_codeandfix_hintfor actionable retries. - Public MCP
inputSchemais strict:- numbers must be native JSON numbers
- arrays must be native JSON arrays
- objects must be native JSON objects
- booleans must be native JSON booleans
- unknown fields are rejected by the MCP boundary
- Use
qf_query_planas the only preflight tool when the agent is unsure about arguments. It can normalize loose/model-shaped inputs before a real query is issued.
For qf_query(summary) and qf_records_aggregate, read data.summary.completeness / data.completeness before concluding:
raw_scan_complete=false: source data is not fully scanned, do not produce a final conclusion.scan_limit_hit=true: query stopped because scan budget was hit.output_page_complete=false: source may be complete, but output was truncated bymax_rowsormax_groups.raw_next_page_token: use this token to continue raw scan pagination (next_page_tokenremains as a backward-compatible alias). Forqf_query(summary)/qf_records_aggregate, the token carries cumulative state, so keep query arguments unchanged when resuming.
List Query Tips
Strict mode (qf_records_list):
select_columnsis required.include_answers=falseis not allowed.Output is flat
rows[](no rawanswerspayload).For
qf_records_list.sort[].que_id, use a real fieldque_id(numeric) or exact field title fromqf_form_get.Avoid aliases like
create_time; Qingflow often rejects them.Use
max_rows(ormax_items) to cap returned rows. Default row cap is 200.Use
max_columnsto cap returned columns per row.Use
select_columnsto return only specific columns (supportsque_idor exact field title).The server may still trim by response-size guardrail (
QINGFLOW_LIST_MAX_ITEMS_BYTES) when payload is too large.Use
requested_pagesandscan_max_pagesfor deterministic page scan.Continue with
page_tokenfrom previousnext_page_token.Column limits:
select_columns <= 2,max_columns <= 2.
Example:
{
"app_key": "your_app_key",
"mode": "all",
"page_size": 50,
"requested_pages": 1,
"scan_max_pages": 1,
"include_answers": true,
"max_rows": 10,
"max_columns": 2,
"select_columns": [1, "客户名称"],
"output_profile": "compact",
"strict_full": false
}For single record details (qf_record_get), the same column controls are supported:
{
"apply_id": "497600278750478338",
"max_columns": 2,
"select_columns": [1, "客户名称"],
"output_profile": "compact"
}qf_record_get requires select_columns.
Aggregate example (qf_records_aggregate):
{
"app_key": "your_app_key",
"group_by": ["归属部门", "归属销售"],
"amount_columns": ["报价总金额"],
"metrics": ["count", "sum", "avg", "min", "max"],
"time_bucket": "day",
"requested_pages": 10,
"scan_max_pages": 10,
"strict_full": true
}Batch detail example (qf_records_batch_get):
{
"app_key": "your_app_key",
"apply_ids": ["497600278750478338", "497600278750478339"],
"select_columns": [1, "客户名称"],
"max_columns": 2
}Export example (qf_export_json):
{
"app_key": "your_app_key",
"mode": "all",
"page_size": 50,
"requested_pages": 5,
"max_rows": 500,
"select_columns": [1, "客户名称"],
"file_name": "报价单导出.json"
}Optional env vars:
export QINGFLOW_LIST_MAX_ITEMS_BYTES=400000Troubleshooting
If you see runtime errors around Headers or missing web APIs:
- Upgrade Node to
>=18. - Upgrade package to latest:
npm i -g [email protected]- Verify runtime:
node -e "console.log(process.version, typeof fetch, typeof Headers)"Publish
npm login
npm publishIf you publish under an npm scope, use:
npm publish --access publicSecurity Notes
- Keep
QINGFLOW_ACCESS_TOKENonly in runtime env vars; do not commit.env. - Rotate token immediately if it appears in screenshots, logs, or chat history.
Community
- Contributing: CONTRIBUTING.md
- Security: SECURITY.md
- Conduct: CODE_OF_CONDUCT.md
