<!-- Source: https://docs.geopera.com/api-reference/sdks/mcp/tools · Markdown for LLMs -->

# Tool reference

The MCP gateway turns Geopera's operation surface into roughly **188 callable tools**. Each tool name is a dotted `operation_id`, its input schema is that operation's typed request body, and it carries the same scope and side-effect tier the operation enforces on `POST /v1/op/{operation_id}`.

The surface now includes the **read and list operations** an agent uses to read back what it creates — list and fetch orders, collections, items, notifications, balances, usage, job lists, alert rules and events, API keys, analytics indices, STAC search, and more. Every read tool carries `readOnlyHint`, so a well-behaved client knows it is safe to call freely. The practical effect: an agent can place an order and then poll its status, or create a collection and then list its contents, without leaving the governed surface.

The gateway adds nothing the operation does not already declare: it names each tool by its `operation_id` and stamps safety hints derived from the operation's scope and side-effect tier. For how to connect a client, see [Hosted MCP](/api-reference/sdks/mcp/hosted) and the [local quickstart](/api-reference/sdks/mcp/quickstart-local).

## How an operation becomes a tool

Every Geopera operation is reachable as `POST /v1/op/{operation_id}` with a JSON body. The gateway exposes one tool per customer-facing operation. The mapping is direct:

| MCP concept             | Source on the operation                              |
| ----------------------- | ---------------------------------------------------- |
| Tool name               | `operation_id` (e.g. `catalog.search`)               |
| Input schema            | the operation's typed request body                   |
| `x-api-key` input field | optional header parameter forwarded with the request |
| Required scope          | `x-required-scope`                                   |
| Side-effect tier        | `x-side-effect`                                      |
| Safety hints            | derived from `x-side-effect` (see below)             |

The tool name is the canonical operation identity, not a slugified label. Calling the `catalog.search` tool with a body is exactly equivalent to `POST /v1/op/catalog.search` with that body, run as the identity behind the bearer token your MCP client presents. Scopes and authentication are unchanged from the HTTP surface — see [Authentication](/api-reference/authentication) and [Scopes](/api-reference/scopes).

### Side-effect tiers and safety hints

The gateway translates each operation's `x-side-effect` tier into MCP `ToolAnnotations` so a client can reason about safety before it calls:

| `x-side-effect`  | `readOnlyHint` | `destructiveHint` | `idempotentHint` | `openWorldHint` |
| ---------------- | -------------- | ----------------- | ---------------- | --------------- |
| `read`           | true           | false             | false            | true            |
| `compute`        | false          | false             | false            | false           |
| `destructive`    | false          | true              | false            | false           |
| `external_spend` | false          | false             | true             | true            |
| `share_export`   | false          | false             | false            | true            |

`external_spend` tools (money or external provider spend) are marked idempotent because their dedicated route honours an `Idempotency-Key` — a repeat with the same key is side-effect-free. See [Idempotency](/api-reference/idempotency) and [Errors](/api-reference/errors). The scope and tier are also placed in each tool's structured `meta` (`x-required-scope`, `x-side-effect`, `x-produces`) so a client can filter or route without parsing the description text.

## Why some operations are not tools

A focused set of operations stays excluded. None of these are things an agent needs to work around — each is excluded for a concrete reason:

- **Raster tiles and rendered images** — map tiles and rendered PNG/JPEG/TIFF/WebP outputs (for example `cog.tile.render`, WMTS capabilities). These are frontend map plumbing meant for a tile-rendering map view, not a request-to-single-result tool.
- **Binary downloads** — file exports, generated reports (`reports.generate`), and clip downloads (`clip.job.download`) return large binary payloads. Those belong on a signed URL the agent can hand off, not inlined into a tool result, so the download operation itself is withheld.
- **Streaming responses** — NDJSON and server-sent-event listings and exports (for example `catalog.search_stream`) do not fit the MCP request → single-result model. Their paged, non-streaming siblings are exposed instead (the paged `stac.search` and `catalog.search` are tools; the streaming export is not).
- **Admin, cron, and unsafe mutations** — admin/internal/cron operations are scrubbed from the customer surface entirely, and a small set of untyped destructive or external-spend mutations is withheld for safety. The governed, typed mutations you need — place an order, create a collection, top up credits — remain available with their side-effect hints intact (see the side-effect table above).

