# Tools

Tools are the unit of capability on a Wire container. MCP and REST are two **transports**: same tools, different connection mechanisms. This page is the source of truth for what each tool does, its parameters, and its default visibility on each transport.

## Tool reference

| Tool | Purpose | MCP default | REST default | Cost |
|------|---------|:-:|:-:|:-:|
| [`wire_explore`](#wire_explore) | Browse the container: its shape, and the entries in it | ✅ | ✅ | 1 |
| [`wire_search`](#wire_search) | Fuzzy hybrid retrieval over raw content | ✅ | ✅ | 5 |
| [`wire_navigate`](#wire_navigate) | Traverse from an entry: relationships (filterable by type), siblings, source | ✅ | ✅ | 1 |
| [`wire_write`](#wire_write) | Save an entry to the container | ✅ | ✅ | 0 |
| [`wire_delete`](#wire_delete) | Remove an entry by ID | ✅ | ✅ | 0 |
| [`wire_status`](#wire_status) | Container status snapshot | ❌ | ✅ | 0 |
| [`wire_files_list`](#wire_files_list) | List uploaded files | ❌ | ✅ | 0 |
| [`wire_files_upload`](#wire_files_upload) | Upload a file | ❌ | ✅ | 0 |
| [`wire_files_delete`](#wire_files_delete) | Delete a file and its derived entries | ❌ | ✅ | 0 |
| [`wire_claim`](#wire_claim) | Convert an ephemeral container to a permanent one (ephemeral only) | ✅ | ✅ | 0 |
| [`wire_query`](#wire_query) | Read-only SQL over structured objects (opt-in, off by default) | ❌ | ❌ | 1 |

Every tool is independently toggleable per transport from the container's **Tools** page in the dashboard. The defaults above are what new containers ship with; users can flip any tool on or off for either transport. Inactive tools are hidden from `tools/list` on MCP and return `404 NOT_FOUND` on REST.

The five standard tools (`wire_explore`, `wire_search`, `wire_navigate`, `wire_write`, `wire_delete`) are on for both transports on every new container. `wire_query` is the one tool that ships off on **both** transports, so no agent sees it until someone turns it on. See [`wire_query`](#wire_query) for what it does and who can enable it.

The typical agent journey is **explore → search → navigate**: use `wire_explore` to discover what's in the container and fetch specific rows, use `wire_search` when you have a question and need fuzzy retrieval across raw content to find the right starting point, and use `wire_navigate` to move around from a content entry you've already landed on.

## `wire_explore`

Browse the container: what shape it has, and the entries in it. Two modes, `list` and `get`.

Use `wire_explore` to find your footing — what kinds of thing this container holds, and which entries exist. For fuzzy/semantic retrieval over raw content, use [`wire_search`](#wire_search); to move around from an entry you've already landed on, use [`wire_navigate`](#wire_navigate).

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mode` | string | No | `list` (default) or `get` |
| `id` | string | `get` only | Entry id |
| `tag` | string | No | `list` only: return only entries carrying this tag |
| `limit` | number | No | `list` only: how many entries to return (default 25, max 100) |
| `offset` | number | No | `list` only: pagination offset into `entries` |

### Modes

**`list`** (default): the container's shape. `entries` holds the rows you and your agents wrote — paginated by `limit`/`offset`, filterable by `tag`. Beside it, four groups describe everything else the container holds. Each appears only when the container has that kind, so their presence is itself the answer to "does this container have a knowledge graph / structured objects / uploaded files?"

- `entities[]` — the canonical entity rows background analysis built: `id`, `name`, `entityType`, and `mentions` (how many entries reference this entity).
- `schemas[]` — the field profile of each structured object, per source: `object`, `source`, `mode` (`declared` when the source declared its schema, `accreted` when Wire folded it from writes), and `fields[]` of `name` / `type`.
- `composites[]` — the shell entry for each uploaded file or chunked write: `compositeType`, `fileName`, `chunkCount`, `source`. Pass a composite's `id` to [`wire_navigate`](#wire_navigate) to reach its chunks.
- `entityTypes[]` — the entity type names in this container.

These groups describe the container, not a page of it: each is capped at 50 entries, with `hasMore` naming any group that was cut, and none of them is affected by `limit`, `offset` or `tag` — those page `entries`. `entryCount` is the total live rows in the container, all kinds included.

**`get`**: one entry by `id`, with its `relationships` (the same shape [`wire_navigate`](#wire_navigate) returns in `relationships` mode). When the entry is a `_composite` with `_compositeType: 'file'` — the system-created entry for every uploaded file — the response includes a `resource_link` pointing at the file's `wire://` URI. Agents resolve it via `resources/read` to get a signed download URL. See [File downloads (Resources)](/mcp/resources/).

:::note
`mode: "schema"` was removed. It returned a bare list of entity type names and went dark on containers whose entity graph is off, which `list` now covers and more. A call that still sends it is served as `list`.
:::

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "list", "limit": 25}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/explore
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "wire_explore", "arguments": { "mode": "list", "limit": 25 } }
}
```

### Return shape

**`list`**:

```json
{
  "entries": [{ "id": "…", "content": "…", "source": "agent:mcp", "tags": ["meeting"], "properties": {}, "createdAt": 1767225600000 }],
  "count": 25,
  "entryCount": 1432,
  "entities": [{ "id": "…", "name": "Acme Corp", "entityType": "Company", "mentions": 7 }],
  "schemas": [{ "id": "…", "object": "expenses", "source": "file:expenses.csv", "mode": "accreted", "fields": [{ "name": "amount", "type": "number" }] }],
  "composites": [{ "id": "…", "compositeType": "file", "fileName": "runbook.md", "chunkCount": 42, "source": "file:runbook.md" }],
  "entityTypes": ["Company", "Person"],
  "hasMore": { "entities": true }
}
```

- `count` is how many entries this page returned; `entryCount` is the container total.
- `entities` / `schemas` / `composites` / `entityTypes` / `hasMore` are omitted entirely when they'd be empty.

**`get`**:

- `entry`: the entry, with `content`, `source`, `tags`, `properties` and `createdAt`.
- `relationships`: `from`, `to`, and `byType` — see [`wire_navigate`](#wire_navigate).

## `wire_search`

Fuzzy hybrid retrieval over **raw content** in the container (file chunks, agent writes, and other unstructured entries). Always runs a hybrid pipeline: BM25 + vector embedding + HyDE + LLM rerank.

Use this when you have a question and need semantic search to find relevant passages. To see what the container holds, or to fetch one entry by id, use `wire_explore`.

Flat 5 credits per call.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | Free-text query |
| `limit` | number | No | Max results. Default 10. Clamped to 1–100. |
| `topK` | number | No | Alias for `limit`, same clamp. If you send both, `limit` wins. |

`limit` and `topK` are the same parameter under two names — `topK` is the original spelling and
keeps working. Values outside 1–100 are clamped rather than rejected, so `limit: 0` returns one
result and `limit: 5000` returns at most 100.

Each match includes `_meta.wire.navigate` affordance hints telling the agent what a `wire_navigate` call on this match would find. See the [return shape](#return-shape) below for the full structure.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "pricing concerns"}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/search
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "wire_search", "arguments": { "query": "pricing concerns" } }
}
```

### Return shape

Each match carries:

- `id`: the entry uuid. Pass this directly to `wire_navigate` or `wire_delete`.
- `score`: hybrid relevance score (post-rerank).
- `content`: the raw chunk or entry text.
- `provenance`: typed metadata object holding `source`, `sourceFileId`, `chunkIndex`, `totalChunks`, `ingestedAt`, `tags`, `fileName`, `sectionHeader`, `chunkSummary`, and more. Read the fields directly, no parsing needed.
- `_meta.wire.navigate` carries affordance hints for `wire_navigate`:
  - `hasSiblings`: count of adjacent chunks in the same source file. Zero means this chunk stands alone.
  - `relationshipTypes`: map of edge type to count, only non-zero entries. Example: `{ "elaborates": 3, "corroborates": 1 }`. Tells the agent upfront which `wire_navigate mode: "relationships" type: ...` calls would find something.
- `_meta.wire.duplicates`: present only when this result represents more than one instance of the same content (identical after whitespace normalization). Wire keeps every instance as a complete entry with its own timestamp, source, and metadata (repetition is signal, not noise), and collapses them to one representative at search time so results stay clean. The number is the total count of instances in the group, including this representative. Applies to any kind of repetition: agent retries, file re-ingests, recurring connector pulls, repeated log lines.
- `_meta.wire.firstSeenAt`: earliest `ingestedAt` across the instance group. Useful when a recurring source keeps re-ingesting the same content and you want to know how long it's been in the container.
- `_meta.wire.mostRecentAt`: latest `ingestedAt` across the group. The representative *is* the most recent instance, so its provenance answers "when did this last happen" directly.
- `_meta.wire.instanceId`: id of the group's instance record. Pass it to `wire_navigate` (`mode: "relationships"`) to enumerate every instance in the group, each with its own timestamp and source.
- `_meta.wire.versions`: up to 5 instances from the group with `source` and `ingestedAt`, ordered newest first. The representative result's own provenance gives the latest instance; this list lets agents see prior sources if they need to.

Use these hints to decide whether a `wire_navigate` call is worth making, to calibrate `range` or `limit` params, and to reason about whether content is fresh or recurring. A high `duplicates` count is itself information (how many times an alert fired, how often the same page was pulled) that browsing the group via `instanceId` turns into a timeline.

## `wire_navigate`

Traverse from an entry you've already landed on — typically a `wire_search` match. Move to adjacent chunks, to the entry's source document, or along relationship edges. Flat 1 credit per call.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entryId` | string | Yes | The entry to traverse from (e.g. a `wire_search` match id) |
| `mode` | string | No | `relationships` (default), `siblings`, or `source` |
| `range` | number | `siblings` only | Chunks before AND after the anchor (default 3, max 50) |
| `type` | string or string[] | `relationships` only | Return only these edge types. Omit for all edges. |

### Modes

**`relationships`** (default): follow the edges touching this entry. Both directions are kept apart — `from` holds edges where this entry is the source ("this entry `contradicts` X"), `to` holds edges where it's the target ("X `contradicts` this entry"). The direction matters: labels are written from the new entry's perspective at classification time, so those two are different statements.

The same edges come back a second time under `byType`, bucketed by label, so "what contradicts this?" is one call whether or not you filtered.

**`siblings`**: adjacent chunks from the same source document. Positional navigation — given a chunk, returns `range` chunks before and `range` chunks after it.

**`source`**: the source (composite) entry this one was chunked from. When that source is an uploaded file, the response also carries a `resource_link` pointing at the file's `wire://` URI; call `resources/read` on it to get the original bytes. See [File downloads (Resources)](/mcp/resources/).

### Filtering by edge type

`type` takes one label or a list of them:

| Family | Types |
|--------|-------|
| Provenance | `corroborates`, `elaborates`, `supersedes`, `contradicts` |
| Structural | `part_of` (chunk → its document), `instance_of` (entry → its duplicate group) |
| Entity graph | `mentions` (entry → a canonical entity), `related_via` (entity → entity) |
| CRM mirrors | `crm_assoc:{field}`, one per lookup field on the mirrored record |

A term ending in `:` matches a whole family by prefix — `type: "crm_assoc:"` returns every `crm_assoc:{field}` edge, which is the only way to ask for them without knowing the source system's field names. Nothing else is a wildcard: `contradicts` never matches a longer type.

An unrecognized type simply matches nothing. A `type` that isn't a string or an array of strings is an error rather than a silently unfiltered result.

`wire_search` hits carry `_meta.wire.navigate.relationshipTypes` — a map of edge type to count for that entry, computed from the same grouping — so an agent can see which filtered call would find something before making it.

### Example call

After a `wire_search` call returns a chunk id, fetch surrounding context:

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"entryId": "entry_abc123", "mode": "siblings", "range": 3}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/navigate
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "wire_navigate", "arguments": { "entryId": "entry_abc123", "mode": "relationships", "type": "contradicts" } }
}
```

### Return shape

**`relationships`**:

```json
{
  "relationships": {
    "from": [{ "fromId": "entry_abc123", "toId": "entry_def456", "type": "contradicts", "properties": {} }],
    "to": [],
    "byType": { "contradicts": { "from": [{ "fromId": "entry_abc123", "toId": "entry_def456", "type": "contradicts", "properties": {} }], "to": [] } }
  },
  "type": ["contradicts"]
}
```

`type` echoes the filter and is absent when you didn't send one. `byType` is always present; it's `{}` when the entry has no edges.

**`siblings`** returns `siblings[]`, and **`source`** returns `source` — entries in the same shape `wire_explore mode: "get"` returns.

## `wire_write`

Save an entry to the container.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string or object | Yes | The entry content. Strings are stored as text. Objects are stored as structured data. |
| `tags` | string[] | No | Tags for categorization and filtering |
| `metadata` | object | No | Arbitrary key-value metadata |
| `source` | string | No | Where this entry came from. Defaults to `"agent:mcp"` for MCP calls and `"webhook:<api-key-name>"` for REST calls. |
| `occurredAt` | string | No | When this entry happened, as an ISO 8601 date. Defaults to the write time and must not be in the future. Use it to backfill historical memory (imported notes, past conversations) so the entry's `ingestedAt` provenance reflects when the event happened, not when it was imported. |
| `object` | string | No | Group same-shaped records under an object name, e.g. `"expenses"`. See [Objects](/getting-started/concepts/#objects). |
| `fields` | object | No | The record's queryable values, e.g. `{"amount": 42.5, "vendor": "Acme"}`. Only meaningful with `object`, and optional when `content` is already a JSON object, in which case its keys are used. |

### Writing records

Naming an `object` turns a write into a **row** rather than a note: its values are stored so they can be filtered and aggregated, and the container keeps a field profile for the object describing what it has seen.

```json
{
  "content": "Lunch with the Acme team",
  "object": "expenses",
  "fields": { "amount": 42.5, "vendor": "Acme", "date": "2026-07-24" }
}
```

No setup is needed: the first write creates the object and later writes widen it. Reuse the same object name for every record of a kind; a new name per write is capped and makes the profile useless.

The response echoes what the entry was filed under:

| Field | Meaning |
|-------|---------|
| `object` | The object's canonical name. May differ in case from what you passed, because the first spelling an object gets is the one it keeps. |
| `unknownFields` | Present only when writing into an object whose schema comes from a connector, listing keys that schema doesn't define. The write still lands; this is a heads-up, not an error. |

Object names must start with a letter and contain only letters, digits, and underscores. `entries`, `relationships`, and `fields` are reserved.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Acme Corp wants to upgrade to Enterprise; needs SSO.", "tags": ["meeting", "acme-corp"]}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/write
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "wire_write",
    "arguments": { "content": "Acme Corp wants to upgrade to Enterprise; needs SSO.", "tags": ["meeting", "acme-corp"] }
  }
}
```

### Return shape

```json
{
  "entryId": "entry_abc123",
  "workflowId": "wf_abc123",
  "message": "Entry queued for storage. It will be searchable within a few seconds."
}
```

- `entryId`: the new entry's uuid. Pass to `wire_delete` or `wire_navigate` later.
- `workflowId`: id of the background write workflow (chunking, embedding, classification). It's internal; agents don't need to act on it.
- `message`: human-readable status string.

## `wire_delete`

Remove an entry by its ID.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entryId` | string | Yes | The ID of the entry to delete |
| `includeDuplicates` | boolean | No | Also delete every other instance of the same content (the whole group). Default `false`: only the specified entry. |

When content exists as multiple instances (see `_meta.wire.duplicates` on `wire_search`), each instance is independent: deleting one leaves the others untouched, and the group's counts update. `includeDuplicates: true` removes the entire group in one call, and the response adds `duplicatesDeleted` with the number of additional instances removed.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"entryId": "entry_abc123"}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/delete
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "wire_delete", "arguments": { "entryId": "entry_abc123" } }
}
```

### Return shape

```json
{
  "entryId": "entry_abc123",
  "workflowId": "wf_abc123"
}
```

- `entryId`: echoes the deleted id.
- `workflowId`: id of the background delete workflow that cleans up derived chunks, embeddings, and relationship edges. It's internal; agents don't need to act on it.

## `wire_claim`

Generate a claim link for an ephemeral container. Takes no parameters. Returns a URL that the user can open in a browser to sign up or log in and claim the container permanently.

Claim links are short-lived. Call `wire_claim` again to generate a new one if needed.

This tool only appears on ephemeral containers (created via [instant onboarding](/guides/instant-onboarding/)). Once a container is claimed and belongs to a permanent organization, `wire_claim` is no longer listed.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/claim
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "wire_claim", "arguments": {} }
}
```

### Return shape

```json
{
  "claim_url": "https://app.usewire.io/onboarding/create-account?claimToken=...",
  "expires_in": 3600
}
```

- `claim_url`: open in a browser to sign up or log in and claim the container.
- `expires_in`: seconds until the claim link expires. Call `wire_claim` again to get a fresh one.

## `wire_status`

Container status snapshot: initialization state, paused flag, counts (tools, entries, entity types), analysis cadence. Useful for readiness checks before kicking off work.

**Default visibility:** REST on, MCP off. Invoke over REST as `GET /container/:id/status`.

Takes no parameters.

### Example call

```bash title="REST"
curl -H "x-api-key: YOUR_API_KEY" \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/status
```

### Return shape

```json
{
  "containerId": "abc123",
  "containerName": "My Container",
  "is_ephemeral": false,
  "initialized": true,
  "initializationStatus": "complete",
  "paused": false,
  "ready": true,
  "counts": {
    "tools": 9,
    "composites": 28,
    "entityTypes": 17,
    "entries": { "total": 7381, "eligible": 7102, "analyzed": 7044, "pending": 58 }
  },
  "analysis": { "inProgress": false, "cadence": "automatic" }
}
```

- `ready`: `true` when the container is initialized, complete, and not paused. Use this as the gate before kicking off work.
- `initializationStatus`: one of `pending`, `analyzing`, `generating_tools`, `complete`.
- `counts.entries.pending`: eligible entries awaiting their first analysis pass. `0` means everything has been analyzed at least once.
- `analysis.cadence`: `automatic` or `manual` per the container's setting.

## `wire_files_list`

List files uploaded to this container with their processing status.

**Default visibility:** REST on, MCP off. Invoke over REST as `GET /container/:id/files`.

Takes no parameters.

### Example call

```bash title="REST"
curl -H "x-api-key: YOUR_API_KEY" \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/files
```

### Return shape

```json
[
  {
    "id": "file_abc123",
    "name": "document.pdf",
    "size": 245760,
    "mimeType": "application/pdf",
    "uploadedAt": "2026-03-30T12:00:00Z",
    "processingStatus": "complete",
    "chunkCount": 42
  }
]
```

Array of file records. Pass `id` to `wire_files_delete` or to the `wire://` URI scheme via [MCP resources](/mcp/resources/) to download the original bytes.

`processingStatus` is `pending` while the file is queued, `processing` during extraction, `complete` once its entries exist, `skipped` for a download-only file type, and `failed` if extraction gave up. `chunkCount` is `null` until processing finishes.

Over REST this array arrives as the `data` field of the standard envelope: `{"success": true, "data": [ ... ]}`. See the [REST API reference](/mcp/rest-api/).

## `wire_files_upload`

Upload a file to this container. Wire processes it automatically into entries (chunks, extracted entities, embeddings) after upload.

**Default visibility:** REST on, MCP off. REST is the path built for real files: invoke as `POST /container/:id/files` with a multipart body containing a `file` field, and it carries up to **25 MB** per file. An owner can also turn the tool on for MCP, where an agent passes the file's text or bytes as ordinary tool arguments instead. That path is capped at 1 MB, since tool arguments are not a file transfer channel, so anything larger has to go over REST. Files between 25 MB and 50 MB (the platform storage cap) upload from the dashboard. See [Supported File Types](/reference/file-types/) for accepted formats.

The request must carry a `Content-Length` header — the endpoint refuses a body whose size it cannot check up front with `411 LENGTH_REQUIRED`. `curl -F`, `fetch` with a `FormData` body, and every mainstream HTTP client set it for you.

**Over REST:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `file` | multipart file | Yes | The file to upload. Filename + content type are read from the part headers. |

**Over MCP** (once enabled):

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | File name including extension. The extension drives how the file is processed. |
| `content` | string | One of | UTF-8 text content |
| `contentBase64` | string | One of | Base64-encoded bytes, for anything not plain text |
| `mimeType` | string | No | Content type, if you want to state it explicitly |

Pass either `content` or `contentBase64`, not both, and keep the decoded size under 1 MB.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@customers.csv" \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/files
```

### Return shape

```json
{
  "id": "file_abc123",
  "name": "customers.csv",
  "size": 245760,
  "uploadedAt": "2026-03-30T12:00:00Z",
  "processingStatus": "pending"
}
```

- `id`: the new file's id. Pass to `wire_files_delete` or to `wire://` resource URIs.
- `processingStatus`: `pending` immediately after upload, or `skipped` when the file type is stored for download only. Background workflows extract entries; check `wire_status` or `wire_files_list` to watch progress.

Over REST the response is `201 Created` and this object arrives as the `data` field of the standard envelope. The content type Wire records is the one it detects from the file's own bytes, not the one the multipart part declares.

Called over MCP, the tool returns the new file's id, its name, and a status of either `processing` or `stored`. `stored` means the file type is download-only and no entries will be created from it.

## `wire_files_delete`

Delete a file and all entries derived from it.

**Default visibility:** REST on, MCP off. Invoke over REST as `DELETE /container/:id/files/:fileId`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fileId` | string | Yes | The file ID returned by `wire_files_list`. |

### Example call

```bash title="REST"
curl -X DELETE \
  -H "x-api-key: YOUR_API_KEY" \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/files/file_abc123
```

### Return shape

```json
{
  "message": "File deleted",
  "fileId": "file_abc123",
  "entriesDeleted": 42
}
```

`entriesDeleted` is how many entries derived from the file were removed with it. Deleting a file that isn't there returns `404 NOT_FOUND`, and a file cannot be deleted while it is still being processed.

## `wire_query`

Read-only SQL over the container's structured [objects](/getting-started/concepts/#objects), for the answers retrieval cannot give: sums, averages, counts, grouping, exact filters across many records at once.

:::caution[Opt-in, and off by default]
`wire_query` is an **additional** tool, not one of the five standard tools. New containers ship it off on **both** MCP and REST, so agents cannot see or call it until an organization owner or admin turns it on for that container from the **Tools** page. Until then it is absent from MCP `tools/list` and its REST endpoint returns `404 NOT_FOUND`.

Two reasons for the default: the query surface is empty on a container that holds no objects, and switching it on is an account-level decision rather than something every container should carry.
:::

The tool reaches objects only. Raw content (file chunks, prose entries), the knowledge graph, and container internals are not queryable here; those stay behind `wire_explore`, `wire_search`, and `wire_navigate`.

Flat 1 credit per call.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `sql` | string | Yes | A single read-only SQL statement (`SELECT`, or `WITH … SELECT`) |

### What you can query

Two kinds of table exist, and nothing else is reachable.

**`fields`** is the catalog. One row per object, source, and field, so an agent can learn the shape of the container before writing a real query. Start every session here:

```sql
SELECT * FROM fields
```

| Column | Meaning |
|--------|---------|
| `object` | Object name, which is also the table name |
| `source` | Where the records came from (a file, a connector, an agent write) |
| `name` | Field name, which is also the column name |
| `type` | `integer`, `number`, `boolean`, `date`, `text`, `json`, or `mixed` when values disagree |
| `present` | How many of the profiled records actually carry this field |
| `seen` | How many records were profiled for this object and source |

No rows back means the container has no objects yet, and there is nothing to query. `present` versus `seen` is worth reading before filtering: a field that appears in 4 of 12 records will silently drop rows from a `WHERE` clause.

**One table per object.** Its columns are that object's profiled field names, plus two the container always adds:

- `_entry_id`: the underlying entry id, so a row in an aggregate can be traced back with `wire_navigate`.
- `_source`: the source the record came from.

They carry the underscore prefix because real source data collides: a CSV routinely has its own `id` and `source` columns. The same object name written under two sources becomes one table whose columns are the union of both profiles, with `_source` telling the rows apart; a column missing from one of the profiles reads `NULL` for those rows.

### Rules

- **One statement per call.** A second statement, or one hidden after a comment, is refused.
- **Reads only.** The statement must begin with `SELECT` or `WITH`. Anything that writes, changes schema, or reaches outside the container is refused, including `PRAGMA` and its table-valued forms.
- **Only declared tables.** `FROM` and `JOIN` may name the catalog, an object table, or a CTE the statement defines itself. Any other name comes back as an error telling the agent to run `SELECT * FROM fields`.
- **`json_each` and `json_tree`** are the only table-valued functions callable. They expand JSON the statement already has.
- **Results are capped** at 200 rows or roughly 256 KB of serialized rows, whichever comes first. Aggregate in SQL rather than paging raw rows.

A refused or invalid statement returns the reason as an error, including the database's own message for things like an unknown column, which is usually enough for an agent to correct itself and retry.

### Example call

```bash title="REST"
curl -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT vendor, sum(amount) AS total FROM expenses GROUP BY vendor ORDER BY total DESC"}' \
  https://YOUR_ORG_SLUG.api.usewire.io/container/YOUR_CONTAINER_ID/tools/query
```

```json title="MCP (tools/call)"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "wire_query",
    "arguments": { "sql": "SELECT vendor, sum(amount) AS total FROM expenses GROUP BY vendor ORDER BY total DESC" }
  }
}
```

### Return shape

```json
{
  "columns": ["vendor", "total"],
  "rows": [["Acme", 412.5], ["Globex", 96]],
  "rowCount": 2,
  "truncated": false
}
```

- `columns`: column names in result order, taken from the statement's own projection.
- `rows`: one array per row, values positioned to match `columns`. An absent value is `null`.
- `rowCount`: number of rows in `rows`.
- `truncated`: `true` when the row cap or the size cap cut the result short. Rewrite the query to aggregate rather than asking for the next page.

### Choosing between explore, search, and query

- **`wire_explore`** for classified canonical entries: what entity types exist, list them, get one by id, filter on a field, keyword-search within a type.
- **`wire_search`** when the question is in natural language and the answer is somewhere in raw content.
- **`wire_query`** when the answer is a number or a breakdown over records: totals, averages, counts, group-by, or a precise predicate applied across everything at once. `wire_explore`'s `filter` mode returns matching rows; `wire_query` computes over them.

## Activating and deactivating tools

Every tool can be toggled independently per transport from the container's **Tools** page in the dashboard. Three controls govern visibility:

- **Active / Inactive**: global kill switch. Inactive tools are hidden everywhere.
- **MCP pill**: when active, controls whether the tool appears in MCP `tools/list` and is callable via `tools/call`.
- **REST pill**: when active, controls whether the underlying REST endpoint serves requests. A disabled REST tool returns `404 NOT_FOUND`.

The defaults in the [tool reference table](#tool-reference) at the top of this page are what new containers ship with. User toggles in the dashboard always win over the defaults and are preserved across container wakes.

Toggling a tool is an admin-level action. It takes an organization owner or admin who also has admin permission on the container itself (the creator has it automatically, otherwise it comes from a grant). Organization members cannot toggle tools. That is also the gate on switching [`wire_query`](#wire_query) on. See [Roles & Permissions](/reference/roles/).

## Public container visibility

A [public container](/guides/public-containers/) is readable by any signed-in Wire user. Connecting still takes a sign-in, which the MCP client prompts for, and Wire then grants the visitor read-only access.

A public visitor sees the read tools: `wire_explore`, `wire_search`, and `wire_navigate`, plus `wire_query` and `wire_status` where the owner has switched them on. Write and claim tools (`wire_write`, `wire_delete`, `wire_claim`) are hidden from them. Members of the owning organization, and anyone holding a grant on the container, see the full set their permission allows.

`wire_query` is read-only, so it follows the same rule. Leave it off if the container is public and its object data is not meant to be aggregated by anyone who connects.

## Next Steps

- [Core Concepts](/getting-started/concepts/) - Learn about entries and containers
- [MCP Overview](/mcp/overview/) - How to connect and use tools
- [REST API](/mcp/rest-api/) - Transport details for invoking tools over HTTP