# MCP Tool Reference

Every `crumbs_*` MCP tool: what it reads, its input schema, its output shape and the `ext.crumbs.*` extension it calls in the running app.

Every tool dispatches through the VM Service, so the app must be running under dowel: start it with `./bin/dowel start` or the `dowel_start` tool, and `dowel_status` shows the recorded session.

- [Quick Reference](#quick-reference)
- [Common conventions](#common-conventions)
- [crumbs_tail](#crumbs_tail)
- [crumbs_requests](#crumbs_requests)
- [crumbs_exceptions](#crumbs_exceptions)
- [crumbs_events](#crumbs_events)
- [crumbs_gates](#crumbs_gates)
- [crumbs_dumps](#crumbs_dumps)
- [crumbs_queries](#crumbs_queries)
- [crumbs_caches](#crumbs_caches)
- [crumbs_frames](#crumbs_frames)
- [crumbs_clear](#crumbs_clear)
- [Related Documentation](#related-documentation)

## Quick Reference

| Tool | Reads | Parameters | Answer key |
|:-----|:------|:-----------|:-----------|
| `crumbs_tail` | `package:logging` records | `limit`, `level` | `messages` |
| `crumbs_requests` | HTTP requests | `limit` | `records` |
| `crumbs_exceptions` | Uncaught exceptions | `limit` | `exceptions` |
| `crumbs_events` | In-app events | `limit` | `events` |
| `crumbs_gates` | Gate checks | `limit` | `gates` |
| `crumbs_dumps` | `debugPrint` output | `limit` | `dumps` |
| `crumbs_queries` | DB queries | `limit` | `queries` |
| `crumbs_caches` | Cache operations | `limit` | `caches` |
| `crumbs_frames` | Per-frame performance | `limit` | `frames` |
| `crumbs_clear` | Empties every buffer | none | `cleared` |

## Common conventions

Every reading tool (all but `crumbs_clear`) follows these rules:

- **Oldest first.** Records come back in chronological order: the last entry is the newest.
- **`limit`.** Optional; keeps the newest N records. Omitted, the whole buffer comes back, up to its capacity (500 records by default, 3600 for frames; see [Buffers](https://anilcancakir.com/crumbs/docs/getting-started/buffers.md)). A zero or negative `limit` returns nothing.
- **Envelope.** Every tool answers a JSON object whose list sits under the answer key in the table above. An empty buffer, on a fresh start or after `crumbs_clear`, answers an empty list, not an error.
- **Redaction.** Log, event, exception and HTTP records are masked at insert when the host registered `CrumbsRedaction.redactor` (see [Redaction](https://anilcancakir.com/crumbs/docs/getting-started/redaction.md)), and each carries a `redacted` boolean.
- **Errors.** When the app is not running or the VM Service cannot be reached, the tool result is an error with a plain-text message that says what to do.

## crumbs_tail

Return recent log records from the running Flutter app.

Reads the Crumbs log ring buffer populated by every `package:logging` `Logger` call in the app.
Use this to inspect what the app logged without scraping `flutter run` stdout via `dowel_logs`.

**VM Service extension:** `ext.crumbs.console`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |
| `level` | string | no | Minimum log level to include. Common values: `FINE`, `INFO`, `WARNING`, `SEVERE`, `SHOUT`. Omit for all levels. A name that is not a level matches nothing. |

The extension behind this tool also accepts `since` and `logger` and answers a `cursor`; the tool
descriptor does not declare them. See [Cursor and filters](https://anilcancakir.com/crumbs/docs/mcp/cursor-and-filters.md).

### Output Shape

```json
{
  "messages": [
    {
      "level": "WARNING",
      "levelValue": 900,
      "message": "Monitor check timed out after 30s",
      "loggerName": "MonitorController",
      "time": "2026-05-20T14:32:10.123",
      "atUs": 81234567,
      "redacted": false,
      "error": "TimeoutException: ...",
      "stackTrace": "#0 MonitorController.check ..."
    }
  ],
  "cursor": 81234567
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `level` | string | yes | Log level name (e.g. `INFO`, `WARNING`, `SEVERE`) |
| `levelValue` | integer | yes | Numeric level value from `package:logging` |
| `message` | string | yes | Log message string |
| `loggerName` | string | yes | Name of the `Logger` instance that emitted the record |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; join on `atUs` |
| `atUs` | integer | yes | Monotonic microseconds from `FlutterTimeline.now`, the same clock as event records |
| `redacted` | boolean | yes | True when `CrumbsRedaction.redactor` ran over the record before it was buffered |
| `error` | string | no | Error object stringified, when one was attached |
| `stackTrace` | string | no | Stack trace stringified, when one was attached |
| `cursor` | integer or null | yes (envelope) | Largest `atUs` returned, else the `since` given, else null |

### Example Invocations

```text
# Last 10 log lines at any level
crumbs_tail {"limit": 10}

# Last 20 entries at WARNING or above
crumbs_tail {"limit": 20, "level": "WARNING"}

# Every SEVERE entry in the buffer
crumbs_tail {"level": "SEVERE"}
```

### Related Tools

- `crumbs_exceptions`: uncaught exceptions (not expected `try/catch` flows)
- `crumbs_requests`: HTTP traffic
- `crumbs_clear`: wipe the buffer before a repro

---

## crumbs_requests

Return recent HTTP request records from the running Flutter app.

Reads the HTTP buffer, filled by any `CrumbsHttpAdapter` the app registers (`MagicHttpFacadeAdapter`
from `magic_devtools` on a Magic app) or by an interceptor that calls `DioHttpAdapter.recordRequest()`.
Use this to debug API issues without a proxy or the network panel of DevTools.

**VM Service extension:** `ext.crumbs.requests`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of HTTP records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer. |

### Output Shape

```json
{
  "records": [
    {
      "url": "https://api.example.com/monitors",
      "method": "GET",
      "statusCode": 200,
      "durationMs": 142,
      "isError": false,
      "timestamp": "2026-05-20T14:32:11.456",
      "requestHeaders": {
        "Authorization": "********"
      },
      "responseBody": "{\"data\": []}",
      "atUs": 1234567,
      "redacted": false
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `url` | string | yes | Full request URL |
| `method` | string | yes | HTTP method (`GET`, `POST`, etc.) |
| `statusCode` | integer | yes | HTTP status code; 0 when the call failed before a response |
| `durationMs` | integer | yes | Round-trip time in milliseconds |
| `isError` | boolean | yes | True when the adapter classified the call as failed |
| `timestamp` | string | yes | ISO 8601 device-local wall clock of when the request was recorded, no offset; join on `atUs` |
| `requestHeaders` | object | no | Request headers as a `Map<String, String>`; credential headers read `********` (see Notes) |
| `requestBody` | string | no | Request body as a string, when present and readable; credential keys of a JSON body read `********` |
| `responseBody` | string | no | Response body snippet, when present; credential keys of a JSON body read `********` |
| `attributedHeuristically` | boolean | no | True when the adapter used best-effort FIFO attribution for concurrent requests |
| `requestId` | string | no | Adapter-assigned identifier pairing this response with its request exactly, when the adapter tracks one |
| `startUs` | integer | no | Monotonic microsecond timestamp (`FlutterTimeline.now` clock) when the request was sent, when tracked |
| `endUs` | integer | no | Monotonic microsecond timestamp (`FlutterTimeline.now` clock) when the response arrived, when tracked |
| `atUs` | integer | yes | Monotonic microsecond timestamp from `FlutterTimeline.now`, captured at record construction |
| `interactionId` | string | no | Correlates this record to others captured during the same logical interaction |
| `linkedBy` | string | no | How `interactionId` was derived: `zone` \| `frame` \| `window` |
| `redacted` | boolean | yes | True when `CrumbsRedaction.redactor` ran over the record before it was buffered |

### Example Invocations

```text
# Last 5 HTTP requests
crumbs_requests {"limit": 5}

# Every request in the buffer
crumbs_requests {}
```

### Notes

- Only HTTP calls that go through an installed Crumbs adapter are recorded. Raw
  `dart:io HttpClient` calls are invisible to this tool.
- Pair with `crumbs_clear` before a user action to isolate exactly the traffic that action
  produces.
- Credentials are masked before a record is buffered, the way Laravel Telescope does it: the
  value of a hidden header or a hidden JSON body key reads `********`, which is crumbs's mask
  and not what the app sent. See [Credential redaction](https://anilcancakir.com/crumbs/docs/watchers/index.md#credential-redaction)
  for the default lists and how to extend them.

---

## crumbs_exceptions

Return recent uncaught exception records from the running Flutter app.

Reads the exception buffer, filled by `ExceptionWatcher` through `FlutterError.onError` and
`PlatformDispatcher.onError`. `CrumbsPlugin.install()` does not register it: `crumbs:install` does,
and a hand wiring calls `CrumbsPlugin.registerWatcher(ExceptionWatcher())`. Use this when a crash or
unhandled exception was thrown and you need the stack without scraping `flutter run` output.

**VM Service extension:** `ext.crumbs.exceptions`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of exception records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer. |

### Output Shape

```json
{
  "exceptions": [
    {
      "exceptionType": "StateError",
      "message": "Bad state: Stream has already been listened to",
      "time": "2026-05-20T14:33:00.789",
      "stackTrace": "#0 _StreamController._subscribe ...",
      "isolate": "main",
      "redacted": false
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `exceptionType` | string | yes | Runtime type name of the exception object |
| `message` | string | yes | Exception message string |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; this record has no `atUs` |
| `stackTrace` | string | no | Full stack trace, when available |
| `isolate` | string | no | Isolate name where the exception was caught |
| `redacted` | boolean | yes | True when `CrumbsRedaction.redactor` ran over the record before it was buffered |

### Example Invocations

```text
# Last 3 uncaught exceptions
crumbs_exceptions {"limit": 3}

# Every exception in the buffer
crumbs_exceptions {}
```

### Notes

- Only **uncaught** errors reach `FlutterError.onError` or `PlatformDispatcher.onError`. Expected
  `try/catch` flows do not surface here.
- The watcher chains to the handlers that were set before it (Sentry, Bugsnag), so both keep working.
- Pair with `crumbs_tail` to see what the app logged in the moments before the crash.

---

## crumbs_events

Return recent in-app event records from the running Flutter app.

Reads the event buffer. On a Magic app `MagicEventWatcher` fills it from a curated set of Magic auth,
database and gate-definition events; any app can add its own through `CrumbsStore.recordEvent`. Use this
to trace event-driven side effects without adding `print` statements.

**VM Service extension:** `ext.crumbs.events`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of event records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |

The extension behind this tool also accepts `since` and `type` and answers a `cursor`; the tool
descriptor does not declare them. See [Cursor and filters](https://anilcancakir.com/crumbs/docs/mcp/cursor-and-filters.md).

### Output Shape

```json
{
  "events": [
    {
      "eventType": "MonitorChecked",
      "payload": {
        "monitorId": "abc-123",
        "status": "up"
      },
      "time": "2026-05-20T14:34:00.001",
      "listenerCount": 3,
      "atUs": 81234567,
      "redacted": false
    }
  ],
  "cursor": 81234567
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `eventType` | string | yes | Runtime class name of the dispatched event |
| `payload` | object | yes | JSON snapshot of the event payload. A value `jsonEncode` cannot write is stored as its `toString()` |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; join on `atUs` |
| `listenerCount` | integer | no | Number of listeners notified at dispatch time, when available |
| `atUs` | integer | yes | Monotonic microseconds from `FlutterTimeline.now` |
| `redacted` | boolean | yes | True when `CrumbsRedaction.redactor` ran over the record before it was buffered |
| `cursor` | integer or null | yes (envelope) | Largest `atUs` returned, else the `since` given, else null |

### Example Invocations

```text
# Last 10 events
crumbs_events {"limit": 10}

# Every event in the buffer
crumbs_events {}
```

### Notes

- Only events that reach the buffer are visible: a Magic event outside the watcher's set, or a raw
  `ChangeNotifier.notifyListeners` call, does not surface here.
- `MagicEventWatcher` records an empty `payload` for every Magic event; per-event fields are not
  extracted yet.
- For gate checks use `crumbs_gates`; for HTTP traffic use `crumbs_requests`.

---

## crumbs_gates

Return recent gate authorisation check records from the running Flutter app.

Reads the gate buffer, filled by `MagicGateWatcher` on every `Gate.allows` and `Gate.denies` call
through the Magic `Gate` facade. Use this to debug authorisation issues (why a button is hidden, why a
route is blocked) without modifying policy classes.

**VM Service extension:** `ext.crumbs.gates`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of gate check records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |

### Output Shape

```json
{
  "gates": [
    {
      "ability": "monitors.destroy",
      "result": false,
      "arguments": ["Monitor"],
      "time": "2026-05-20T14:35:00.010",
      "userId": "user-42"
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `ability` | string | yes | Ability name passed to `Gate.allows` / `Gate.denies` |
| `result` | boolean | yes | `true` when the gate allowed the action; `false` when denied |
| `arguments` | array | yes | List of argument values (class name strings or serialised primitives) |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; this record has no `atUs` |
| `userId` | string | no | Authenticated user ID at check time, when available |

### Example Invocations

```text
# Last 20 gate checks
crumbs_gates {"limit": 20}

# Every gate check in the buffer
crumbs_gates {}
```

### Notes

- Only checks routed through the Magic `Gate` facade are captured. Direct policy class calls
  are not recorded.
- Pair with `crumbs_clear` before walking a guarded flow to isolate just the relevant checks.

---

## crumbs_dumps

Return recent `debugPrint` output records from the running Flutter app.

Reads the dump buffer, filled by `DumpWatcher`, which replaces the global `debugPrint` callback and
chains to the previous one. Use this to read `debugPrint()` output from within the running app without
scraping `flutter run` output.

**VM Service extension:** `ext.crumbs.dumps`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of dump records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |

### Output Shape

```json
{
  "dumps": [
    {
      "message": "[MonitorController] reloading monitors...",
      "time": "2026-05-20T14:36:00.200",
      "wrapWidth": 80
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `message` | string | yes | Full message string passed to `debugPrint` |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; this record has no `atUs` |
| `wrapWidth` | integer | no | Wrap width passed to the original `debugPrint` call, when provided |

### Example Invocations

```text
# Last 10 debugPrint calls
crumbs_dumps {"limit": 10}

# Every dump record in the buffer
crumbs_dumps {}
```

### Notes

- Only output routed through `debugPrint` is captured. `print()` and `stdout.write` calls are
  invisible to this tool.
- `DumpWatcher` installs nothing outside a debug build unless `allowInRelease` is set, so a profile
  build under `!kReleaseMode` records no dumps.
- For structured logs use `crumbs_tail`; for crashes use `crumbs_exceptions`.

---

## crumbs_queries

Return recent database query records from the running Flutter app.

Reads the query buffer, filled by `MagicQueryWatcher` from the `QueryExecuted` event the Magic query
builder dispatches after every SQL run. Use this to inspect what queries the app ran without attaching
a separate SQL profiler.

**VM Service extension:** `ext.crumbs.queries`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of query records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |

### Output Shape

```json
{
  "queries": [
    {
      "sql": "SELECT * FROM monitors WHERE team_id = ? LIMIT 50",
      "bindings": ["team-abc"],
      "timeMs": 4,
      "connectionName": "default",
      "time": "2026-05-20T14:37:00.100",
      "atUs": 1234567
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `sql` | string | yes | SQL string the query builder dispatched to the driver |
| `bindings` | array | yes | Positional or named query bindings |
| `timeMs` | integer | yes | Execution time in milliseconds reported by the query builder |
| `connectionName` | string | yes | Connection name; `"default"` when the app did not name it |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; join on `atUs` |
| `atUs` | integer | yes | Monotonic microsecond timestamp from `FlutterTimeline.now` |
| `interactionId` | string | no | Correlates this record to others captured during the same interaction |
| `linkedBy` | string | no | How `interactionId` was derived: `zone` \| `frame` \| `window` |

### Example Invocations

```text
# Last 5 queries
crumbs_queries {"limit": 5}

# Every query in the buffer
crumbs_queries {}
```

### Notes

- Only queries that go through magic's QueryBuilder (dispatching `QueryExecuted` via
  `EventDispatcher`) are recorded. Raw `sqlite3` or direct Dio SQL calls bypass this tool.
- For Magic Cache traffic use `crumbs_caches`; for HTTP use `crumbs_requests`; for log
  lines use `crumbs_tail`.

---

## crumbs_caches

Return recent Magic cache operation records from the running Flutter app.

Reads the cache buffer, filled by `MagicCacheWatcher` from Magic's `CacheHit`, `CacheMiss`,
`CachePut`, `CacheForget` and `CacheFlush` events. Use this to inspect cache traffic without
instrumenting the app.

**VM Service extension:** `ext.crumbs.caches`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of cache records to return: the most recent N, in chronological order (oldest of that window first, newest last). Omit for the whole buffer (cap enforced by ring-buffer size, 500). |

### Output Shape

```json
{
  "caches": [
    {
      "operation": "put",
      "key": "monitors.team-abc",
      "time": "2026-05-20T14:38:00.300",
      "ttlMs": 300000,
      "atUs": 1234567
    }
  ]
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `operation` | string | yes | One of: `hit`, `miss`, `put`, `forget`, `flush` |
| `key` | string | yes | Cache key string (`*` for a flush) |
| `time` | string | yes | ISO 8601 device-local wall clock, no offset; join on `atUs` |
| `ttlMs` | integer | no | TTL in milliseconds, when the operation carried one |
| `atUs` | integer | yes | Monotonic microsecond timestamp from `FlutterTimeline.now` |
| `interactionId` | string | no | Correlates this record to others captured during the same interaction |
| `linkedBy` | string | no | How `interactionId` was derived: `zone` \| `frame` \| `window` |

### Example Invocations

```text
# Last 20 cache operations
crumbs_caches {"limit": 20}

# Every cache operation in the buffer
crumbs_caches {}
```

### Notes

- Only Magic `Cache` facade calls dispatch the watcher events. Raw driver-level cache calls
  bypass this tool.
- For DB queries use `crumbs_queries`; for HTTP use `crumbs_requests`.

---

## crumbs_frames

Return recent per-frame performance records from the running Flutter app.

Reads the frame buffer, filled by `FramePerfWatcher`, which joins
`SchedulerBinding` frame timings with a per-frame `FlutterTimeline` block-attribution drain. Each
record carries frame number, build/raster/vsync-overhead/total-span durations in microseconds, a
timestamp, and a `blocks` map of named spans to their aggregated duration and call count. Use this
to attribute jank to a specific widget or layout phase after driving an interaction.

**VM Service extension:** `ext.crumbs.frames`

### Input Schema

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Maximum number of frame records to return (oldest-first; last entry is newest). Omit for the whole buffer (cap enforced by the frame-perf buffer size, default 3600). |

### Output Shape

```json
{
  "frames": [
    {
      "frameNumber": 128,
      "buildMicros": 1400,
      "rasterMicros": 2100,
      "vsyncOverheadMicros": 300,
      "totalSpanMicros": 3800,
      "time": "2026-05-20T14:38:00.300",
      "blocks": {
        "Widget.build": { "micros": 600, "selfMicros": 400, "count": 5 }
      },
      "vsyncStartUs": 998200,
      "atUs": 1234567
    }
  ],
  "livenessCounter": 128
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `frames` | array | yes | The frame records, may be empty |
| `frames[].frameNumber` | integer | yes | The engine's frame number |
| `frames[].buildMicros` | integer | yes | Build phase duration in microseconds |
| `frames[].rasterMicros` | integer | yes | Raster phase duration in microseconds |
| `frames[].vsyncOverheadMicros` | integer | yes | Vsync overhead in microseconds |
| `frames[].totalSpanMicros` | integer | yes | Total frame span in microseconds |
| `frames[].time` | string | yes | ISO 8601 device-local wall clock, no offset; join on `atUs` |
| `frames[].blocks` | object | yes | Named span to `{micros, selfMicros, count}`; empty when no measurement session is collecting. `micros` is inclusive of nested children, `selfMicros` is exclusive (`micros` minus directly nested children's combined duration) |
| `frames[].vsyncStartUs` | integer | no | Vsync signal timestamp from `FrameTiming.timestampInMicroseconds(FramePhase.vsyncStart)`. Clock parity with `atUs` is NOT proven; treat a comparison as needing a live-engine run, not a unit-test guarantee |
| `frames[].atUs` | integer | yes | Monotonic microsecond timestamp from `FlutterTimeline.now`, captured at record construction |
| `frames[].interactionId` | string | no | Correlates this record to others captured during the same logical interaction |
| `frames[].linkedBy` | string | no | How `interactionId` was derived: `zone` \| `frame` \| `window` |
| `livenessCounter` | integer | yes | Monotonic count of frames actually drawn since `FramePerfWatcher` was installed; present even when `frames` is empty, so a caller can tell a quiet app from a stalled engine |

### Example Invocations

```text
# Last 100 frames
crumbs_frames {"limit": 100}

# Every frame in the buffer
crumbs_frames {}
```

### Notes

- `FramePerfWatcher` is opt-in and not auto-installed. An empty `frames` list with a
  non-advancing `livenessCounter` means it is not registered, not that the app is idle.
- `blocks` is only populated while a measurement session has `FlutterTimeline` collection
  enabled; otherwise records carry frame magnitude with an empty `blocks` map.

---

## crumbs_clear

Clear every crumbs ring buffer.

Empties every buffer in one call, so the next read of any `crumbs_*` tool returns only records
produced **after** this clear. Use it as a starting point before reproducing a bug or capturing the
output of one user action.

**VM Service extension:** `ext.crumbs.clear`

### Input Schema

No parameters.

### Output Shape

```json
{
  "cleared": true
}
```

| Field | Type | Always present | Description |
|---|---|---|---|
| `cleared` | boolean | yes | Always `true` on success |

### Example Invocations

```text
# Clear, perform the action, then read
crumbs_clear {}
# the user action runs
crumbs_requests {"limit": 20}
crumbs_tail {"limit": 50}
```

### Notes

- Idempotent: safe to call when the buffers are already empty.
- Does not touch the live `package:logging` stream or any other sink (Sentry, Bugsnag); only the
  buffers crumbs keeps.
- `ext.crumbs.pause` and `ext.crumbs.resume`, which stop and restart recording, have no MCP tool.

---

## Related Documentation

- [Cursor and filters](https://anilcancakir.com/crumbs/docs/mcp/cursor-and-filters.md): `since`, `type`, `logger` and `cursor` on the console and
  events extensions.
- [MCP overview](https://anilcancakir.com/crumbs/docs/mcp/overview.md): how the tools reach the running app through `CrumbsDowelProvider`.
- [MCP setup](https://anilcancakir.com/crumbs/docs/mcp/setup.md): write the `.mcp.json` entry and connect a client.
- [dowel MCP tool reference](https://anilcancakir.com/dowel/docs/mcp/tool-reference): the `dowel_*`
  substrate tools (`dowel_start`, `dowel_stop`, `dowel_status`) and filter configuration.