To enumerate the exact tools your gateway exposes at runtime, call `list_tools` from your MCP client and read each tool's `name`, `annotations`, and `meta`.

## Exposed tools by domain

The tables below list representative tools, grouped by operation domain — both the read/list tools an agent uses to inspect state and the governed mutations it uses to change it. Each row is callable as the tool of that name and as `POST /v1/op/{name}`. For the authoritative, up-to-the-minute set, call `list_tools` from your MCP client.

### catalog

| Tool                       | Summary                                                           | Scope          | Side-effect |
| -------------------------- | ----------------------------------------------------------------- | -------------- | ----------- |
| `catalog.search`           | Search a commercial-imagery host's catalog (STAC, price-enriched) | `catalog:read` | read        |
| `catalog.federated_search` | Search every data source covering the AOI in one query            | `catalog:read` | read        |
| `catalog.granule_points`   | Extract a 3D point cloud from a GEDI or ICESat-2 granule          | `catalog:read` | read        |
| `catalog.sources.list`     | List every searchable data source + its collections               | `catalog:read` | read        |
| `stac.search`              | STAC-compliant search across the catalog (paged)                  | `catalog:read` | read        |

### items

| Tool                         | Summary                                               | Scope         | Side-effect |
| ---------------------------- | ----------------------------------------------------- | ------------- | ----------- |
| `items.get`                  | Fetch a single catalog item by id                     | `items:read`  | read        |
| `items.search`               | Search a project's items with STAC-compatible filters | `items:read`  | read        |
| `items.search_org`           | Search all items across the caller's organization     | `items:read`  | read        |
| `items.create`               | Create an item within a project                       | `items:write` | compute     |
| `items.update`               | Update an item's mutable metadata                     | `items:write` | compute     |
| `items.duplicate`            | Duplicate an item with its assets + viz profiles      | `items:write` | compute     |
| `items.detect_asset_bands`   | Auto-detect band names from reflectance statistics    | `items:read`  | compute     |
| `items.set_asset_band_names` | Rename the bands on an item's asset                   | `items:write` | compute     |
| `items.delete`               | Soft-delete an item                                   | `items:write` | destructive |

### assets

| Tool            | Summary                             | Scope         | Side-effect |
| --------------- | ----------------------------------- | ------------- | ----------- |
| `assets.delete` | Delete an asset (file) from an item | `items:write` | destructive |

### collections

| Tool                 | Summary                                          | Scope               | Side-effect |
| -------------------- | ------------------------------------------------ | ------------------- | ----------- |
| `collections.create` | Create an organizing collection within a project | `collections:write` | compute     |
| `collections.update` | Update a collection's metadata                   | `collections:write` | compute     |
| `collections.delete` | Soft-delete a collection, unlinking its items    | `collections:write` | destructive |

### projects

| Tool                     | Summary                                              | Scope            | Side-effect |
| ------------------------ | ---------------------------------------------------- | ---------------- | ----------- |
| `projects.create`        | Create a workspace in the caller's organization      | `projects:write` | compute     |
| `projects.update`        | Update a workspace's metadata                        | `projects:write` | compute     |
| `projects.archive`       | Archive a workspace, hiding it from the default list | `projects:write` | compute     |
| `projects.add_member`    | Add a member to a workspace                          | `projects:write` | compute     |
| `projects.update_member` | Change a member's role in a workspace                | `projects:write` | compute     |
| `projects.remove_member` | Remove a member from a workspace                     | `projects:write` | destructive |
| `projects.delete`        | Permanently delete a workspace and all its data      | `projects:write` | destructive |

### orders

