npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

qingflow-mcp

v0.3.24

Published

MCP server for Qingflow CRUD workflows

Readme

Qingflow MCP (CRUD)

This MCP server wraps Qingflow OpenAPI for:

  • qf_apps_list
  • qf_apps_info_list
  • qf_app_info_get
  • qf_app_packages_list
  • qf_departments_list
  • qf_department_users_list
  • qf_users_list
  • qf_user_get
  • qf_form_get
  • qf_field_resolve
  • qf_query_plan
  • qf_write_plan
  • qf_records_list
  • qf_record_get
  • qf_records_batch_get
  • qf_export_csv
  • qf_export_json
  • qf_query (unified read entry: list / record / summary)
  • qf_records_aggregate (deterministic grouped metrics)
  • qf_apply_audit_records_list
  • qf_apply_audit_record_get
  • qf_record_create
  • qf_record_update
  • qf_operation_get

It intentionally excludes delete for now.

Setup

Runtime requirement:

  • Node.js >=18
  1. Install dependencies:
npm install
  1. 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 dev

Build and run:

npm run build
npm start

Run tests:

npm test

Command Line Usage

qingflow-mcp still defaults to MCP stdio mode:

qingflow-mcp

Use 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_query

CLI Install

Global install from GitHub:

npm i -g git+https://github.com/853046310/qingflow-mcp.git

Install 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 | bash

Safer (review script before execution):

curl -fsSL https://raw.githubusercontent.com/853046310/qingflow-mcp/main/install.sh -o install.sh
less install.sh
bash install.sh

MCP client config example:

{
  "mcpServers": {
    "qingflow": {
      "command": "qingflow-mcp",
      "env": {
        "QINGFLOW_BASE_URL": "https://api.qingflow.com",
        "QINGFLOW_ACCESS_TOKEN": "your_access_token"
      }
    }
  }
}

Recommended Flow

  1. qf_apps_list to pick app.
  2. qf_form_get to inspect field ids/titles and field_summaries[].write_format.
  3. For complex forms, run qf_write_plan first to surface static blockers and linked-field risks.
  4. qf_record_create or qf_record_update.
  5. If create/update returns only request_id, call qf_operation_get to resolve async result.

Directory / org flow:

  1. qf_departments_list to inspect department tree.
  2. qf_department_users_list to inspect one department's members.
  3. qf_users_list for workspace-wide pagination.
  4. qf_user_get for one exact user.

Admin app flow:

  1. qf_apps_list for lightweight visible app listing.
  2. qf_apps_info_list for admin-level app detail listing.
  3. qf_app_info_get for one exact app.
  4. qf_app_packages_list for user-visible app packages.

Audit flow:

  1. qf_apply_audit_records_list to inspect one record's workflow history.
  2. qf_apply_audit_record_get to 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:

  1. qf_form_get
    • field_summaries[].write_format is populated for member/department fields.
  2. qf_tool_spec_get
    • qf_record_create / qf_record_update include member/department examples in limits.special_field_write_formats.
  3. qf_record_create / qf_record_update
    • invalid member/department values fail fast with FIELD_VALUE_FORMAT_ERROR.
  4. qf_write_plan
    • performs static preflight only
    • detects obvious missing required fields, option-linked fields, readonly/system field writes
    • warns when questionRelations means 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:

  1. the form has conditional required fields
  2. selected options may reveal linked fields
  3. 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.

  1. query_mode=auto:
    • if apply_id is 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.
  2. query_mode=list|record|summary forces explicit behavior.
  3. In list mode, time_range is translated to list filters when from or to is provided.
  4. In list mode, select_columns is required.
  5. In list mode, row cap defaults to 200 when max_rows and max_items are omitted.
  6. In record mode, select_columns is required.
  7. In summary mode, select_columns is optional and can be auto-derived from amount_column / time_range (max_rows defaults to 200 when omitted).

Summary mode output:

  1. summary: aggregated stats (total_count, total_amount, by_day, missing_count).
  2. rows: strict column rows (requested select_columns, or auto-derived preview columns when omitted).
  3. meta: field mapping, filter scope, stat policy, execution limits (output_profile=verbose only).

