FlowMCP

FlowMCP

An MCP server that exposes deterministic workflows as tools, allowing small models to reliably orchestrate APIs and other MCP servers with minimal parameters.

Category
访问服务器

README

FlowMCP

Most MCP servers wrap an entire platform: every endpoint becomes a tool, the model gets a 40-tool surface, and orchestration is outsourced to sampling — then everyone blames the model. FlowMCP inverts that: workflows are the tools. Each MCP tool is one known, named workflow; a deterministic engine executes the steps; the model's only job is picking the flow and filling 2–3 parameters. Small models (7–30B) can drive this reliably, because there is almost nothing to get wrong.

Quickstart (60 seconds)

git clone https://github.com/PeterGreenAppliedAI/FlowMCP.git && cd FlowMCP
npm install
npm test          # hermetic — no network needed
npm start         # serves MCP over stdio

Point any MCP client at it. Claude Desktop / Claude Code / anything MCP:

{
  "mcpServers": {
    "flowmcp": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/flowmcp/src/server.ts"]
    }
  }
}

Your client will list two tools — morning_brief and hn_top — not forty. Both run against keyless public APIs (Open-Meteo, Hacker News), so they work on a fresh clone with zero configuration.

> morning_brief city="Lisbon"

# Morning brief — Lisbon, Portugal

## Weather today
High 29.4°C / low 19.3°C, 0% chance of rain.

## Top of Hacker News
- **…** — 330 points https://…

Flow file format

Flows are data, not code. The server loads every flows/*.flow.json5 at startup and exposes each as one MCP tool. An invalid flow is a loud startup error naming the file and field.

{
  name: 'morning_brief',          // becomes the MCP tool name (snake_case)
  description: 'WHEN TO USE: …',  // ≤300 chars — this is the model's entire manual
  input: {                        // 0–3 parameters, no more
    city: { type: 'string', description: 'City for the weather', required: false, default: 'New York' },
  },
  env: ['WEATHER_API_KEY'],       // ONLY these env vars are visible to {{env.X}} — least privilege
  steps: [ /* run in order; each result is available as steps.<id> */ ],
  output: '{{steps.render}}',     // the tool's text result
}

Check a directory without serving: npm start -- --flows ./my-flows --validate exits 0 if every flow is valid, 1 with the file and field otherwise.

Step kinds

kind fields what it does
http_request method (GET/POST), url, headers?, body?, timeoutMs? (default 15000) Fetch a URL; JSON responses are parsed. One automatic retry on network error — GET only: a timed-out POST may have landed, so it is never retried.
transform expr Reshape prior results with a sandboxed expression — paths, object/array literals, comparisons. No code execution.
template template Mustache-style string build: {{steps.x.y[0]}}. Arrays join line-per-item; missing terminal values render as ''.
map over, as? (default item), step Run one leaf step per array element, sequentially, max 10 items — slice with steps.ids[0:5].
branch if, then, else? Evaluate a condition, run one of two step lists. No nested branches.
mcp_call server, tool, args?, timeoutMs? (default 30000), maxResultChars? (default 8000) Call one tool on a downstream MCP server from servers.json5. See Composition below.

Everything downstream of a step sees input.*, env.* (for {{env.API_KEY}} — never put secrets in flow files), and steps.<id>. A failed step aborts the flow and returns a structured isError result naming the step. Whole-flow timeout: 60s.

Writing your own flow

Drop a file in a flows directory, restart the server — that's the whole workflow. The server reads flows/ in the repo by default; point it anywhere with --flows (or the FLOWMCP_FLOWS_DIR env var), which is how you keep private flows out of a public checkout:

npm start -- --flows ~/my-flows
// flows/cat_fact.flow.json5
{
  name: 'cat_fact',
  description: 'WHEN TO USE: the user wants a random cat fact.',
  input: {},
  steps: [
    { id: 'fact', kind: 'http_request', url: 'https://catfact.ninja/fact' },
    { id: 'render', kind: 'template', template: 'Cat fact: {{steps.fact.fact}}' },
  ],
  output: '{{steps.render}}',
}

Composition: wrapping other MCP servers

Flows can call tools on other MCP servers — and this is where the thesis becomes an operation instead of an opinion. Register downstream servers in a servers.json5 next to your flow files:

{
  github: {
    command: 'npx',
    args: ['-y', '@modelcontextprotocol/server-github'],
    env: { GITHUB_TOKEN: '{{env.GITHUB_TOKEN}}' },  // interpolated — never inline secrets
    allow: [],                                       // non-read-only tools need explicit listing
    shell: true,                                     // Windows: npx is a .cmd shim — raw spawn can't exec it
  },
}