| Tool                              | Summary                                                   | Scope          | Side-effect    |
| --------------------------------- | --------------------------------------------------------- | -------------- | -------------- |
| `orders.list`                     | List the org's orders, with status and filters            | `orders:read`  | read           |
| `orders.get`                      | Fetch a single order by id (poll its status)              | `orders:read`  | read           |
| `orders.archive.estimate`         | Preview the AOI price for a cart of archive captures      | `orders:read`  | read           |
| `orders.tasking.estimate`         | Preview the price for an AOI-priced tasking request       | `orders:read`  | read           |
| `orders.tasking.sensors`          | List the tasking sensor catalog                           | `orders:read`  | read           |
| `orders.tasking.templates.list`   | List the org's active tasking templates                   | `orders:read`  | read           |
| `orders.tasking.templates.save`   | Save a reusable tasking acquisition template              | `orders:write` | compute        |
| `orders.tasking.templates.delete` | Soft-delete a tasking template                            | `orders:write` | compute        |
| `orders.update`                   | Patch an order's tags (metadata only)                     | `orders:write` | compute        |
| `orders.place`                    | Place order(s) from a FeatureCollection                   | `orders:write` | external_spend |
| `orders.archive.place`            | Place an archive order                                    | `orders:write` | external_spend |
| `orders.tasking.place`            | Place a tasking order                                     | `orders:write` | external_spend |
| `orders.cancel`                   | Cancel an order (policy-gated), refunding its reservation | `orders:write` | external_spend |

### processing

| Tool                             | Summary                                        | Scope                | Side-effect    |
| -------------------------------- | ---------------------------------------------- | -------------------- | -------------- |
| `processing.create`              | Create a processing job                        | `processing:process` | external_spend |
| `processing.create_and_dispatch` | Create and dispatch a processing job           | `processing:process` | external_spend |
| `processing.dispatch`            | Dispatch an existing pending job to its worker | `processing:process` | external_spend |
| `processing.execute`             | Execute a process (reserve credits + dispatch) | `processing:process` | external_spend |

### clip

| Tool                    | Summary                    | Scope                | Side-effect    |
| ----------------------- | -------------------------- | -------------------- | -------------- |
| `clip.create_from_item` | Create a clip from an item | `processing:process` | external_spend |

### analytics

| Tool                         | Summary                                        | Scope               | Side-effect |
| ---------------------------- | ---------------------------------------------- | ------------------- | ----------- |
| `analytics.indices.list`     | List the available spectral indices            | `analytics:read`    | read        |
| `analytics.operations.list`  | List the registered analytics operations       | `analytics:read`    | read        |
| `analytics.execute`          | Run a registered analytics operation           | `analytics:process` | read        |
| `analytics.validate_formula` | Validate a band-math formula without executing | `analytics:read`    | read        |

### band_formulas

| Tool                   | Summary                                | Scope                 | Side-effect |
| ---------------------- | -------------------------------------- | --------------------- | ----------- |
| `band_formulas.get`    | Fetch a custom band-math formula by id | `band_formulas:read`  | read        |
| `band_formulas.create` | Create a custom band-math formula      | `band_formulas:write` | compute     |
| `band_formulas.update` | Update a custom band-math formula      | `band_formulas:write` | compute     |
| `band_formulas.delete` | Delete a custom band-math formula      | `band_formulas:write` | destructive |

### visualization

| Tool                                | Summary                                    | Scope         | Side-effect |
| ----------------------------------- | ------------------------------------------ | ------------- | ----------- |
| `visualization.list_for`            | The visualizations available for an item   | `tiles:read`  | read        |
| `visualization.profile.create`      | Create a visualization profile for an item | `items:write` | compute     |
| `visualization.profile.update`      | Update a visualization profile             | `items:write` | compute     |
| `visualization.profile.set_default` | Set a profile as the item's default        | `items:write` | compute     |
| `visualization.profile.delete`      | Delete a visualization profile             | `items:write` | destructive |

### uploads