Directory / Org Tools

These tools expose department and member APIs without routing through qf_query:

  1. qf_departments_list
    • optional dept_id
    • local keyword, limit, offset
    • aliases: deptId, department_id, departmentId
  2. qf_department_users_list
    • required dept_id, fetch_child
    • local keyword, limit, offset
    • aliases: deptId, department_id, departmentId, fetchChild
  3. qf_users_list
    • required page_num, page_size
    • aliases: pageNum, pageSize
  4. qf_user_get
    • required user_id
    • alias: userId

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:

  1. qf_apps_info_list
    • required page_num, page_size
    • optional app_key
    • aliases: pageNum, pageSize, appKey
  2. qf_app_info_get
    • required app_key
    • alias: appKey
  3. qf_app_packages_list
    • required user_id
    • optional tag_id, keyword, limit, offset
    • aliases: userId, tagId

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:

  1. qf_apply_audit_records_list
    • required apply_id
    • alias: applyId
  2. qf_apply_audit_record_get
    • required apply_id, audit_rcd_id
    • aliases: applyId, auditRcdId

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:

  1. success: structured payload { "ok": true, "data": ... } (meta only in output_profile=verbose)
  2. failure: MCP isError=true, and text content is JSON payload like { "ok": false, "message": ..., ... }
  3. incomplete strict queries fail with { "code": "NEED_MORE_DATA", "status": "need_more_data", ... }

Deterministic read protocol (list/summary/aggregate):

  1. 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) and qf_records_aggregate always return completeness, even in compact, so agents can block on incomplete statistics
  2. when output_profile=verbose, completeness fields are:
    • result_amount
    • returned_items
    • fetched_pages
    • requested_pages
    • actual_scanned_pages
    • has_more
    • next_page_token
    • is_complete
    • partial
    • omitted_items
    • omitted_chars
  3. when output_profile=verbose, evidence fields are:
    • query_id
    • app_key
    • filters
    • selected_columns
    • time_range
    • source_pages
  4. strict_full=true makes incomplete results fail fast with NEED_MORE_DATA.
    • for qf_query(summary), strict_full enforces raw source scan completeness; sample rows may still be capped by max_rows, which is reflected by output_page_complete=false
  5. Error payloads expose error_code and fix_hint for actionable retries.
  6. Public MCP inputSchema is 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
  7. Use qf_query_plan as 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:

  1. raw_scan_complete=false: source data is not fully scanned, do not produce a final conclusion.
  2. scan_limit_hit=true: query stopped because scan budget was hit.
  3. output_page_complete=false: source may be complete, but output was truncated by max_rows or max_groups.
  4. raw_next_page_token: use this token to continue raw scan pagination (next_page_token remains as a backward-compatible alias). For qf_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):

  1. select_columns is required.

  2. include_answers=false is not allowed.

  3. Output is flat rows[] (no raw answers payload).

  4. For qf_records_list.sort[].que_id, use a real field que_id (numeric) or exact field title from qf_form_get.

  5. Avoid aliases like create_time; Qingflow often rejects them.

  6. Use max_rows (or max_items) to cap returned rows. Default row cap is 200.

  7. Use max_columns to cap returned columns per row.

  8. Use select_columns to return only specific columns (supports que_id or exact field title).

  9. The server may still trim by response-size guardrail (QINGFLOW_LIST_MAX_ITEMS_BYTES) when payload is too large.

  10. Use requested_pages and scan_max_pages for deterministic page scan.

  11. Continue with page_token from previous next_page_token.

  12. 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=400000

Troubleshooting

If you see runtime errors around Headers or missing web APIs:

  1. Upgrade Node to >=18.
  2. Upgrade package to latest:
npm i -g [email protected]
  1. Verify runtime:
node -e "console.log(process.version, typeof fetch, typeof Headers)"

Publish

npm login
npm publish

If you publish under an npm scope, use:

npm publish --access public

Security Notes

  1. Keep QINGFLOW_ACCESS_TOKEN only in runtime env vars; do not commit .env.
  2. Rotate token immediately if it appears in screenshots, logs, or chat history.

Community