(shell defaults to false. On Windows, .cmd shims like npx need shell: true — or point command directly at a Node entry point. servers.json5 is operator-trusted config, so the shell opt-in is a portability knob, not an injection surface.)

Then use an mcp_call step like any other:

{ id: 'issue', kind: 'mcp_call', server: 'github', tool: 'get_issue',
  args: { owner: 'x', repo: 'y', issue_number: '{{input.n}}' } }

The key property: the wrapped server's 40 tools never appear in FlowMCP's tools/list. 40 tools in, 3 workflows out — the model's surface never grows, no matter how many servers sit behind it.

Rules of engagement:

  • Read-only by default, fail-closed. A downstream tool is callable only if it declares annotations.readOnlyHint: true — or you explicitly name it in that server's allow list. Naming a write tool is a consent moment, on purpose — and it changes what FlowMCP advertises: annotations are computed per flow from its steps, so a flow containing a POST or an allowlisted write tool is published with readOnlyHint: false, destructiveHint: true. FlowMCP never tells a client a write-capable flow is read-only.
  • Children get a minimal environment. Downstream servers receive a baseline (PATH, HOME, …) plus the vars you configure in their env block — never the whole parent environment, unless you set inheritEnv: true for that server.
  • One session per child, not per flow. Downstream servers spawn lazily on first use, stay alive across calls, respawn on crash (3 attempts, then a 5s backoff), and shut down after 5 minutes idle. The client handshakes at the newest supported protocol revision and validates what comes back.
  • The step timeout covers spawn + handshake + call as one unit, bounded by the flow's 60s deadline — a slow cold-start can't invisibly eat the budget.
  • Results are capped at maxResultChars (default 8K) — downstream verbosity is not your flow's problem to inherit. structuredContent is preferred when the downstream tool provides it; otherwise JSON text results are parsed so later steps can path into them.

Trust model

Flow files are trusted programs — treat them like code, review them like code. The expression language can't execute code, but a flow can still send data to any URL it names; what bounds the blast radius is what the flow can see: only the env vars it declares in env: [...] (never all of process.env), only the 0–3 inputs it declares, and only downstream MCP tools that are read-only or explicitly allowlisted. servers.json5 is operator configuration, same trust level as the server's own command line. Don't load flow files you haven't read.

Design constraints (on purpose)

  • Hand-rolled protocol, ~150 lines: initialize, tools/list, tools/call, ping over newline-delimited JSON-RPC on stdio. No MCP SDK — the server is small enough to audit in one sitting.
  • Dependencies: zod and json5. That's it.
  • Small surfaces everywhere: few tools, ≤300-char descriptions, ≤3 params. Every token in tools/list is budget spent by every client on every turn.
  • Writes are gated by construction. A flow containing a write step (a POST, or an mcp_call to an allowlisted tool) automatically gets a two-phase confirmation protocol — there is no opt-out flag. The first call runs the read steps, pauses before the first write, and returns a proposal plus a single-use confirmation token (5-minute expiry) bound to the frozen pre-write state; confirming executes exactly what was proposed, never a recomputation. A proposal template on the flow customizes the prompt. Write flows advertise readOnlyHint: false and a confirm parameter — all computed from the steps, never declared. Be precise about what this is not: the model receives the token and can confirm autonomously, so this is a checkpoint, not a guaranteed human gate — a human-in-the-loop guarantee requires the MCP host to mediate the confirmation call (which the pause makes possible).
  • stdout is the protocol channel; all logging goes to stderr.

Roadmap

  • MCP conformance matrix (Inspector-based CI against current protocol revisions)
  • A benchmark: FlowMCP vs. a 30–40-tool primitive server across 7B/30B/frontier models — completion rate, argument accuracy, tokens, tool-call count
  • Destination allowlists and HTTPS policy for http_request
  • HTTP transport for the server itself
  • Flow hot-reload

Development

npm test            # vitest: spawns the real server, speaks JSON-RPC, mocks only outbound HTTP
npm run typecheck   # strict TS, no emit
npm run build       # emits dist/ — the `flowmcp` bin entry points there

CI runs typecheck + tests on Node 20 and 22 for every push. Engineering log — what worked, what didn't, what the fix was — lives in DECISIONS.md. The flow file format is specified as a portable contract in FORMAT.md; benchmark method and results live in bench/.

MIT license.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选