> For the complete documentation index, see [llms.txt](https://docs.flowcp.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flowcp.ai/guides/review-and-customize-tools.md).

# Review and customize tools

After importing your API, FlowCP generates an MCP tool for every API endpoint it finds. This guide explains how to review, enable, and customize those tools.

## Opening the tools list

From your app's dashboard, click the **Tools** tab. The page shows:

* **Stat cards** at the top: total tools, enabled tools, disabled tools, and destructive tools
* **Toolbar** with a search box and filter buttons (All / Enabled / Disabled / Destructive)
* **Tool rows** — one per endpoint, grouped into category sections

## Tool categories

Tools are grouped into **categories** so related endpoints sit together instead of in one long flat list. Each category section shows a heading and the number of tools it contains.

A tool's category comes from the Swagger / OpenAPI spec:

* If the operation declares **tags**, the first tag is used (for example, an operation tagged `Orders` lands in the **Orders** section).
* Otherwise FlowCP falls back to the **resource** derived from the endpoint path (for example, `/obj/Customer` → **Customer**).
* Tools that resolve to neither appear under **Uncategorized**.

Categories are recomputed every time you re-import, and they are also sent to AI clients — see [how the server appears in your AI client](/guides/publish-your-mcp-server.md#how-the-server-appears-in-your-ai-client).

Categories also power **dynamic tool exposure**: when a server uses dynamic exposure, AI clients discover tools through the built-in `search_tools` tool, which can filter by category — so well-tagged specs make discovery much more precise. See [Tool exposure](/guides/publish-your-mcp-server.md#tool-exposure).

## Enabling a tool

All tools start **disabled**. To make a tool available to AI clients, toggle the switch on its row to **Enabled**.

You can enable or disable tools at any time. Changes take effect immediately — no re-publish required for enable/disable toggles (though publishing is required for the server to accept new connections in the first place).

> Skills, widgets, prompts, and resources are default-deny in the same way. If you connected your app from a [GitHub or GitLab repository](/guides/connect-your-api.md#import-skills-widgets-prompts-and-resources-from-the-repo), any content imported from the repo's `skills/`, `widgets/`, `prompts/`, and `resources/` directories also starts **disabled** and must be enabled on its respective page before it reaches your live MCP server.

## Add a tool manually

Not every API has a spec, and a spec doesn't always cover every endpoint. Click **Add tool** (in the page header, or in the empty state when a server has no tools) to author a tool by hand. This is the only way to add tools to a [Manual app](/guides/connect-your-api.md#connecting-a-manual-api), and it works alongside imported tools on any app.

In the dialog, set:

* **Name** — snake\_case identifier (e.g. `list_orders`).
* **Method** — `GET`, `POST`, `PATCH`, or `DELETE`. The risk level defaults from the method (you can override it).
* **Path** — appended to the app's base URL, e.g. `/api/1.1/obj/order/{id}`. Use `{name}` for path parameters.
* **Description**, **Risk level**, **Requires auth**, and whether the tool starts **Exposed**.
* **Input parameters** — use the visual builder to add fields one at a time (name, type, required, description). FlowCP turns these into the tool's input schema; no JSON Schema knowledge needed.

A manually-created tool inherits the app's base URL and authentication automatically — you never enter a URL or credential on the tool itself. Manual tools carry a **Manual** badge and, unlike imported tools, remain **fully editable**: you can change the method, path, parameters, and risk level later from the same dialog (open it with the **Edit** button on the row). A later re-import never overwrites a manual tool, even if a generated endpoint shares its name.

### Test a manual tool

While editing a manual tool, use **Send test call** to run it once against the live API with sample arguments. FlowCP makes the real request (using the app's base URL and auth — connect your account in the [Playground](/guides/chat-test-your-mcp-server.md) first for User-OAuth servers), shows you the response, and saves the response's field keys as the tool's **output schema**. Those keys then feed [response shaping](#response-shaping). Destructive tools require an explicit confirmation before the test call runs.

## Renaming and editing descriptions

Click the pencil icon on any tool row to edit:

* **Name** — the identifier shown to the AI client. Keep it short and action-oriented, e.g. `get_user_profile` or `create_invoice`.
* **Description** — the natural-language explanation passed to the AI. Good descriptions improve the AI's ability to choose the right tool. Describe what the tool does and when to use it.

## Risk level badges

Each tool displays a risk level badge:

| Badge         | Color  | Meaning                                       |
| ------------- | ------ | --------------------------------------------- |
| `read`        | Blue   | GET endpoint — retrieves data only            |
| `write`       | Yellow | POST / PATCH / PUT — creates or modifies data |
| `destructive` | Red    | DELETE — removes data permanently             |

See [Tool risk levels](/reference/tool-risk-levels.md) for the full classification rules.

## Authentication badges

Each tool also shows whether its endpoint requires a Bearer token, as declared in the Swagger `security` field:

| Badge    | Color  | Meaning                                                                                                          |
| -------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `Auth`   | Violet | The endpoint's OpenAPI spec declares a security requirement — a valid Bearer token must be present on every call |
| `Public` | Gray   | The endpoint has no security requirement in the spec — no token is needed to call it                             |

Most endpoints are auth-protected. The `Public` badge appears only when an endpoint explicitly opts out of security in the Swagger definition (e.g. a webhook receiver or public data endpoint).

## Require confirmation

For **destructive** tools, an additional **Require confirmation** toggle is shown. When on, the AI client must present a confirmation step to the user before the tool call is executed.

> Destructive tools with **Require confirmation** off cannot be published. The publish button is blocked until every enabled destructive tool has this toggle on.

## Required OAuth scope

For **destructive** tools, a **Required OAuth scope** field is also shown next to the confirmation toggle. Enter an OAuth scope (for example `records.delete`) to gate the tool: at request time, the runtime verifies that the end user's access token actually grants that scope before the call is dispatched. If the token is missing the scope, the call fails with an `INSUFFICIENT_SCOPE` error and never reaches your API.

* Leave the field **blank** to leave the tool ungated (the default).
* Scope enforcement fails **closed** — an ambiguous or unverifiable token is rejected rather than allowed.
* This is most useful with identity providers that issue scoped tokens (Generic OAuth2, Auth0, Clerk, Okta). Bubble's opaque tokens carry no scope model, so leave the field blank for Bubble apps.

The scope you enter must be one your identity provider is configured to issue — set it in the provider's **Scopes** field on the [OAuth configuration page](/guides/configure-oauth.md).

## Linking a widget

If your server has one or more [MCP widgets](/guides/build-widgets.md), each tool row shows a **widget selector**. Pick a widget to render it inline when the tool runs, or choose **No widget** to unlink. Linking only takes effect once both the tool and the widget are enabled; the linked tool still returns its normal text, so clients without widget support degrade cleanly. See [Build MCP widgets](/guides/build-widgets.md) for how to author them.

## Response shaping

Every tool row has a collapsible **Response shaping** section. Use it to trim what the AI model sees in the tool's responses — large list responses full of internal IDs, timestamps, and nested blobs burn tokens on every call and make it harder for the agent to pick the right fields. Shaping changes only the response sent to the MCP client; **the upstream API request is unchanged**.

Four settings are available:

| Setting               | Effect                                                                                                  |
| --------------------- | ------------------------------------------------------------------------------------------------------- |
| **Keep only fields**  | Each record keeps only the listed fields (comma-separated); everything else is dropped                  |
| **Drop fields**       | Each record keeps everything except the listed fields                                                   |
| **Max items**         | List responses are capped to the first N records (1–1000)                                               |
| **Max string length** | Strings longer than N characters (20–10000) are truncated with a trailing `…`, anywhere in the response |

Details worth knowing:

* **Keep only** and **Drop** are mutually exclusive — the mode selector picks one. Field names match the **top-level keys of each record** (no dot paths for nested fields yet).
* Field projection applies to each record of a list response (including Bubble's `results` envelope, whose `cursor` / `remaining` pagination fields are preserved) or to the single object of a get-style response. Field names that don't exist in a record are silently ignored — and if **none** of the listed names match a record, FlowCP returns that record in full rather than collapsing it to an empty object, so a projection can never wipe out a result.
* Shaping runs **before** the 50 KB response cap, so a good projection means fewer truncated list responses.
* Changes take effect on the next MCP request — no re-publish needed.
* Re-running a Swagger import **preserves** your response shaping, like the enabled state.
* Leave everything empty to pass responses through untouched (the default).

### Client-requested fields

In addition to your author-configured shaping, the AI client can narrow the response itself: read tools (GET) advertise an optional **`fields`** input — an array of field names. When the model passes it, each returned record is trimmed to just those fields, on top of any shaping you configured. This is the model's escape hatch when a call returns `RESPONSE_TOO_LARGE`: it can re-issue the call asking only for the fields it needs.

* `fields` shapes only what the model sees; like your shaping, it never changes the upstream API request.
* The `fields` input advertises the record's **real field names** (drawn from the tool's response schema) as its allowed values, so the model selects valid keys instead of guessing — guessed names that match nothing are rejected before they can shape the response.
* The client can only **narrow** within what you expose — if you set **Keep only fields**, a client `fields` request is intersected with it; **Drop fields** are always removed. The client can never widen the projection.
* Oversized list responses are now **truncated** (with `_truncated` / `_omitted` markers) rather than replaced wholesale with a `RESPONSE_TOO_LARGE` error, so the model still gets a usable first page.

## Filtering and searching

Use the toolbar to narrow the list:

* Type in the search box to filter by tool name, path, description, or HTTP method.
* Use the filter buttons to show only Enabled, Disabled, or Destructive tools.

## AI-enhanced descriptions

FlowCP can use AI to write richer, more accurate descriptions for your server and all its tools. AI descriptions give AI clients better context about what each tool does and when to use it — going beyond the simple templates generated at import time.

### How to use it

1. Open the **Tools** page for your server.
2. Click **Enhance with AI** in the top-right area of the page.
3. A job is queued in the background. Descriptions update within a few seconds — refresh the page to see the results.

The server description (shown in italics below the page heading) is also generated at this time.

### Automatic enhancement on import

AI descriptions run automatically every time you import a Swagger spec, as long as an OpenRouter API key is configured. If the key is missing, tools keep their template descriptions and no error is shown.

### Configuration

Two environment variables control AI descriptions:

| Variable             | Default                               | Description                                                                    |
| -------------------- | ------------------------------------- | ------------------------------------------------------------------------------ |
| `OPENROUTER_API_KEY` | *(empty)*                             | Your OpenRouter API key. Leave blank to disable AI descriptions.               |
| `OPENROUTER_MODEL`   | `anthropic/claude-haiku-4-5-20251001` | The model used to generate descriptions. Any OpenRouter-supported model works. |

Get an API key at [openrouter.ai/keys](https://openrouter.ai/keys).

### Editing AI descriptions

AI-generated descriptions are just text — you can edit them the same way as any other description (pencil icon on the tool row). Manual edits are never overwritten unless you click **Enhance with AI** again.

## Searching and filtering list tools (Bubble)

> This section applies to **Bubble** apps. For generic OpenAPI providers, list parameters come from the endpoint's own declared query parameters in the spec — FlowCP does not inject Bubble's search schema.

Tools generated from Bubble **GET /obj/{type}** endpoints (names like `list_customers`) support the [Bubble Data API search parameters](https://manual.bubble.io/help-guides/integrations/api/the-bubble-api/the-data-api/data-api-requests#searching-for-things):

| Parameter                  | Purpose                                                                      |
| -------------------------- | ---------------------------------------------------------------------------- |
| `constraints`              | Filter records (array of `{ key, constraint_type, value }` or a JSON string) |
| `cursor`, `limit`          | Pagination                                                                   |
| `sort_field`, `descending` | Sort order                                                                   |

Example tool input for an AI client:

```json
{
  "constraints": [
    { "key": "unitname", "constraint_type": "text contains", "value": "Unit" },
    { "key": "unitnumber", "constraint_type": "greater than", "value": "3" }
  ],
  "limit": 10
}
```

If your tools were imported before search support was added, **re-run Swagger import** on the app to refresh list tool input schemas. Enable/disable state is preserved on re-import.

## Publishing after changes

Tool enable/disable changes are live immediately. Changes to names, descriptions, or the confirmation flag take effect on the next MCP request after saving. You only need to re-publish the server if you initially had it in draft status.
