Skip to content

Ask the docs

Answers come only from these docs and are written by an AI model (Gemini), so check the linked pages. Your question is not kept. Do not include personal details or keys.

Run MCP servers and messaging agents on your own Node infrastructure.

Flow runs in your Node process. Host an MCP server behind HTTPS, or run a messaging worker with a durable database. You choose the model, messaging provider and observability system.

Use Node 22.18 or later and a project from the quickstart. Install its locked dependencies with npm ci. Keep agent/, src/ and the package files in the deployed application; TypeScript source runs directly on Node.

The starter’s npm run mcp binds to loopback for local development. Add this entry point to listen on the container or server’s network interface:

src/serve-mcp.ts
import { createServer } from "node:http";
import { mcp } from "./mcp.ts";
const port = Number(process.env.PORT ?? 8080);
createServer((req, res) => {
if (req.url === "/health") {
res.writeHead(200).end("ok");
return;
}
void mcp.node(req, res);
}).listen(port, "0.0.0.0");

Start it with:

Terminal
node --env-file-if-exists=.env src/serve-mcp.ts

Put a TLS reverse proxy or load balancer in front of the process. Preserve the MCP paths and OAuth discovery routes, and allow streaming responses. Connect remote hosts to https://your-domain.example/mcp; plugins use /mcp/plugin.

Add the MCP authentication configuration before exposing account data. A public listener is not an authentication boundary.

Check it worked: curl http://127.0.0.1:8080/health returns ok. Then make a real tool request or send a test message; a healthy listener alone does not prove the integration works.

Messaging state includes history, pending messages, background tools, scheduled messages and the agent’s own records. The default database is in memory and is lost on restart.

StorageConfiguration
SQLitedatabase: sqlite("data/agent.db") on a persistent volume
Postgresdatabase: postgres(pool) with your node-postgres pool
Your backendImplement Database or provide a store

With a custom buildAgent, pass the database through host: options.host to createAgent, as the starter does. Here is a Postgres host configuration fragment; pool is your application’s node-postgres pool:

Postgres host configuration
import { postgres } from "@flow-engineer/flow";
const agent = buildAgent({
host: { database: postgres(pool) },
channels,
});
await agent.resume();

Call await agent.resume() before accepting messages with a durable database. Cut-off turns run again; a tool that was running is reported as interrupted rather than silently executed again.

Pass the provider’s message id as messageId to agent.message(). A retried webhook with that id is handled once. Ids are kept for seven days by default (keepMessageIdsDays). Flow Messaging’s adapter supplies these ids for you.

Run one steady-state messaging instance per database, kept warm with CPU available for timers. Conversation leases protect deployment overlap; a dead copy’s lease expires after 15 seconds. They are not a substitute for verifying multi-instance steady-state operation.

Keep the process under a supervisor, retain logs and alert when its messaging stream stops. On release, stop routing new work to the old process and allow active work to finish before termination. agent.status() reports activity and idle time.

  • Store secrets in your hosting platform’s secret store; never bake .env into an image.
  • Use live messaging senders for real customers, not shared trial numbers.
  • Keep the local playground private; the entry points above do not serve it.
  • Back up the database and test restoring it. Never share a SQLite file across machines.
  • Exercise restart recovery, a retried webhook and a slow tool before sending customer traffic.

The MCP server and tool CLI need no Flow account. Using Flow’s models, Messaging or scenario-test cloud remains optional and uses the credentials for those services.