@remits/remits-cli 0.1.112 → 0.1.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,962 @@
1
+ # remits-cli Tool Reference
2
+
3
+ > A `remits-cli` skill reference. **Load this when** you are about to call any `mcp_*` tool. Read the entry for that tool before you build its input.
4
+ >
5
+ > The table of contents below carries **real line numbers** (`- L84 Some Heading`), resolved when
6
+ > this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
7
+ > only those. The entry text is the heading verbatim, so it also greps.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Tool Reference](#tool-reference)
12
+ - [Execute a Tool](#execute-a-tool)
13
+ - [`mcp_account_view`](#mcp_account_view)
14
+ - [`mcp_account_user_admin`](#mcp_account_user_admin)
15
+ - [`mcp_firestore_search`](#mcp_firestore_search)
16
+ - [`mcp_firestore_patch`](#mcp_firestore_patch)
17
+ - [`mcp_object_activity`](#mcp_object_activity)
18
+ - [`mcp_record_listing`](#mcp_record_listing)
19
+ - [`mcp_record_view`](#mcp_record_view)
20
+ - [`mcp_ai_session_search`](#mcp_ai_session_search)
21
+ - [`mcp_run_action`](#mcp_run_action)
22
+ - [Stopping a run — `controlAction:'interrupt'`](#stopping-a-run--controlactioninterrupt)
23
+ - [`mcp_run_agent`](#mcp_run_agent)
24
+ - [Controlling a live agent — `pause` / `unpause` / `interrupt`](#controlling-a-live-agent--pause--unpause--interrupt)
25
+ - [`mcp_system_logs`](#mcp_system_logs)
26
+ - [`mcp_user_activity`](#mcp_user_activity)
27
+ - [`mcp_performance_trace`](#mcp_performance_trace)
28
+ - [`mcp_event_diagnostics`](#mcp_event_diagnostics)
29
+ - [`mcp_component_view`](#mcp_component_view)
30
+ - [`mcp_component_grep`](#mcp_component_grep)
31
+ - [`mcp_support_ticket`](#mcp_support_ticket)
32
+ - [Component branches](#component-branches)
33
+ - [`mcp_cache`](#mcp_cache)
34
+ - [`mcp_sql_query`](#mcp_sql_query)
35
+ - [`mcp_index_search`](#mcp_index_search)
36
+ - [`mcp_get_guide`](#mcp_get_guide)
37
+ - [`mcp_test_fixture`](#mcp_test_fixture)
38
+ - [`mcp_playwright_replay`](#mcp_playwright_replay)
39
+ - [`mcp_jvm_spike_triage`](#mcp_jvm_spike_triage)
40
+ - [`mcp_support_ticket_queue`](#mcp_support_ticket_queue)
41
+
42
+ ## Tool Reference
43
+
44
+ ### Execute a Tool
45
+
46
+ ```bash
47
+ remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "invoices"}' --data-mode prod
48
+ ```
49
+
50
+ Response saved to `./.remits-cli/tool-responses/<callId>.json`. Read the file to see results.
51
+
52
+ **"Tool call succeeded" means DISPATCHED, not that the tool did what you asked.** A tool that runs
53
+ and refuses — an unmet precondition, a rejected enum value, a failed validation — returns HTTP 200
54
+ with its own `success: false` inside `result`. The CLI now prints `Tool call FAILED — the tool ran
55
+ and returned an error.` plus a `Tool error:` line and exits non-zero, and the response envelope
56
+ carries `toolSuccess` / `toolMessage`. **For any MUTATING call, confirm the tool's own verdict before
57
+ reporting the work as done** — do not grep the terminal output for "succeeded":
58
+
59
+ ```bash
60
+ F=$(remits-cli tool --name mcp_support_ticket --input "$(cat payload.json)" --data-mode prod 2>&1 \
61
+ | grep -o '[^ ]*tool-responses/[a-f0-9-]*\.json' | tail -1)
62
+ python3 -c "import json;r=json.load(open('$F'))['result'];print(r.get('success'), r.get('message'))"
63
+ ```
64
+
65
+ Build non-trivial JSON into a file (e.g. with `python3 -c 'json.dumps(...)'`) and pass it as
66
+ `--input "$(cat payload.json)"`. Long inline single-quoted JSON intermittently produces no response
67
+ file at all.
68
+
69
+ For long-running Action/Agent runners, use the tool's own async mode (`executionMode:"async"`), which returns
70
+ the `actionRunId`/`agentRunId` (and, for agents, `sessionId`) immediately:
71
+
72
+ ```bash
73
+ remits-cli tool --name "mcp_run_action" --input '{"accountId":37,"actionName":"Rebuild Invoice","executionMode":"async","actionInput":{"invoiceId":"abc"}}' --data-mode prod
74
+ # poll by run id: {"controlAction":"status","accountId":37,"actionRunId":"<actionRunId>"}
75
+ ```
76
+
77
+ For long tools that lack their own async mode, use the CLI transport async (`--async true`), optionally with
78
+ `--wait true` to poll locally, and `remits-cli tool status --call-id <callId>`. Do not stack both mechanisms
79
+ (see `command-reference.md` → *Tool Execution Lifecycle*). Use `--timeout-ms <ms>` only to adjust the per-request client timeout; it is
80
+ not a replacement for async mode on multi-minute workflows.
81
+
82
+ **Account-id precedence for tool calls.** When the CLI and the tool input both carry an account id, the server resolves them in this order:
83
+
84
+ 1. Explicit `--account-id <ID>` flag — always wins. Use this when you want to be certain the tool runs against a specific account (and the user's session covers it).
85
+ 2. `input.accountId` (or `input.account_id`) — the tool's per-call execution target.
86
+ 3. The current repo's `account-info.json` / active session — the default fallback.
87
+
88
+ So from inside a parent account's repo you can target a child account just by setting `input.accountId`, or force it with `--account-id` if you need it to override whatever the tool input says. Verify with the `accountId` field in the response envelope.
89
+
90
+ ### `mcp_account_view`
91
+ Returns complete account structure — schemas, components, relationships.
92
+
93
+ Also returns two blocks that explain how the account RESOLVES, which is what you need before comparing
94
+ its behavior against component source:
95
+
96
+ - `resolution` — `role` (`OWNER` = resolves its own component trunk, `SUBSCRIBER` = resolves another
97
+ account's components through a branch edge) and a one-sentence `summary`; `accountId` (the account
98
+ described — the top level of this shape is the hierarchy ROOT, so do not read identity from there);
99
+ `databaseName` (the account's own override, often null) vs `resolvedDatabaseName` (the storage namespace
100
+ actually in effect); `branchName` (the repo sync branch) and `lastRepoSync`; `domainName` vs
101
+ `resolvedDomainName` (the custom host in effect, which differs when reached through an edge host);
102
+ `authPath` / `targetPath` (login and post-login landing routes); `editMode`; and — when a component
103
+ branch is in effect — `componentBranch` plus `componentOwnerAccountId` / `componentOwnerAccountName`.
104
+ - `resolution.relationships` — **every structural link upward**, primary first: `parentAccountId` /
105
+ `parentAccountName` / `parentAccountCode` / `parentAccountType`, `primary` (true for the one link that
106
+ mirrors the account's primary parent), `active`, and the three independent link-scoped properties
107
+ `branchName` (which component-variant code runs), `databaseName` (where data lives), `domainName` (which
108
+ host reaches the account through this link). The hierarchy tree flattens all links into one shape, so this
109
+ is the **only** place that answers "does this account have more than one parent, and which link carries the
110
+ branch/namespace/host?" More than one entry ⇒ this account can resolve differently depending on the path a
111
+ request travelled — establish which one a failing request used before comparing behavior.
112
+ - `componentBranches` — the variant branches this account OWNS, with override/add/remove, subscriber, and
113
+ drift counts. Same summary the owner's `account-info.json` carries.
114
+
115
+ Note: this tool does **not** return users, and it returns EVERYTHING about one account. For users, for the
116
+ deep account tree, or for small account/user updates, use `mcp_account_user_admin`.
117
+
118
+ | Parameter | Required | Description |
119
+ |-----------|----------|-------------|
120
+ | `accountId` | yes | Account ID |
121
+
122
+ ### `mcp_account_user_admin`
123
+ The account-graph and user surface: the middle ground between `account-info.json` (which states structure
124
+ compactly, because it is read into your context every session) and `mcp_account_view` (everything about one
125
+ account).
126
+
127
+ - `action: 'hierarchy'` (default) — the descendant tree trimmed to `depth` (1-10, default 2; nodes cut off
128
+ are marked `truncated` and still report their child count) and/or the anchored ancestor chain plus every
129
+ edge (`direction: 'down'|'up'|'both'`). Nodes include `testAccount`. **This is how you get the deep tree
130
+ account-info.json omits.**
131
+ - `action: 'account'` — one account's `resolution` block, including `testAccount`, plus masked
132
+ configuration fields, without paying for the component inventory.
133
+ - `action: 'users'` — an account's users at a hierarchy `scope` (`self`/`children`/`parents`/`hierarchy`),
134
+ optional `email` substring filter. Rows include `testUser`. Extension fields are omitted here on purpose:
135
+ they are stored **per account** and these users are bound to their own.
136
+ - `action: 'user'` — one user (`userId` or `email`) with roles, account memberships, and extension fields
137
+ **correctly scoped to the requested account**, plus `testUser`. It never grants membership as a side
138
+ effect of a read, and tells you when the fields shown belong to a different account.
139
+ - `action: 'account_update'` — write Account-schema configuration `fields`, and/or `name`/`status`
140
+ (`ACTIVE`/`ON_HOLD`/`PENDING`).
141
+ - `action: 'user_update'` — write User-schema `fields` under the named account, plus `name`/`enabled` and
142
+ membership add/remove. Refused unless the user is a member or you pass `addAccount: true`, because the
143
+ write would otherwise land on another account. If an email does not exist globally, `addAccount: true`
144
+ intentionally creates that user first, then binds them to the named account before writing fields.
145
+
146
+ **Building an account hierarchy** (the structural writes — this is how a coding agent provisions accounts
147
+ without a browser):
148
+
149
+ **Data-lane rule for provisioning:** `remits-cli tool` defaults to `--data-mode test`. That is correct for
150
+ fixtures and rehearsals, but it means `mcp_account_user_admin` `action:'account_create'` creates test-lane
151
+ accounts unless the command explicitly passes `--data-mode prod`. For real platform/product/customer
152
+ provisioning, always dry-run in prod first, check the response's `dataMode`, then run the write in prod:
153
+
154
+ ```bash
155
+ remits-cli tool --name mcp_account_user_admin --data-mode prod --input '{"action":"account_create","parentAccountId":4,"name":"Freto","type":"PRODUCT","dryRun":true}'
156
+ ```
157
+
158
+ After the real write, verify the response (or re-read `action:'account'`) shows the command `dataMode` you
159
+ intended and `testAccount:false` for real provisioning. `testAccount:true` means you created a test-data
160
+ account, even if the name and structure look correct.
161
+
162
+ For a test rehearsal, make the opposite assertion explicit: the response should show `dataMode:'test'` and
163
+ `testAccount:true` for **newly created** accounts (or `testUser:true` for created users).
164
+
165
+ Read `reusedExisting` before reading anything into the flag. `account_create` is find-or-create, and a
166
+ **test**-lane create can legitimately match a **real** account: real accounts are visible in both lanes,
167
+ so a rehearsal for a name that already exists in prod returns `reusedExisting:true` /
168
+ `testAccount:false` and changes nothing. That is correct reuse, not a lane error. Only
169
+ `reusedExisting:false` with `testAccount:false` in a test rehearsal means the lane was not the one you
170
+ intended. (The prod direction is not symmetrical: a prod-lane create never resolves onto a test
171
+ CLIENT/PROVIDER account, so the same name can exist once per lane.)
172
+
173
+ **A test-lane account does not get the `code` the prod one will.** `code` is derived from `name` and is
174
+ globally unique, so a test-lane create with no explicit `code` is assigned `test_<code>_<parentId>`.
175
+ A namespace resolves as `databaseName ?: platform.code ?: code`, so a rehearsal **does not prove the
176
+ storage namespace** the real create will land in unless you set `databaseName` explicitly. Conversely,
177
+ passing an explicit `code` in a test rehearsal opts out of the prefix, and the later prod create then
178
+ fails on `code unique:true` — as it also will against a test account created before this rule existed.
179
+ Check the existing account's `code` before assuming a name is free.
180
+
181
+ Account/User schema `fields` are Firestore-backed extension fields. Their physical storage follows the
182
+ same data lane as the tool call: `--data-mode test` writes under `testing/<resolvedDatabaseName>/...`, while
183
+ `--data-mode prod` writes under `accounts/<resolvedDatabaseName>/...` (for modern segmented accounts). If a
184
+ test-lane rehearsal should become real provisioning, rerun the create/update in prod mode; do not assume the
185
+ test-lane Firestore fields moved.
186
+
187
+ - `action: 'account_create'` — create a child under `parentAccountId`, with its **primary relationship edge**,
188
+ applying `type` / `databaseName` / `domainName` / `authPath` / `targetPath` / `code` /
189
+ `repositoryNameOverride` / `branchName` / `editMode` / … **at birth**. That ordering matters: the storage
190
+ namespace is resolved from those properties, and the parent's cascaded schema fields are written into it
191
+ during creation. Idempotent — an existing same-name account under that parent comes back with
192
+ `reusedExisting: true`, unchanged. The response includes `testAccount`.
193
+ - `action: 'account_structure'` — change those properties on an existing account, including account-level
194
+ custom host, login path, and landing path.
195
+ - `action: 'edge_add'` / `'edge_update'` / `'edge_remove'` — manage a membership `AccountRelationship` edge to
196
+ `parentAccountId`, including the three independent edge properties `branchName` (which component code runs),
197
+ `databaseName` (a path-scoped storage-namespace override — **live**, and inherited by everything below
198
+ that edge) and `domainName` (which host reaches the account through that edge). Cycles, self-edges, and
199
+ removing the primary edge are all refused.
200
+ - `action: 'reparent'` — move the account's **primary** edge (and `Account.parentId`) to `parentAccountId`.
201
+
202
+ > **The namespace guard.** An account's storage namespace resolves as
203
+ > `databaseName ?: platform.code ?: code`, and `Account.setName` **regenerates `code`** — so renaming an
204
+ > account whose namespace falls through to its own code silently repoints its storage. Any write that would
205
+ > move the resolved namespace is **refused** unless you pass `confirmSegmentChange: true`, and the refusal
206
+ > names both namespaces. To rename without moving storage, pin `code` to the old value in the same call.
207
+ >
208
+ > Because the namespace follows the **path** (see *Account Structure* in `platform-overview.md`), an
209
+ > `edge_update` that sets `databaseName` repoints storage for **everything below that edge**, and the
210
+ > answer is path-specific — the same account reached through a different parent can resolve a different
211
+ > namespace. Dry-run these.
212
+
213
+ Every write supports `dryRun: true`, which reports each `from -> to` without writing. Not here by design:
214
+ component-branch subscription reporting and drift (use `remits-cli components branches` /
215
+ `remits-cli components branch <name>`), and account/user **deletion** (admin only, so the
216
+ destructive-teardown contract applies).
217
+
218
+ **Provisioning recipe** — a new product account under a platform, running its own component branch, with
219
+ client accounts beneath it:
220
+
221
+ ```
222
+ 1. mcp_account_user_admin action:'account_create' parentAccountId:<platform> name:'Adyen'
223
+ type:'PLATFORM'|'PRODUCT' [databaseName:'...']
224
+ 2. git branch + push in the OWNER's repo, then `remits-cli components sync` from that checkout
225
+ (non-trunk => writes ComponentVariant overlays only)
226
+ 3. mcp_account_user_admin action:'edge_update' targetAccountId:<new> parentAccountId:<platform>
227
+ branchName:'<branch>'
228
+ 4. mcp_account_user_admin action:'account_create' parentAccountId:<new> name:'<business unit>'
229
+ type:'CLIENT' # inherits the branch automatically
230
+ ```
231
+
232
+ > **Put the branch on the edge that is UNAMBIGUOUSLY on the account's path up — primary or membership.**
233
+ > Inheritance walks `parentId` and, at an account that has no `parentId`, continues through its *single*
234
+ > active membership edge. So a subscriber shell with **no `parentId` and one membership edge** to its owner
235
+ > is a fully supported shape: it resolves the branch, **and so do all of its descendants**. You do not need
236
+ > to make the subscriber a structural child of its owner.
237
+ >
238
+ > What is NOT resolved by default is genuine **ambiguity** — an account with *several* upward links, where
239
+ > the platform refuses to guess which product it was reached through. That account (and its descendants)
240
+ > resolve trunk until a request names the path: `--as-account`, `--variant-branch`, or the edge's own host.
241
+ > An account that has a `parentId` **and** a separate membership edge carrying the branch is this case: the
242
+ > `parentId` wins, so put the branch on the link the account actually inherits through.
243
+ >
244
+ > Either way, descendants inherit the branch down the chain automatically, which is what makes step 4 free.
245
+
246
+ | Parameter | Required | Description |
247
+ |-----------|----------|-------------|
248
+ | `action` | no | `hierarchy` (default), `account`, `users`, `user`, `account_update`, `user_update` |
249
+ | `accountId` / `targetAccountId` | no | Account to act on; `targetAccountId` targets another account without moving tool resolution |
250
+ | `depth` / `direction` | no | `hierarchy` only |
251
+ | `scope` | no | `users` only |
252
+ | `userId` / `email` | no | Identify the user (`user`, `user_update`); `email` is a filter for `users` |
253
+ | `fields` / `name` / `status` / `enabled` | no | The update payload |
254
+ | `addAccount` / `removeAccount` | no | Membership changes for `user_update` |
255
+ | `dryRun` | no | Report the change without writing |
256
+
257
+ ### `mcp_firestore_search`
258
+ Query Firestore documents.
259
+
260
+ | Parameter | Required | Description |
261
+ |-----------|----------|-------------|
262
+ | `accountId` | yes | Account ID |
263
+ | `collection` | yes | Collection name (snake_case plural, e.g., `invoices`) |
264
+ | `documentId` | no | Fetch single document by ID |
265
+ | `filters` | no | `[{field, operation, value}]`. Operations: `EQUALS` (or `==`), `NOT_EQUALS` (or `!=`), `GREATER_THAN` (or `>`), `GREATER_THAN_EQUALS` (or `>=`), `LESS_THAN` (or `<`), `LESS_THAN_EQUALS` (or `<=`), `IN`, `NOT_IN`, `ARRAY_CONTAINS`, `ARRAY_CONTAINS_ANY`, `IS_NULL`, `IS_NOT_NULL`. `op` is accepted as alias for `operation`. |
266
+ | `sort` | no | `[{field, direction}]` — `ASC`/`DESC`. Also accepts top-level `orderBy` + `orderDirection`. |
267
+ | `pagination` | no | `{limit, offset}`. Default limit=25, max=200. Also accepts top-level `limit`/`offset`. |
268
+ | `fields` | no | Field names to return. If omitted, auto-selects up to 20 fields. |
269
+ | `aggregation` | no | `{sum: [...], avg: [...], min: [...], max: [...], count: true}` |
270
+ | `dateRanges` | no | `[{field, startDate, endDate}]` (yyyy-MM-dd) |
271
+ | `textSearch` | no | `[{field, prefix}]` for prefix matching |
272
+ | `fallbackOnMissingIndex` | no | When `true`, a sorted read that fails on a missing composite index retries without sort and returns `warning`, `missingIndexUrl`, and `sortApplied:false`. Use when an unsorted first page is still useful. |
273
+
274
+ **HTTP audits use this same tool** — they are Firestore documents in monthly collections
275
+ (`http-audits/http-audits-YYYY-MM/entries`). Which filters to reach for, and when audits are the right
276
+ evidence at all, is in `investigation.md` → *HTTP audits*.
277
+
278
+ ```bash
279
+ remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "http-audits/http-audits-2026-05/entries", "filters": [{"field": "direction", "operation": "EQUALS", "value": "OUTBOUND"}, {"field": "request.path", "operation": "EQUALS", "value": "/api/orders"}, {"field": "success", "operation": "EQUALS", "value": false}], "sort": [{"field": "timestamp", "direction": "DESC"}], "pagination": {"limit": 10}}' --data-mode prod
280
+ ```
281
+
282
+ ### `mcp_firestore_patch`
283
+ Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
284
+ updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
285
+ fields.
286
+
287
+ Use it when the desired repair is mechanical and smaller than rerunning an expensive Action, for example copying
288
+ canonical fields into stale UI mirror fields. Always dry-run first and include preconditions:
289
+
290
+ ```bash
291
+ remits-cli tool --name mcp_firestore_patch --data-mode prod --input '{
292
+ "accountId":743,
293
+ "collection":"statements",
294
+ "documentId":"20958",
295
+ "dryRun":true,
296
+ "preconditions":[
297
+ {"field":"interchangeOptimization.status","equals":"Calculated"},
298
+ {"field":"interchangeOptimizationChecked","equals":false}
299
+ ],
300
+ "patch":{
301
+ "interchangeOptimizationChecked":true,
302
+ "feeBreakdown.interchangeOptimization":{"$copyFrom":"interchangeOptimization"},
303
+ "feeBreakdown.interchange.optimization":{"$copyFrom":"interchangeOptimization"}
304
+ }
305
+ }'
306
+ ```
307
+
308
+ Response fields include `dataMode`, `accountId`, `collection`, `documentId`, `dryRun`, `patchedFields`, and
309
+ `diff`. Switch to `"dryRun":false` only after the diff and preconditions are exactly what you intended.
310
+
311
+ ### `mcp_object_activity`
312
+ Object lifecycle timeline — metadata + recent activity.
313
+
314
+ | Parameter | Required | Description |
315
+ |-----------|----------|-------------|
316
+ | `accountId` | yes | Account ID |
317
+ | `objectId` | yes | Object ID (from `object_id` in documents) |
318
+ | `dataMode` | no | Explicit lane: `prod` or `test`. Response echoes `dataMode`. |
319
+ | `activityOptions` | no | `{limit, offset, types, start, end, order}`. Default: limit=5, order=desc. Types: `OBJECT_LOG`, `EVENT`, `ALERT`. |
320
+
321
+ Response fields include `dataMode`, `object.testMode`, and `testMode` on Event/Alert timeline entries.
322
+
323
+ ### `mcp_record_listing`
324
+ List and search lifecycle records when you do not already know the record ID.
325
+
326
+ Typical use:
327
+ - find active error alerts by `type` or `status`
328
+ - search alert/event/object_log content for an error phrase from an inbound support email
329
+ - narrow candidate records before switching to `mcp_record_view`
330
+
331
+ Tool ID: `88`
332
+
333
+ | Parameter | Required | Description |
334
+ |-----------|----------|-------------|
335
+ | `accountId` | yes | Account ID used for tenant scoping |
336
+ | `recordType` | yes | `object`, `object_log`, `event`, or `alert` |
337
+ | `filters` | no | Exact-match domain-property filters following the admin `listData` model, for example `status`, `type`, `active`, `threadGroupingId`, `action`, or enum fields using `_enum` |
338
+ | `ids` | no | Exact ID filter. Accepts a comma-separated string or array of numeric IDs |
339
+ | `query` | no | Lightweight text query over key searchable fields such as alert type/content/error text or object_log description/content |
340
+ | `page` | no | 1-based page number. Default: `1` |
341
+ | `pageSize` | no | Records per page. Default: `10`, max: `100` |
342
+ | `sort` | no | Domain property to sort by. Default: `id` |
343
+ | `order` | no | Sort direction: `asc` or `desc`. Default: `desc` |
344
+ | `includeChildren` | no | When `true`, include the specified account and child accounts |
345
+ | `scanLimit` | no | When using `query`, number of filtered candidate records to scan before text matching. Default: `200`, max: `500` |
346
+ | `maxPreviewChars` | no | Override preview length for returned content/body snippets. Max: `2048` |
347
+
348
+ ### `mcp_record_view`
349
+ Inspect individual lifecycle records with line-range or grep.
350
+
351
+ | Parameter | Required | Description |
352
+ |-----------|----------|-------------|
353
+ | `accountId` | yes | Account ID |
354
+ | `recordType` | yes | `object`, `object_log`, `event`, or `alert` |
355
+ | `recordId` | yes | Record primary key |
356
+ | `field` | no | `content` (default) or `body` (objects only) |
357
+ | `revisionId` | no | Envers revision ID (not for object_log) |
358
+ | `lineRange` | no | `{start, end}` (1-based inclusive) |
359
+ | `grep` | no | `{pattern, caseSensitive, contextBefore, contextAfter}` |
360
+
361
+ ### `mcp_ai_session_search`
362
+ Search AI session groupings and export grouping detail in human-readable form.
363
+
364
+ Typical use:
365
+ - find AI sessions by agent, user, account, session ID, or grouping ID
366
+ - inspect the exact prompts, system messages, tools, and responses used in a prior run
367
+ - identify tuning opportunities in Agent behavior by comparing session output with the front-stage guides and component source
368
+
369
+ Front-stage references:
370
+ - `components/agent-components.md`
371
+ - `features/ai-support.md`
372
+
373
+ | Parameter | Required | Description |
374
+ |-----------|----------|-------------|
375
+ | `action` | no | `search` (default), `detail`, or session control `pause`/`unpause`/`interrupt` |
376
+ | `dataMode` | no | Explicit execution lane: `prod` or `test`. Response echoes `dataMode`, but persisted groupings do not have durable per-row lane flags. |
377
+ | `search` | no | Broad text match against session IDs and grouping IDs |
378
+ | `sessionId` | no | Session ID filter in search mode, or grouping/session key in detail mode |
379
+ | `groupingId` | no | Grouping ID filter in search mode, or grouping key in detail mode |
380
+ | `groupingKey` | no | Preferred explicit grouping key for detail mode |
381
+ | `user` | no | User filter |
382
+ | `account` | no | Account filter |
383
+ | `agent` | no | Agent filter |
384
+ | `scope` | no | `all`, `agents`, or `internal` |
385
+ | `status` | no | Search mode: filter by live runtime status, comma-separated (e.g. `paused,interrupted`) |
386
+ | `scanLimit` | no | Search mode: window scanned when `status` is set. Default `100`, max `500` |
387
+ | `page` | no | 1-based page number. Default: `1` |
388
+ | `pageSize` | no | Results per page. Default: `25`, max: `100` |
389
+ | `summaryOnly` | no | Detail mode: return the MAP (record index + stats + timeline) with no payloads. Same as `parts:["index"]` |
390
+ | `parts` | no | Detail parts: `index`, `stats`, `transcript`, `tool_calls`, and record sections `full`, `conversation_messages`, `system`, `response`, `tools` |
391
+ | `sections` | no | Alias for `parts` |
392
+ | `recordIds` | no | Detail mode: open only these request/response record IDs |
393
+ | `toolCallIds` | no | Detail mode: return the FULL exact input/result from `ai_tool_call` for these tool-call ids (what the tool PRODUCED — see the lens caveat) |
394
+ | `consolidateContext` | no | When `true`, collapses repeated XML-like prompt context into a consolidated section |
395
+
396
+ Audit flow: `action:"search"` to find the grouping → `action:"detail"` + `summaryOnly:true` for the MAP → re-call detail with `recordIds`/`toolCallIds` + `parts` to open exactly what you need. Prefer the map → open flow over a full-detail dump. Remember the two-lens rule: `tool_calls`/`toolCallIds` is what the tool PRODUCED; `conversation_messages`/`transcript` is what the AI CONSUMED (after any `_offload`/`_hideResult`/`_message`/supersede/evict transform).
397
+
398
+ ### `mcp_run_action`
399
+ Run an Action on a target account, with explicit prod/test data mode, optional staged branch resolution, and
400
+ staged-vs-DB provenance in the result.
401
+
402
+ Describe the Action first when the input shape is not obvious. This does not execute the Action:
403
+
404
+ ```bash
405
+ remits-cli tool --name mcp_run_action --input '{"controlAction":"describe","accountId":743,"actionId":25,"includeInputSchema":true}' --data-mode prod
406
+ ```
407
+
408
+ The describe response reports `hasInputSchema`, optional `inputSchema`, `inferredInputKeys`, and component
409
+ provenance. If `hasInputSchema:false`, treat `inferredInputKeys` as a best-effort static scan, not a contract.
410
+
411
+ Use direct mode only for quick Actions:
412
+
413
+ ```bash
414
+ remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"direct","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
415
+ ```
416
+
417
+ Use the tool's own async mode for long-running Action execution — it returns immediately with an
418
+ `actionRunId`. Pass your **own** `actionRunId` so you can poll deterministically without first parsing it out
419
+ of the start response. Do **not** also pass the CLI `--async` flag; that only buries these ids behind the
420
+ transport layer:
421
+
422
+ ```bash
423
+ remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
424
+ ```
425
+
426
+ Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
427
+ `accountId` + `actionRunId`:
428
+
429
+ ```bash
430
+ remits-cli tool --name mcp_run_action --input '{"controlAction":"status","accountId":49,"actionRunId":"my-stable-run-id"}' --data-mode prod
431
+ ```
432
+
433
+ > This poll is a normal `remits-cli tool --name mcp_run_action` call — **not** `remits-cli tool status`,
434
+ > which polls the CLI-transport `--async` `callId` (a different mechanism). Use `controlAction:"status"`
435
+ > (rather than `command:"status"`) inside the input so it is never conflated with the transport-level status.
436
+ > If you started the run with a different `userId`, include that same `userId` in the poll (the run's status
437
+ > is keyed by account + user + `actionRunId`; it otherwise defaults to the current user).
438
+
439
+ For job-style Actions only (`Action.job == true`), prefer `executionMode:"event"` when you want the durable
440
+ Event lifecycle, Event status, and platform recovery behavior. Event mode is inherently async; poll it the
441
+ same way (`controlAction:"status"` + `actionRunId`) — the status resolves the backing Event's terminal state:
442
+
443
+ ```bash
444
+ remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"event","actionRunId":"my-stable-run-id","actionInput":{}}' --data-mode prod
445
+ ```
446
+
447
+ Returned fields on the async/event start: `actionRunId`, `status:"running"`, `executionMode`,
448
+ `threadGroupingId`, `componentSource`, `componentSignature`, and (event mode) `eventId`/`eventStatus`. The
449
+ `status` poll adds `result` on completion, or `message`/`error` on failure.
450
+
451
+ > **In event mode the EVENT is the source of truth, not the promise.** `status` is driven by the Event's
452
+ > own state; the value the dispatch call returned is reported separately as `dispatchResult` and is **not**
453
+ > the Action's result. Read `eventStatus` for the outcome.
454
+
455
+ #### Stopping a run — `controlAction:'interrupt'`
456
+
457
+ The tool counterpart of the **Interrupt** button on the admin Events page. Use it when an investigation
458
+ turns up a run that is consuming resources and should not finish — the case this exists for is finding a
459
+ `PROCESSING` event that has been running far too long.
460
+
461
+ ```bash
462
+ # the usual path: you found the event in mcp_record_listing / mcp_object_activity
463
+ remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"eventId":19102,"reason":"runaway extraction, 45min"}'
464
+
465
+ # or stop a run you started yourself (executionMode:'event' only)
466
+ remits-cli tool --name mcp_run_action --data-mode prod --input '{"controlAction":"interrupt","accountId":4,"actionRunId":"my-run-id"}'
467
+ ```
468
+
469
+ Aliases `cancel` / `stop` / `kill` all work. What you need to know before using it:
470
+
471
+ - **It is COOPERATIVE cancellation, not a thread kill.** It sets a flag the running work observes at its
472
+ next checkpoint, then throws. Checkpoints are dense across everything that matters — every Firestore
473
+ read/write, outbound HTTP call, AI turn, and front-stage DSL call — so a normal run stops promptly. A
474
+ run blocked inside a *single* long call (one slow AI turn) stops when that call returns, not instantly.
475
+ - **Work already committed is NOT rolled back.** This stops further work; it does not undo what has run.
476
+ - **Only `PENDING`/`QUEUED`/`PROCESSING` can be interrupted.** A terminal event is reported back with its
477
+ status rather than being silently reported as "interrupted".
478
+ - **Event-scoped.** A `direct` or `async` run has no Event and cannot be stopped this way.
479
+ - Tenant-scoped: you cannot interrupt another account's event.
480
+ - The response returns `previousStatus`, `eventStatus`, and `threadGroupingId`, so you can pivot straight
481
+ into `mcp_performance_trace` / `mcp_system_logs` to see what it was doing when you stopped it.
482
+
483
+ **AI sessions are stopped separately** with `mcp_ai_session_search` (`action:'interrupt'`, plus
484
+ `pause`/`unpause`) — that controls an agent's conversation loop, whereas this controls an Action's Event.
485
+
486
+ ### `mcp_run_agent`
487
+ Run one real Agent turn on a target account. The Agent hooks and tools execute for real against the requested
488
+ `dataMode`; pass `dataMode:"test"` for safer tuning.
489
+
490
+ Use direct mode only for short turns:
491
+
492
+ ```bash
493
+ remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Summarize this invoice context","executionMode":"direct"}' --data-mode prod
494
+ ```
495
+
496
+ Use the tool's own async mode for autonomous or long Agent turns. It returns immediately with both an
497
+ `agentRunId` and a single, stable `sessionId` — the **same** id the running session uses, so you can inspect
498
+ it right away. Pass your own `agentRunId` for deterministic polling. Do **not** also pass the CLI `--async`
499
+ flag (that only delays these ids into a polled result):
500
+
501
+ ```bash
502
+ remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Audit this invoice","executionMode":"async","agentRunId":"my-stable-run-id","context":{"invoiceId":"..."}}' --data-mode prod
503
+ ```
504
+
505
+ Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
506
+ `accountId` + `agentRunId` (this is a normal `mcp_run_agent` call, **not** `remits-cli tool status`):
507
+
508
+ ```bash
509
+ remits-cli tool --name mcp_run_agent --input '{"controlAction":"status","accountId":49,"agentRunId":"my-stable-run-id"}' --data-mode prod
510
+ ```
511
+
512
+ Inspect the live/persisted AI session at any time using the `sessionId` returned by the start call (it is the
513
+ run's real, canonical session id):
514
+
515
+ ```bash
516
+ remits-cli tool --name mcp_ai_session_search --input '{"action":"detail","sessionId":"<sessionId>","summaryOnly":true}' --data-mode prod
517
+ ```
518
+
519
+ Key returned fields: `agentRunId`, `sessionId`, `status`, `threadGroupingId`, runtime pause/interruption
520
+ hints, `lastAssistantMessage`, `toolCallCount`, `result`, `message`, and `error`.
521
+
522
+ #### Controlling a live agent — `pause` / `unpause` / `interrupt`
523
+
524
+ ```bash
525
+ remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"pause","accountId":49,"agentRunId":"my-run-id"}'
526
+ remits-cli tool --name mcp_run_agent --data-mode prod --input '{"controlAction":"interrupt","accountId":49,"sessionId":"<sessionId>"}'
527
+ ```
528
+
529
+ Target the session with the `agentRunId` an async start returned, or the `sessionId` directly.
530
+
531
+ - **`interrupt` is TERMINAL** (aliases `stop`/`cancel`/`kill`). It clears any pending resume and pending
532
+ guardrails and persists a terminal lifecycle status. An interrupted session **cannot be unpaused** —
533
+ attempting it is refused with that reason rather than silently doing nothing. Use `pause` if you intend
534
+ to resume.
535
+ - If the session is not resident on the serving node, it is rehydrated by agent name — so pass `agentName`
536
+ (or an `agentRunId`, which carries it) when controlling a session you did not just start.
537
+ - The same controls remain available on `mcp_ai_session_search`, which is the right tool when you are
538
+ *searching* for the session; this is the right one when you *started* the run.
539
+
540
+ ### `mcp_system_logs`
541
+ Query Cloud Run service logs.
542
+
543
+ | Parameter | Required | Description |
544
+ |-----------|----------|-------------|
545
+ | `node` | * | Node name (e.g., `remitsAdmin-east5`). Auto-resolves to serviceName+region. |
546
+ | `serviceName` | * | Cloud Run service. Not needed if `node` provided. |
547
+ | `region` | * | Cloud Run region. Not needed if `node` provided. |
548
+ | `timeRange` | * | Relative time: `1h`, `4h`, `30m`, `7d`. Auto-calculates startTime. |
549
+ | `startTime` | * | ISO 8601 timestamp. Not needed if `timeRange` provided. |
550
+ | `endTime` | no | ISO 8601 upper bound (defaults to now) |
551
+ | `severity` | no | Minimum: `INFO`, `WARNING`, `ERROR`, etc. |
552
+ | `threadGroupingId` | no | Filter by processing chain ID |
553
+ | `filter` | no | Additional Cloud Logging filter (LQL). **To search message text, pass the bare phrase** — it is widened automatically to match both `jsonPayload.message` (all `log.*` output) and `textPayload` (`println`/stdout). See the warning in `component-resolution.md` → *Diagnosing which version is in play*. |
554
+ | `maxPreviewChars` | no | Per-entry truncation width. Default 512, max 8000. Raise it when an entry carries a structured payload (a serialized `RemitsTrace`, a long stack frame) that the default cuts mid-JSON. |
555
+ | `pageSize` | no | Default 25, max 100. Also accepts `limit`. |
556
+
557
+ *Provide either `node` or `serviceName`+`region`. Provide either `timeRange` or `startTime`.
558
+
559
+ **Example:**
560
+ ```json
561
+ {"node": "remitsAdmin-east5", "timeRange": "4h", "severity": "ERROR"}
562
+ ```
563
+
564
+ ### `mcp_user_activity`
565
+ Read live user activity sessions and control focused capture. Use this before trace/log spelunking when
566
+ the report is user-centric, for example "User abc is reporting slow responses." It wraps the same
567
+ Redis-backed store as System → Activity and returns ready pivots to traces, logs, and component source.
568
+
569
+ Common flows:
570
+
571
+ ```bash
572
+ # Find what one user is doing right now
573
+ remits-cli tool --name mcp_user_activity --input '{"action":"sessions","userId":3,"limit":10}' --data-mode prod
574
+
575
+ # Open a returned sessionKey and inspect its beats
576
+ remits-cli tool --name mcp_user_activity --input '{"action":"story","sessionKey":"c_46ee68bba67003a6","limit":50}' --data-mode prod
577
+
578
+ # Arm focused capture, ask the user to reproduce, then read the story again
579
+ remits-cli tool --name mcp_user_activity --input '{"action":"watch","userId":3,"minutes":30}' --data-mode prod
580
+ ```
581
+
582
+ | Parameter | Required | Description |
583
+ |-----------|----------|-------------|
584
+ | `action` | no | `sessions`, `story`, `watch`, `unwatch`, `forget`, `status`, or `archive`. Default: `sessions`. |
585
+ | `userId` / `accountId` | no | Filter sessions/archive or choose the focus subject for `watch`/`unwatch`. One is required for `watch`/`unwatch`. |
586
+ | `sessionKey` | for `story`/`forget` | Salted activity session key returned by `sessions`; not a raw browser/session credential. |
587
+ | `focusedOnly` | no | For `sessions`, return only focused/watched sessions. |
588
+ | `sinceMs` | no | For `sessions`, lower bound on last-seen epoch milliseconds. Defaults to the activity TTL window. |
589
+ | `limit` | no | Session/story/archive row cap. Defaults: sessions=100, story=200, archive=50. |
590
+ | `minutes` | no | Watch TTL for `watch`; defaults to `activity.focus.ttl.minutes`. |
591
+ | `traceId` / `threadGroupingId` | no | For `archive`, narrow focused activity log pivot to one trace. |
592
+ | `lookbackHours` | no | For `archive`, Cloud Logging window in the returned `mcp_system_logs` pivot. Default 24, max 168. |
593
+ | `node` / `serviceName` / `region` | no | For `archive`, target for the returned `mcp_system_logs` pivot. `node` defaults to `remitsAdmin-east5`. |
594
+
595
+ Reading rule: use `sessions` → `story` to build the behavioral timeline, then open a slow or failed beat's
596
+ `mcp_performance_trace` pivot. Use `watch` when the user can reproduce and no live story exists. Use
597
+ `archive` only for watched/focused sessions; ordinary activity lives in Redis and expires with the activity
598
+ TTL.
599
+
600
+ ### `mcp_performance_trace`
601
+ Read Remits request traces through the same `traces(...)` DSL that powers the admin Diagnostics "Request
602
+ traces" panel. Use this before raw log spelunking for slow or sluggish requests because it returns profiled
603
+ span rollups, component annotations, and retained slow-request summaries directly.
604
+
605
+ Common flows:
606
+
607
+ ```bash
608
+ # User only knows it was slow this afternoon
609
+ remits-cli tool --name mcp_performance_trace --input '{"action":"slowest","lookbackHours":4,"accountId":52,"minMs":2000,"limit":10}' --data-mode prod
610
+
611
+ # You have the Diagnostics/request/Object/Event/Alert threadGroupingId
612
+ remits-cli tool --name mcp_performance_trace --input '{"action":"trace","traceId":"msf1y65n-001","lookbackHours":6,"format":"markdown"}' --data-mode prod
613
+
614
+ # Same local-node data as the Diagnostics table
615
+ remits-cli tool --name mcp_performance_trace --input '{"action":"snapshot","limit":100}' --data-mode prod
616
+ ```
617
+
618
+ | Parameter | Required | Description |
619
+ |-----------|----------|-------------|
620
+ | `action` | no | `snapshot`, `slowest`, or `trace`. Default: `snapshot`. |
621
+ | `traceId` / `threadGroupingId` | for `trace` | Request/grouping id to open across local ring and Cloud Logging. |
622
+ | `lookbackHours` | no | Cloud Logging window for `trace`/`slowest`. Defaults are 6 and 4 hours. |
623
+ | `accountId` | no | Account filter for `slowest`. |
624
+ | `kind` | no | Operation kind filter for `slowest`. **Rarely what you want** — see the note below. |
625
+ | `componentType` | no | Filter `slowest` by the component that did the work: `Action`, `Reader`, `Rule`, `Embeddable`, `Tool`, `Test`. **This is the right axis for "which Actions/Rules are slow".** |
626
+ | `componentId` / `componentName` | no | Narrow `slowest` to one component. |
627
+
628
+ > **Filter by `componentType`, not `kind`.** `kind` is set by whoever OPENS the trace. An Action delivered
629
+ > by Cloud Tasks arrives over HTTP, so the trace is `kind:'web'` and the Action is a `component.Action`
630
+ > *span inside it*; a Rule fired during a request and a Tool invoked by an agent are the same. So
631
+ > `kind:'action'` matches almost nothing in production. Every component execution annotates
632
+ > `componentType`/`componentId`/`componentName` — filter on those.
633
+ | `minMs` | no | Minimum duration for `slowest`. Default: 1500. |
634
+ | `limit` | no | Result limit. |
635
+ | `format` / `markdown` | no | Set `format:"markdown"` or `markdown:true` for a rendered trace report. |
636
+
637
+ > **Tools are components, so availability is per ACCOUNT.** `Tool not found: mcp_performance_trace` does
638
+ > not mean the tool is broken or that tracing is off — it means that tool has not been synced to the
639
+ > account you are resolving against. This bites most often on **localhost** (a Test Account that has not
640
+ > pulled the System Account's tool set) and on **client accounts**. Run `remits-cli tools` for the account
641
+ > in question, or re-run against an account that owns the tool (`--account-id 4` for the System Account).
642
+ > The same is true of every `mcp_*` tool, including the component tools noted below.
643
+
644
+ Reading rule: first use `slowest` to get candidate trace ids, then call `trace` on the suspicious id, then use
645
+ `mcp_system_logs` only if you need surrounding log lines. A trace dominated by `firestore.*`, `http.*`,
646
+ `gorm.save.*`, or component spans points you at the relevant platform seam or front-stage component. Large
647
+ unaccounted wall time is itself a finding: check cold compile, queueing, blocking I/O, or missing
648
+ `measure(...)` instrumentation.
649
+
650
+ ### `mcp_event_diagnostics`
651
+ Diagnose one Event's infrastructure outcome through the same `eventDiagnostics(eventId)` DSL described in
652
+ `features/observability.md`. Use this before opening Action source when an Event is stuck,
653
+ recovered, timed out, retried, or appears to have been killed.
654
+
655
+ ```bash
656
+ remits-cli tool --name mcp_event_diagnostics --input '{"accountId":49,"eventId":18838}' --data-mode prod
657
+ ```
658
+
659
+ | Parameter | Required | Description |
660
+ |-----------|----------|-------------|
661
+ | `accountId` | yes | Tenant scope. The Event must belong to this account unless `includeChildren:true`. |
662
+ | `eventId` / `id` | yes | Event primary key to diagnose. |
663
+ | `includeChildren` | no | Allow the Event to belong to the requested account or one of its child accounts. Default: `false`. |
664
+
665
+ Read `classification` first:
666
+
667
+ - `APPLICATION_FAILURE` — the Action failed in application code; read `event.errorMessage`, correlated
668
+ alerts, and the producing component.
669
+ - `ORPHANED_*` / `RECOVERED_*` — the attempt was abandoned; read `abandonmentCause`.
670
+ - `REQUEST_TIMEOUT_LIKELY` — it used its whole deadline (`timing.deadlineUsed` near `1.0`). The unit of
671
+ work is too big for one event; the fix is resumable batches, not component logic.
672
+ - `PROCESS_TERMINATED_LIKELY` — it stopped well inside its deadline (`timing.deadlineUsed` near `0`), so
673
+ the worker was killed (memory pressure, restart). Not a logic bug; inspect JVM/node health and
674
+ container lifecycle logs.
675
+ - `UNKNOWN_NO_DEADLINE_EVIDENCE` — no deadline was recorded, so the cause is genuinely unknown. Use the
676
+ returned `logQuery` filters; do not assume. Events predating delivery-envelope capture always look
677
+ like this.
678
+ - `AWAITING_DELIVERY` — the Event was never claimed; check queue delivery and action-node health.
679
+ - `IN_FLIGHT_HEALTHY` — the Event is still heartbeating. A long Action is not a stuck one; wait, and
680
+ inspect trace/logs before interrupting.
681
+
682
+ `delivery.deliveryAttempt` above `1` means Cloud Tasks had **already** retried this event, so any
683
+ non-idempotent side effect may have run more than once.
684
+
685
+ The response returns `delivery.threadGroupingId` — the same id everything else uses — plus the full
686
+ `result` map, `diagnosisHints`, and `pivots` carrying ready-to-run inputs for `mcp_performance_trace`,
687
+ `mcp_system_logs`, `mcp_record_listing`, and `mcp_object_activity` when those handles are present.
688
+ `logQuery` carries ready-made Cloud Logging filters, including the container-lifecycle and 504 queries.
689
+ For a performance question, open the `mcp_performance_trace` pivot next; for raw failure context, open
690
+ logs and records by `threadGroupingId`.
691
+
692
+ ### `mcp_component_view`
693
+ Read component field content with line numbers.
694
+
695
+ In a normal `remits-cli` coding workflow, prefer local repo files for source reads. Use this tool when the
696
+ local repo is unavailable, when confirming live DB source, or when you need staging / variant metadata that
697
+ is not present in the working tree.
698
+
699
+ | Parameter | Required | Description |
700
+ |-----------|----------|-------------|
701
+ | `accountId` | yes | Account ID |
702
+ | `componentType` | yes | `Schema`, `Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`, `Agent`, `Test`, `Tool`, `Prompt` |
703
+ | `componentId` | yes | Component ID |
704
+ | `fieldName` | no | `source`, `html`, `javascript`, `css`, `schema`, `description`, `mermaid`. Also accepts `field`. Omit for metadata. |
705
+ | `offset` | no | Start line (1-indexed). Also accepts `startLine`. |
706
+ | `limit` | no | Number of lines to return |
707
+
708
+ Returns `componentVariants` when the component has committed branch variants — the branches, their state
709
+ (`current` / `drifted` / `removed`), and a warning. Non-null means some accounts run a different version
710
+ than the source you are reading, and trunk promotions can drift those variants.
711
+ `mcp_component_grep` searches trunk, so it will not match text that exists only in a variant.
712
+
713
+ ### `mcp_component_grep`
714
+ Search component source code with regex.
715
+
716
+ | Parameter | Required | Description |
717
+ |-----------|----------|-------------|
718
+ | `accountId` | yes | Account ID |
719
+ | `componentType` | yes | Component type |
720
+ | `pattern` | yes | Regex to search. Also accepts `searchTerm`, `query`, `search`. |
721
+ | `fieldName` | no | Field to search (default: `source`). Also accepts `field`. |
722
+ | `componentId` | no | Specific component. If omitted, searches ALL of the type. |
723
+ | `context` | no | Lines before AND after each match. Also accepts `contextLines`. |
724
+ | `caseSensitive` | no | Default: true |
725
+
726
+ ### `mcp_support_ticket`
727
+ Create and manage the full lifecycle of account-relative `support_tickets`.
728
+
729
+ | Parameter | Required | Description |
730
+ |-----------|----------|-------------|
731
+ | `accountId` | conditional | The account that owns the support ticket. **Required only for `create`.** For every other action it is optional — the tool resolves the owning account from `ticketId` (the ticket's anchor id) and returns it. Pass it only to override/disambiguate. |
732
+ | `action` | yes | `create`, `read`, `accept`, `update_status`, `complete`, `release`, `record_progress`, `add_artifact`, or `get_attachment` |
733
+ | `ticketId` | conditional | Required for every action **except** `create` (which returns the new ticket ID). Alone it is sufficient to resolve the ticket and its owning account. |
734
+ | `subject` | conditional | Short title. Required for `create`. |
735
+ | `type` | conditional | Required for `create`: `enhancement`, `defect`, `question`, `task`, or `incident` |
736
+ | `priority` | no | `low`/`medium`/`high`/`critical` for `create` (default `medium`) |
737
+ | `description` | no | Longer description of the request/issue for `create` |
738
+ | `affectedComponent` | no | Component or platform area affected (`create`) |
739
+ | `implementationAccountId` / `implementationAccountName` | no | Owning `PLATFORM`/`PRODUCT` account when the ticket concerns shared implementation (e.g. a back-stage platform fix) |
740
+ | `stepsToReproduce` / `acceptanceCriteria` / `tags` | no | Extra `create` fields for defect/enhancement tickets |
741
+ | `assignee` | no | Required for `accept`. The agent's own name by convention — `claude`, `codex`, `gemini` |
742
+ | `status` | no | Required for `update_status`. Valid values: `in_progress`, `pending_review`. **The ticket must be `accept`ed first** — otherwise the call is rejected with *"Ticket must be accepted before updating status"* |
743
+ | `resolution` | no | Required for `complete` |
744
+ | `category` / `summary` / `details` / `findings` / `nextStep` | no | Worklog fields for `record_progress` (`category` + `summary` required). `category` is a **fixed enum** — `triage`, `investigation`, `reproduction`, `fix`, `verification`, `handoff`, `other` — and any other value fails the whole call. `findings` is a **list of strings**, not a paragraph |
745
+ | `artifactType` / `artifactLabel` / `contentBase64` / `gcsPath` / `url` | no | Evidence fields for `add_artifact` (screenshot/trace/log/test_result/link) |
746
+ | `notes` | no | Optional lifecycle note stored with the ticket activity |
747
+ | `attachmentIndex` | no | Zero-based index of the attachment to download. Used with `get_attachment`. |
748
+
749
+ **Opening a ticket for your own work (`create`).** When you are asked to do work — or you discover a Remits **back-stage** defect while building front stage — and you were **not** handed an existing ticket, open one with `action:'create'` so the work is tracked end-to-end. For a platform fix, use `type:'defect'` (or `'enhancement'` for a gap), describe the seam and evidence, reference the fix PR, and set `implementationAccountId`/`implementationAccountName` to the owning `PLATFORM`/`PRODUCT` account.
750
+
751
+ **Attachments:** Support emails may include file attachments (screenshots, logs, documents). These are automatically extracted and stored in GCS when the email is ingested. The `read` action returns an `attachments` array on the ticket with metadata for each file (`index`, `filename`, `contentType`, `size`, `messageId`, `uploadedAt`). To retrieve the actual file content:
752
+
753
+ 1. Use `read` to see the attachments list and their indices
754
+ 2. Use `get_attachment` with the desired `attachmentIndex` to download the file content (returned base64-encoded)
755
+ 3. If called without `attachmentIndex`, `get_attachment` lists all attachments with their indices
756
+
757
+ This keeps file retrieval self-contained — no separate download endpoint is needed.
758
+
759
+ **Recommended flow** — `accountId` is optional throughout; `ticketId` resolves the owning account:
760
+ 1. `read` — check ticket state and any attachments
761
+ - If `read` returns `ticket.mirrorOnly:true`, use the mirrored fields for triage context, restore the backing document first, then claim/update/complete it.
762
+ 2. **`accept`** (with `assignee`) — this is a **hard precondition for `update_status`**, not just etiquette
763
+ 3. `get_attachment` if attachments are present and relevant to the investigation
764
+ 4. `update_status` — `in_progress` while working, `pending_review` when the fix is done but not yet deployed
765
+ 5. `record_progress` as you go — one `investigation` entry for the root cause, one `verification` entry for the proof
766
+ 6. investigate/fix/verify on the owning ticket account or its implementation account as appropriate
767
+ 7. `complete` (with `resolution`) or `release` if handing off
768
+
769
+ **Check each mutation actually landed.** Every action above is a write that can be refused while the
770
+ CLI still reports the *call* as fine — see "Execute a Tool" for why, and read `result.success` from
771
+ the response file. A silent no-op here means telling the user a ticket moved when it did not.
772
+
773
+ **Duplicates are common.** The same defect is often filed twice — once against the `CLIENT`/subscriber
774
+ account where it was observed and once against the owning `PLATFORM`/`PRODUCT` account. Before
775
+ starting, check `mcp_support_ticket_queue` for the same subject or affected component. Close the
776
+ duplicate with a `resolution` naming the ticket that carries the real work, rather than investigating
777
+ it twice.
778
+
779
+ **Automation rule:** If a ticket is involved, you should usually:
780
+ - `read` at the start
781
+ - `accept` before substantive work
782
+ - `get_attachment` if there are attachments relevant to the issue (screenshots, error logs, etc.)
783
+ - `update_status` when actively working or blocked
784
+ - `complete` after verification
785
+ - `release` if you are handing it off or cannot continue
786
+
787
+ ### Component branches
788
+ Use `remits-cli components branches` / `remits-cli components branch <name>` to inspect committed branch
789
+ variants, subscribers, and drift from a local checkout.
790
+
791
+ Common uses:
792
+ - `remits-cli components branches` — list branches this owner has variants on.
793
+ - `remits-cli components branch <name>` — show overridden / added / removed components on that branch.
794
+ - `remits-cli components branch <name> --diff <id> --component-type <kind>` — compare one variant against
795
+ current trunk.
796
+ - `remits-cli components branch <name> --subscribers` — list accounts resolving that branch.
797
+
798
+ ### `mcp_cache`
799
+ Bounded read-only investigation of the platform Redis keyspace — the way to see exactly what a staged
800
+ entry holds (and its TTL) or any other cache key. Read-only: no delete (use `remits-cli components clear`
801
+ to remove staged component entries).
802
+
803
+ | Parameter | Required | Description |
804
+ |-----------|----------|-------------|
805
+ | `action` | yes | `summary` (overview of matching keys), `scan` (paginated key list; add `includeValuePreview:true`), or `inspect` (one exact `key`) |
806
+ | `pattern` | no | Redis glob for summary/scan (e.g. `account:52:cli:*:components:*:reader:id:181`). Alias: `keyPattern`/`query` |
807
+ | `key` | no | Exact key for `action:'inspect'` |
808
+ | `pageSize`/`sampleSize`/`previewChars` | no | Bounding controls |
809
+
810
+ ### `mcp_sql_query`
811
+ Read-only, bounded SQL against the platform database. This is the **catch-all investigation surface** for
812
+ questions the purpose-built tools do not model — above all **users and account membership**, for which there
813
+ is no dedicated tool.
814
+
815
+ | Parameter | Required | Description |
816
+ |-----------|----------|-------------|
817
+ | `query` | yes* | One read-only statement. Alias: `sql`. Must start with `SELECT`, `WITH`, `SHOW`, `DESCRIBE`/`DESC`, or `EXPLAIN`; mutation, DDL, locking, and filesystem constructs are rejected. |
818
+ | `queries` | yes* | Batch of up to 10 read-only queries (strings, or `{query, params}` objects) |
819
+ | `params` / `parameters` | no | Positional parameters — **use these instead of interpolating values** |
820
+ | `maxRows` / `limit` | no | Rows per query. Default 100, max 500. |
821
+ | `maxValueChars` | no | Truncation width per string value. Default 2000, max 20000. |
822
+ | `redact` | no | Redact secret-like columns (`password`, `token`, `secret`, `authorization`, …). **Default true — leave it on.** |
823
+
824
+ *Provide `query`/`sql` or `queries`.
825
+
826
+ Useful shapes:
827
+
828
+ ```sql
829
+ -- who has access to a client account (and through which membership rows)
830
+ SELECT u.id, u.username, u.enabled FROM user u
831
+ JOIN user_account ua ON ua.user_id = u.id WHERE ua.account_id = ?;
832
+
833
+ -- every account a user can reach directly
834
+ SELECT a.id, a.name, a.type FROM account a
835
+ JOIN user_account ua ON ua.account_id = a.id WHERE ua.user_id = ?;
836
+
837
+ -- the account's structural links, including membership edges (`primary` is column `is_primary`)
838
+ SELECT parent_id, is_primary, branch_name, database_name, domain_name, active
839
+ FROM account_relationship WHERE account_id = ?;
840
+ ```
841
+
842
+ **This tool is lane-blind — filter the data lane yourself.** Every other record surface segments test from
843
+ prod data for you; raw SQL does not. `object`, `event`, and `alert` carry a `test_mode` column, `user`
844
+ carries `test_user`, and `account` carries `test_account`, and an unfiltered query returns **both lanes
845
+ mixed** — so "who has access to this account" silently includes throwaway test users, and a row count
846
+ silently includes test fixtures. Add the predicate explicitly:
847
+
848
+ ```sql
849
+ -- prod lane only (legacy rows predate the column, so NULL counts as prod)
850
+ SELECT id, name, status FROM object
851
+ WHERE account_id = ? AND (test_mode IS NULL OR test_mode = 0);
852
+
853
+ -- real users of an account, excluding test fixtures
854
+ SELECT u.id, u.username, u.enabled FROM user u
855
+ JOIN user_account ua ON ua.user_id = u.id
856
+ WHERE ua.account_id = ? AND (u.test_user IS NULL OR u.test_user = 0);
857
+ ```
858
+
859
+ Prefer the purpose-built tools when one fits — they apply account scoping, data-mode segmentation, and
860
+ resolution awareness that raw SQL does not. Reach for SQL when nothing else models the question.
861
+
862
+ ### `mcp_index_search`
863
+ Query and **diagnose** the Vertex AI Search index through the platform's Vertex DSL. Use it to check what a
864
+ RAG/search-backed component actually retrieves before blaming the component.
865
+
866
+ | Parameter | Required | Description |
867
+ |-----------|----------|-------------|
868
+ | `accountId` | yes | Tenant scoping |
869
+ | `action` | no | `search` (default, `index_search()`), `facets` (`index_facets()` — taxonomy/distinct values), `diagnose` (explain why strict filtering dropped matches), `inspect` (what is actually indexed for given `sourceDocumentIds`) |
870
+ | `query` | conditional | Required for `search`/`diagnose` |
871
+ | `schema` / `projectionType(s)` / `projectionSource` | no | Scope to a schema's Vertex projections |
872
+ | `filters` | no | Vertex-style structured filters on indexed metadata |
873
+ | `pageSize` / `maxResults` / `pageToken` | no | Paging and local trimming |
874
+ | `searchProfile` | no | `ai`/`agent`/`strict` (precision) vs `admin`/`default` (recall) |
875
+ | `includeMatchDiagnostics` | no | Return the applied filter/query plus the pre-strict-filter candidate set |
876
+ | `accountIds` | no | Explicit multi-account search |
877
+ | `dataMode` | no | `test` (default) or `prod` |
878
+
879
+ When a component "can't find" an obviously-present document, run `action:'inspect'` on its source document id
880
+ first — that shows what was indexed, which is usually the answer.
881
+
882
+ ### `mcp_get_guide`
883
+ Load the packaged front-stage guides — the same `docs/guides/` set `remits-cli` syncs into a repo. Use it when
884
+ you are working **outside a repo** (or the repo's `guides/` is stale) and need the authoritative guidance
885
+ before writing a component.
886
+
887
+ | Parameter | Required | Description |
888
+ |-----------|----------|-------------|
889
+ | `guide` | conditional | Short name (`agent-components`), relative path (`features/account-management`), or full path |
890
+ | `list` | no | List available guides instead of loading one |
891
+ | `directory` | no | Scope a list to `components` or `features` |
892
+ | `contains` | no | Substring filter when listing |
893
+
894
+ Every guide is delivered with a table of contents whose entries carry **real line numbers**
895
+ (`- L412 Querying Alerts`), resolved at delivery so they are never stale. Several of these guides are
896
+ over a thousand lines: read the head, pick the sections you need, and offset-read those rather than
897
+ loading the whole file. The entry text is the heading verbatim, so it also greps.
898
+
899
+ ### `mcp_test_fixture`
900
+ Seed and remove schema-backed fixture documents in the **forced test** data segment. This is how you construct
901
+ realistic conditions for verification without copying live customer data.
902
+
903
+ | Parameter | Required | Description |
904
+ |-----------|----------|-------------|
905
+ | `accountId` | yes | Account owning the target schema/collection |
906
+ | `action` | no | `create` (fails on existing id), `upsert`, or `delete` |
907
+ | `schemaName` / `collection` | conditional | Schema display name or collection name |
908
+ | `documentId` / `documentIds` | no | Explicit Firestore ids for single/batch operations |
909
+ | `data` / `documents` | conditional | Single payload, or a batch array |
910
+ | `dataMode` | no | Must be `test` — **prod-mode writes are rejected** |
911
+
912
+ ### `mcp_playwright_replay`
913
+ Hosted browser automation (the `playwright-relay` service) for visual verification when local `playwright-cli`
914
+ is unavailable — e.g. an agent running remotely. Returns an accessibility snapshot and interactive refs on
915
+ every command, so you navigate iteratively.
916
+
917
+ | Parameter | Required | Description |
918
+ |-----------|----------|-------------|
919
+ | `command` | yes | `open`, `goto`, `snapshot`, `click`, `dblclick`, `hover`, `fill`, `select`, `check`, `uncheck`, `eval`, `run-code`, `screenshot`, `pdf`, `console`, `network`, `go-back`, `go-forward`, `reload`, `tab-*`, `close`, `close-all` |
920
+ | `sessionId` | conditional | Reuse an existing session. `open`/`goto` without one starts a session. |
921
+ | `url` / `target` / `value` / `expression` / `code` | conditional | Per-command inputs (`target` is an element ref or selector) |
922
+ | `compact` | no | Default true — omits bulky raw relay payloads |
923
+ | `includeSnapshot` / `includeAccessibility` / `includeRefs` / `includePage` / `includeResult` / `includeRaw` | no | Response shaping |
924
+ | `pattern` / `level` / `limit` | no | Filters for `console` / `network` |
925
+ | `ticketId` / `artifactType` / `artifactLabel` / `artifactNotes` | no | Attach a screenshot/pdf/trace/video to a support ticket |
926
+
927
+ ### `mcp_jvm_spike_triage`
928
+ Production-aware triage bundle for a Cloud Run service showing a latency/memory spike. Safe by construction:
929
+ optional class-histogram sampling, **no heap dump and no JFR**.
930
+
931
+ | Parameter | Required | Description |
932
+ |-----------|----------|-------------|
933
+ | `serviceName` | yes | e.g. `remits`, `remits-actions` |
934
+ | `region` | yes | e.g. `us-east5`, `us-east1` |
935
+ | `sampleHeap` | no | Default true — one class histogram + heap composition sample (brief stop-the-world) |
936
+ | `top` | no | Top classes to return. Default 20, max 100. |
937
+ | `includeThreadDump` / `threadLimit` | no | Fuller thread dump beyond the built-in top-thread preview |
938
+ | `includeLogs` / `logLookbackMinutes` / `logLimit` | no | Recent WARNING+ log signals for the same service |
939
+
940
+ ### `mcp_support_ticket_queue`
941
+ List and filter tickets **across an account and its descendants** so you can choose what to work on. Lifecycle
942
+ actions stay on `mcp_support_ticket`.
943
+
944
+ Queue rows are built from support-ticket anchor mirrors by default. A row in this list means a
945
+ support-ticket anchor exists; it does not guarantee the full Firestore document is healthy. Open the
946
+ ticket with `mcp_support_ticket read` before lifecycle work and honor `mirrorOnly` / `documentState` if present.
947
+
948
+ | Parameter | Required | Description |
949
+ |-----------|----------|-------------|
950
+ | `action` | no | `list` (default) |
951
+ | `accountId` / `accountIds` / `includeChildren` / `includeRoot` | no | Queue scope. Defaults to the current account **and its descendants**. |
952
+ | `statuses` / `status` | no | Defaults to `open`, `accepted`, `in_progress`, `pending_review`. `['all']` disables filtering. |
953
+ | `priorities` / `types` / `sources` / `tags` | no | Additional filters |
954
+ | `assignedTo` / `unassigned` | no | Assignment filters |
955
+ | `implementationAccountId` | no | Filter by owning `PLATFORM`/`PRODUCT` account |
956
+ | `workstream` / `plannedIn` / `boardStage` / `size` / `blockedBy` | no | SDLC-agnostic planning filters; exact matches on compact queue fields |
957
+ | `rank` / `minRank` / `maxRank` | no | Exact or inclusive range filters for numeric planning rank |
958
+ | `search` | no | Case-insensitive across subject, description, account, affected component, sender, tags, workstream, and planning fields |
959
+ | `sortBy` | no | `triage` (default-style: critical/high unassigned first), `updated`, `priority`, `status`, `account`, `plannedIn`, `boardStage`, `rank`, `size` |
960
+ | `sortDirection` | no | `asc` / `desc`, and it means the same thing on every axis. Natural order is `desc` for `triage`/`updated`/`priority` and `asc` for `status`/`account`/`rank`/`plannedIn`/`boardStage`/`size`, so pass it only to invert one |
961
+ | `limit` / `offset` / `scanLimitPerAccount` | no | Paging and scan bounds |
962
+ | `dataMode` | no | Normal agent work uses the **prod** ticket queue |