| Tool                 | Summary                                                  | Scope           | Side-effect |
| -------------------- | -------------------------------------------------------- | --------------- | ----------- |
| `uploads.initiate`   | Start an upload session, reserving storage quota         | `uploads:write` | compute     |
| `uploads.signed_url` | Mint a signed URL for uploading a file                   | `uploads:write` | compute     |
| `uploads.progress`   | Update an upload session's progress                      | `uploads:write` | compute     |
| `uploads.complete`   | Complete an upload session                               | `uploads:write` | compute     |
| `uploads.fail`       | Mark an upload session failed, releasing its reservation | `uploads:write` | compute     |

### share

| Tool                  | Summary                                            | Scope          | Side-effect  |
| --------------------- | -------------------------------------------------- | -------------- | ------------ |
| `share.link.validate` | Validate a share link and return the shared target | `shares:read`  | read         |
| `share.link.create`   | Create a password-protected share link             | `shares:write` | share_export |
| `share.link.revoke`   | Revoke a share link                                | `shares:write` | destructive  |

### provenance

| Tool             | Summary                                         | Scope             | Side-effect |
| ---------------- | ----------------------------------------------- | ----------------- | ----------- |
| `provenance.get` | Walk the provenance/lineage graph for an entity | `provenance:read` | read        |

### alerts

| Tool                       | Summary                                            | Scope          | Side-effect |
| -------------------------- | -------------------------------------------------- | -------------- | ----------- |
| `alerts.rules.list`        | List the org's alert rules                         | `alerts:read`  | read        |
| `alerts.rule.get`          | Fetch a single alert rule by id                    | `alerts:read`  | read        |
| `alerts.events.list`       | List alert events, with filters                    | `alerts:read`  | read        |
| `alerts.test_rule`         | Test-evaluate an alert rule without persisting     | `alerts:read`  | read        |
| `alerts.create_rule`       | Create an alert rule for the caller's organization | `alerts:write` | compute     |
| `alerts.update_rule`       | Update an alert rule                               | `alerts:write` | compute     |
| `alerts.acknowledge_event` | Mark an alert event acknowledged                   | `alerts:write` | compute     |
| `alerts.delete_rule`       | Delete an alert rule                               | `alerts:write` | destructive |

### event_subscriptions

| Tool                         | Summary                                          | Scope                       | Side-effect |
| ---------------------------- | ------------------------------------------------ | --------------------------- | ----------- |
| `event_subscriptions.create` | Create a webhook subscription                    | `event_subscriptions:write` | compute     |
| `event_subscriptions.update` | Update a webhook subscription                    | `event_subscriptions:write` | compute     |
| `event_subscriptions.test`   | Send a test event and return the delivery result | `event_subscriptions:write` | compute     |
| `event_subscriptions.delete` | Delete a webhook subscription                    | `event_subscriptions:write` | destructive |

### notifications

| Tool                          | Summary                                   | Scope                 | Side-effect |
| ----------------------------- | ----------------------------------------- | --------------------- | ----------- |
| `notifications.list`          | List the caller's notifications           | `notifications:read`  | read        |
| `notifications.mark_read`     | Mark one notification as read             | `notifications:write` | compute     |
| `notifications.mark_all_read` | Mark all unread notifications as read     | `notifications:write` | compute     |
| `notifications.dismiss`       | Dismiss one of the caller's notifications | `notifications:write` | compute     |

### billing

| Tool                        | Summary                                             | Scope           | Side-effect    |
| --------------------------- | --------------------------------------------------- | --------------- | -------------- |
| `billing.approvals.request` | Request approval for a large credit purchase        | `billing:write` | compute        |
| `billing.approvals.reject`  | Reject a pending credit-purchase approval           | `billing:write` | compute        |
| `billing.set_auto_topup`    | Configure automatic top-up                          | `billing:write` | compute        |
| `billing.approvals.approve` | Approve a credit purchase and charge the saved card | `billing:write` | external_spend |
| `billing.topup`             | Top up credits                                      | `billing:write` | external_spend |

### payment_methods

| Tool                                  | Summary                                       | Scope           | Side-effect |
| ------------------------------------- | --------------------------------------------- | --------------- | ----------- |
| `payment_methods.create_setup_intent` | Start saving a card to the org (off-session)  | `billing:write` | compute     |
| `payment_methods.attach`              | Persist a saved card for the org              | `billing:write` | compute     |
| `payment_methods.set_default`         | Make a saved payment method the org's default | `billing:write` | compute     |
| `payment_methods.detach`              | Detach a saved payment method from the org    | `billing:write` | destructive |

