# Connect Frags to your data

The [hello-world blueprint](/content/docs/guides/hello-world-plan/index.html) only reasoned over its own prompts. Real blueprints pull in live data — answers from a **web search**, messages from an **MCP server**, rows from a **database**, results from an **API**. In Frags these are all **tools**, and a session can call them either deterministically or by letting the model decide.

This guide covers both approaches, then walks through three tools: a built-in web search, a real MCP example (Slack), and a database example (Postgres).

## Prerequisites

- The [hello-world blueprint](/content/docs/guides/hello-world-plan/index.html) guide — you should be comfortable with sessions, prompts, and schemas.
- Your tools configured in `tools.json` — see [Setting up tools](/content/docs/guides/setting-up-tools/index.html). A blueprint can only reach connections that exist there.

## How tools work

Every tool a blueprint uses must be **declared** at the root with `require`, using its type and name:

```
require mcp Slack
require collection postgres
require apicp stripe
```

The three tool types are `mcp` (an MCP server), `collection` (built-in connectors like `postgres`, `http`, `fs`), and `apicp` (an OpenAPI-mapped API). The names match the connections in your `tools.json`.

The one exception is the built-in web **`search`** tool: it needs no `require` and no `tools.json` entry — you declare it only inside a session as `use search`, with no name after it. See [Search the web](/content/docs/guides/call-tools-from-a-plan#search-the-web/index.html) below.

There are then two ways to actually call a tool:

- **PreCall** — `call("function") { … }` invokes a specific tool function deterministically, _before_ the session's prompts run. Use it when you know the exact function and arguments.
- **prePrompt** — a `+` line with `use <type> <name>` in the session lets the **LLM** decide how to call the tool. Use it when the call depends on reasoning.

```
require mcp Slack

session("summary") {
    use mcp Slack
    + Fetch the last 7 days of messages from the #sales channel and read them.
    - Summarize the key wins and blockers.

schema { summary: string }
}
```

The examples below use PreCalls, since they call known functions with known arguments.

## Search the web

The lowest-friction live-data tool is web search. It's built in, so — unlike every other tool — it needs no root `require` and no `tools.json` entry. Just add `use search` to a session and the model runs searches as it reasons:

```
parameter("query", type=string, title="Search Query")

session("search_web") {
    use search

+ Search the internet for information about: {{ .params.query }}

- Provide a comprehensive answer based on the search results.
}
```

What's going on here:

- **`use search`** is the one tool declared _only_ at the session level — no root `require`, and no name after it. (Every other tool type needs both a `require` and a name.)
- The **`+` prePrompt** is where search actually runs: the model issues queries and reads the results before the main prompt.
- The **`-` prompt** turns those results into the session's answer.

This session declares no `schema`, so its output is the model's raw text answer, landing in `context.search_web` as a plain string:

```
{
  "search_web": "Spain won the 2026 FIFA World Cup, held across Canada, Mexico, and the United States, defeating Argentina 1–0 in the final. Ferran Torres scored the winning goal in the 106th minute of extra time at New York/New Jersey Stadium on July 19, 2026."
}
```

Add a `schema` when you need to pull specific fields out of the results instead of a prose answer — see [Sessions without a schema](/content/docs/guides/design-output-schemas#sessions-without-a-schema/index.html).

## Call an MCP server

Here's a trimmed version of a CEO-briefing blueprint that reads a Slack channel. It declares the Slack MCP, fetches a channel's history with a PreCall, cleans the raw payload with a **transformer**, and hands the result to a session:

```
parameter("lookBackDays", type=string, default="7", title="Look back days")

require mcp Slack

# Reshape the raw tool output down to just the messages array
transformer("clean_history") {
    onFunctionOutput = "conversations_history"
    jmesPath         = "structuredContent.messages || messages"
}

# Deterministically fetch the channel history before the session runs.
# The result is routed into vars.salesRaw.
call("conversations_history") -> salesRaw {
    channel = "C091MES33UK"
    limit   = 200
}

session("sales-pulse", target="salesSignals") {
    - You are a business analyst preparing a CEO briefing.
      Here is the recent Slack history for the sales channel:
      ```json
      {{ json .vars.salesRaw }}
      ```
      Summarize the wins, blockers, and the team's overall sentiment for the week.

schema {
        wins: string[]      # Deals closed, milestones landed
        blockers: string[]  # Active blockers or slowdowns raised in the channel
        sentiment: strong|steady|concerning|unclear  # Team momentum this week
        headline: string    # The single most important thing this week
    }
}
```

What each piece does:

- **`require mcp Slack`** declares the Slack MCP server (configured in `tools.json`).
- **`transformer(...)`** runs whenever the named function (`conversations_history`) returns, reshaping its output with a **JMESPath** expression before the LLM ever sees it — great for trimming noisy API payloads. Use `jmesPath` when you know the exact response shape.
- **`call("conversations_history") -> salesRaw { … }`** is a **PreCall**: it runs before the prompts, calls the Slack tool with those arguments, and stores the cleaned result in `vars.salesRaw`.
- The session's prompt reads that data with `{{ json .vars.salesRaw }}` and maps it into the schema.

Note the schema field `sentiment: strong|steady|concerning|unclear` - an **inline enum** that constrains the model to one of those exact values.

## Query a database

A database connection is a **collection**. A Postgres collection named `X` exposes a query function named `X_postgres_query` that takes a `query` string. Declare the collection, run your SQL in a PreCall, and route the rows into `context`:

```
parameter("listingId", type=string)  # The listing to analyze

require collection listings

# Run a query and route the rows into context.listings_offers
call("listings_postgres_query") -> context:offers {
    query = "SELECT id, buyer_email, offer_price, status, lender_name
             FROM listing_offers
             WHERE listing_id = '{{ .params.listingId }}'
               AND status NOT IN ('withdrawn', 'draft')
             ORDER BY created_at DESC"
}

session("rank-offers") {
    context "Offers retrieved from the database:\n{{ json .context.offers }}"

- You are a real estate analyst. Rank the offers from strongest to weakest
      by close certainty, and explain each ranking in one sentence.

schema {
        offer_id: string
        rank: int      # 1 = strongest offer
        note: string   # Why this offer ranks where it does
    }[]
}
```

The new ideas here:

- **`require collection listings`** declares a Postgres connection defined in `tools.json`; its query function is `listings_postgres_query`.
- **`-> context:offers`** routes the PreCall result into `context.offers`. (Compare the Slack example's `-> salesRaw`, which routes into `vars.salesRaw` — `-> name` writes to `vars`, `-> context:name` writes to `context`.)
- **`context "…"`** injects those rows into the session's LLM context so the prompt can reason over them.
- The `query` string interpolates the parameter with `{{ .params.listingId }}`. Because parameters are interpolated as text, validate or constrain any untrusted input before it reaches a query.

> Parameters are substituted into the SQL as template strings, so treat query construction like any other place you build SQL from input — keep untrusted values constrained (e.g. validate that an ID is a UUID) rather than passing them through raw.

## Next steps

- Revisit `tools.json` in [Running Frags Locally](/content/docs/guides/frags-runtime-locally#configuration-files/index.html) to wire up your own MCP servers and collections.
- Read the Frags reference on [Sessions](https://github.com/FragsHQ/frags/wiki/Sessions) and [Flow Control](https://github.com/FragsHQ/frags/wiki/FlowControl) for `after`, `expect`, and `iterate`.
- Browse full multi-session blueprints in the [blueprint marketplace](/content/blueprints/index.html), then run yours over HTTP with the [API reference](/content/docs/api/index.html).

Pull live data into a blueprint — run a web search, call an MCP server (Slack), and query a database (Postgres) with PreCalls, transformers, and context routing.
