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.

Define a product capability once. Call it from MCP, a CLI or your messaging agent.

A tool is a TypeScript function with a typed input. It reads from your product or takes an action, then returns data the caller can use.

Tools are what make a conversation more than an answer box. They let someone check availability, move a booking or place an order against your real systems. Start with the task a person wants to complete, then expose the operations needed to do it.

This guide adds get_hours to the starter. You will call it from the CLI, then make the same capability available in your MCP server and messaging agent.

Create a project with the quickstart. The starter already imports tool and z in src/tools.ts and exports a tools array.

Add this definition inside the existing tools array in src/tools.ts. Keep the other starter tools so its existing commands and test scenarios continue to work.

src/tools.ts — add to the tools array
tool({
name: "get_hours",
title: "Opening hours",
description: "Get the opening and closing time and the business time zone.",
input: z.object({}),
output: z.object({
opens: z.string(),
closes: z.string(),
timeZone: z.string(),
}),
signIn: "none",
hints: { readOnly: true, openWorld: false },
run: () => ({
opens: "08:00",
closes: "18:00",
timeZone: "America/New_York",
}),
}),

This example returns fixed data so you can try it immediately. In your product, run can call your API or database and return its result.

Restart the MCP server to load your change, then call the tool in another terminal:

First terminal
npm run mcp

In the second terminal:

Second terminal
npm run cli -- get-hours --json

Check it worked: the CLI returns the hours from your function.

Result
{
"opens": "08:00",
"closes": "18:00",
"timeZone": "America/New_York"
}

No CLI configuration is needed for this new tool: unnamed commands use the tool’s name with underscores changed to dashes. The MCP server lists get_hours too. Restart npm start to use it in the playground, then ask “What time do you open?”

FieldPurpose
nameThe stable identifier used by the agent and MCP clients
descriptionWhat the tool does and when a model should call it
inputA Zod schema that validates the caller’s arguments
runThe function that performs the operation
titleA short name shown to people
outputThe result schema; MCP checks it and sends structured content
signInWhether the operation needs a verified account
hintsWhether it reads, writes, can overwrite, or reaches external systems

Describe the input a person would naturally ask for. Zod validates it before run is called. For a tool that looks up a product, the input could be:

Input schema
input: z.object({
product_id: z.string().describe("The product id returned by a search"),
include_stock: z.boolean().default(false),
}),

Return the data a later tool call needs, such as an id or status. Keep credentials and internal implementation details out of the returned result.

In Framework 0.8.0, a tool can provide Markdown for people while retaining structured data for models and scripts. Add these fields to a tool whose run returns the matching object:

Tool result
output: z.object({ order_id: z.string(), ready_by: z.string() }),
format: (result) => `**Order ${result.order_id}** is ready by ${result.ready_by}.`,

Expected result: { order_id: "BL-1040", ready_by: "10:30" } produces the text **Order BL-1040** is ready by 10:30. MCP returns it in content alongside the data in structuredContent; the CLI prints it by default. Messaging models still receive the data. format requires output and runs after output validation.

run receives a second argument, context, supplied by the framework. Use its verified identity to find the account, rather than asking a model to provide an account id.

ValueUse it for
context.keyYour verified account key, or null without one
context.personThe stable person identity
context.credentialsServer-side access to a configured backend
context.idempotencyKeyDeduplicating a backend write on retries
context.signalCancelling work when the conversation closes
context.recordsThe agent’s own small records, kept across conversations (records)
context.eventsThe agent’s own business events side, once events.attach(agent) ran: save a person’s watch there (events)

The authentication guide shows how to connect your existing login and credentials provider.

Hosts that support status text can show a short line while a tool runs and when it finishes:

Tool metadata
status: {
running: "Checking availability…",
done: "Availability checked",
},

For messaging, Flow handles acknowledgement and background execution. deadlineMs limits how long a reply waits for one tool; it does not replace an API client’s own timeout.

Terminal
npx @flow-engineer/flow check

This checks tool descriptors, skill references and CLI configuration. Exercise the tool as well: a valid schema does not establish that your API call returns the right result.