### api_keys

| Tool              | Summary                                             | Scope            | Side-effect  |
| ----------------- | --------------------------------------------------- | ---------------- | ------------ |
| `api_keys.list`   | List the org's API keys (metadata only, no secrets) | `api_keys:read`  | read         |
| `api_keys.create` | Mint an organization API key (secret returned once) | `api_keys:write` | share_export |
| `api_keys.revoke` | Revoke an organization API key                      | `api_keys:write` | destructive  |

### organizations

| Tool                   | Summary                                               | Scope                 | Side-effect |
| ---------------------- | ----------------------------------------------------- | --------------------- | ----------- |
| `organizations.create` | Create an organization and enroll the caller as owner | `organizations:write` | compute     |

## Worked example: the `catalog.search` tool

`catalog.search` (scope `catalog:read`, side-effect `read`) searches a commercial-imagery host's STAC catalog and price-enriches the results. As an MCP tool it carries `readOnlyHint=true`, and its input schema is the operation's typed request body, `CatalogSearchInput`:

| Field          | Type     | Required | Notes                                                             |
| -------------- | -------- | -------- | ----------------------------------------------------------------- |
| `host_name`    | string   | yes      | the imagery host to search                                        |
| `bbox`         | number[] | no       | bounding box `[west, south, east, north]`                         |
| `intersects`   | object   | no       | GeoJSON geometry to intersect                                     |
| `collections`  | string[] | no       | restrict to specific collections                                  |
| `ids`          | string[] | no       | fetch specific item ids                                           |
| `datetime`     | string   | no       | RFC 3339 instant or interval                                      |
| `query`        | object   | no       | per-field filters; e.g. `cloudCoverage` maps to `{GT,GTE,LT,LTE}` |
| `limit`        | integer  | no       | default `100`, min `1`, max `500`                                 |
| `next`         | string   | no       | opaque pagination cursor                                          |
| `full_catalog` | boolean  | no       | search the host's full catalog                                    |

The tool also exposes an optional `x-api-key` header field, mapped from the operation's header parameter and forwarded with the request; in normal use your MCP client's bearer token is the identity, so you leave it unset. See [Pagination](/api-reference/pagination) for the `next` cursor contract and [Concepts](/api-reference/concepts) for the operation model.

A tool call carries arguments matching that schema:

```json
{
	"host_name": "umbra",
	"bbox": [-122.52, 37.7, -122.35, 37.83],
	"datetime": "2024-06-01T00:00:00Z/2024-09-01T00:00:00Z",
	"query": { "cloudCoverage": { "LTE": 10 } },
	"limit": 50
}
```

Because the tool maps directly to the operation, the same arguments are a valid HTTP request body. The equivalent direct call:

```bash
curl -X POST https://api.geopera.com/v1/op/catalog.search \
  -H "Authorization: Bearer gpra_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "host_name": "umbra",
    "bbox": [-122.52, 37.70, -122.35, 37.83],
    "datetime": "2024-06-01T00:00:00Z/2024-09-01T00:00:00Z",
    "query": { "cloudCoverage": { "LTE": 10 } },
    "limit": 50
  }'
```

Errors surface as RFC 9457 `application/problem+json` on the HTTP surface and propagate to the tool result — a missing scope, an invalid body, or a rate limit all carry a typed `type` and `detail`. See [Errors](/api-reference/errors) and [Rate limits](/api-reference/rate-limits).

## See also

- [Hosted MCP](/api-reference/sdks/mcp/hosted) — connecting an MCP client with OAuth or a forwarded token
- [Local quickstart](/api-reference/sdks/mcp/quickstart-local) — running the gateway over stdio with a static token
- [Operations](/api-reference/operations) — the full operation index behind every tool
- [Scopes](/api-reference/scopes) — what each `*:read` / `*:write` scope grants
