@synapsor/runner 0.1.0-alpha.0 → 0.1.0-alpha.10

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.
Files changed (44) hide show
  1. package/README.md +389 -21
  2. package/TRADEMARKS.md +23 -0
  3. package/dist/cli.d.ts +4 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +20 -8723
  6. package/dist/runner.mjs +12759 -0
  7. package/docs/README.md +36 -0
  8. package/docs/getting-started-own-database.md +460 -0
  9. package/docs/http-mcp.md +242 -0
  10. package/docs/limitations.md +95 -0
  11. package/docs/local-mode.md +351 -0
  12. package/docs/mcp-audit.md +152 -0
  13. package/docs/mcp-client-setup.md +231 -0
  14. package/docs/recipes.md +61 -0
  15. package/docs/release-notes.md +129 -0
  16. package/docs/security-boundary.md +94 -0
  17. package/docs/troubleshooting-first-run.md +248 -0
  18. package/docs/writeback-executors.md +209 -0
  19. package/examples/app-owned-writeback/README.md +120 -0
  20. package/examples/app-owned-writeback/business-actions.md +221 -0
  21. package/examples/app-owned-writeback/command-handler.mjs +46 -0
  22. package/examples/app-owned-writeback/node-fastify-handler.mjs +55 -0
  23. package/examples/app-owned-writeback/python-fastapi-handler.py +57 -0
  24. package/examples/dangerous-mcp-tools.json +88 -0
  25. package/examples/openai-agents-http/README.md +56 -0
  26. package/examples/openai-agents-http/agent.py +54 -0
  27. package/examples/openai-agents-http/requirements.txt +1 -0
  28. package/examples/openai-agents-stdio/README.md +62 -0
  29. package/examples/openai-agents-stdio/agent.py +70 -0
  30. package/examples/openai-agents-stdio/requirements.txt +1 -0
  31. package/examples/reference-support-billing-app/README.md +137 -0
  32. package/examples/reference-support-billing-app/docker-compose.yml +13 -0
  33. package/examples/reference-support-billing-app/mcp-client.generic.json +11 -0
  34. package/examples/reference-support-billing-app/schema.sql +68 -0
  35. package/examples/reference-support-billing-app/scripts/run-demo.sh +7 -0
  36. package/examples/reference-support-billing-app/seed.sql +33 -0
  37. package/examples/reference-support-billing-app/synapsor.runner.json +241 -0
  38. package/package.json +12 -4
  39. package/recipes/accounts.trial_extension.json +42 -0
  40. package/recipes/billing.late_fee_waiver.json +46 -0
  41. package/recipes/credits.account_credit.json +45 -0
  42. package/recipes/orders.refund_review.json +57 -0
  43. package/recipes/support.ticket_resolution.json +51 -0
  44. package/dist/bin.cjs +0 -13
package/docs/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Synapsor Runner Docs
2
+
3
+ Start with the README. Use these docs only when you need the next level of
4
+ detail.
5
+
6
+ ## First Setup
7
+
8
+ - [Connect Your Own Database](getting-started-own-database.md): inspect a
9
+ staging Postgres/MySQL database, generate `synapsor.runner.json`, preview
10
+ semantic tools, and serve them over MCP.
11
+ - [MCP Client Setup](mcp-client-setup.md): connect Claude, Cursor, VS Code, or
12
+ another stdio MCP client.
13
+ - [HTTP MCP](http-mcp.md): run Synapsor Runner as an authenticated HTTP MCP
14
+ service for app/server agents.
15
+
16
+ ## Safety And Operations
17
+
18
+ - [Security Boundary](security-boundary.md): what the model can and cannot see.
19
+ - [Current Limitations](limitations.md): current alpha scope.
20
+ - [Release Notes](release-notes.md): alpha behavior, breaking changes, and the
21
+ stable release policy.
22
+ - [Troubleshooting First Run](troubleshooting-first-run.md): common setup
23
+ failures and fixes.
24
+ - [Local Mode](local-mode.md): local store, proposals, approval, replay, and
25
+ writeback flow.
26
+
27
+ ## Features
28
+
29
+ - [MCP Audit](mcp-audit.md): static risk review for database MCP tools.
30
+ - [Recipes](recipes.md): starter business-capability templates.
31
+ - [Writeback Executors](writeback-executors.md): app-owned writeback handlers
32
+ for approved proposals.
33
+
34
+ The public docs intentionally stay small. Historical implementation reports,
35
+ release checklists, and internal planning notes are not part of the public
36
+ getting-started path.
@@ -0,0 +1,460 @@
1
+ # Connect Your Own Database
2
+
3
+ Use this path after the Docker demo passes and you want to try Synapsor Runner
4
+ against a staging or disposable Postgres/MySQL database.
5
+
6
+ Do not start with your most sensitive production database. The current alpha
7
+ runner is a local commit-safety runtime for reviewed single-row business
8
+ actions, not a production certification.
9
+
10
+ If you only ran `synapsor-runner demo --quick`, you have tested the fixture-only
11
+ teaching path and local ledger commands. This page is the real own-database
12
+ path: it inspects your Postgres/MySQL metadata and generates reviewed semantic
13
+ tools from your selections.
14
+
15
+ ## Fast path
16
+
17
+ Set one read-only database URL and run the public guided path:
18
+
19
+ ```bash
20
+ export DATABASE_URL="<postgres-or-mysql-read-url>"
21
+ npx -y -p @synapsor/runner@alpha synapsor-runner start --from-env DATABASE_URL
22
+ ```
23
+
24
+ That command does the useful local mini-Synapsor path:
25
+
26
+ ```text
27
+ inspect your schema
28
+ -> choose one table/view
29
+ -> choose trusted scope and visible fields
30
+ -> optionally choose proposal/writeback rules
31
+ -> generate synapsor.runner.json
32
+ -> preview MCP tools exposed to the model
33
+ -> run a local smoke check of the tool boundary
34
+ -> optionally write .synapsor/smoke-input.json for one real row
35
+ -> print mcp serve and local UI commands
36
+ ```
37
+
38
+ It does not print your database URL, put the URL in MCP client config, expose
39
+ `execute_sql`, expose approval/commit tools, or give the model write
40
+ credentials.
41
+
42
+ `start --from-env` is the shortest public command for first-run onboarding.
43
+ `onboard db --from-env DATABASE_URL` is the same explicit path if you prefer the
44
+ older command name in scripts.
45
+
46
+ During the wizard, provide the optional sample object id if you know one safe
47
+ row in the selected table. Runner writes `./.synapsor/smoke-input.json` with
48
+ that id and prints the exact `smoke call` command. If you skip it, use
49
+ `--json '{"<lookup_arg>":"<real_id>"}'` when you are ready to test one real
50
+ row.
51
+
52
+ The rest of this page shows the same flow step by step using the public
53
+ `synapsor-runner ...` CLI. From a source checkout, use `./bin/synapsor-runner ...` if the
54
+ global binary is not linked yet.
55
+
56
+ From a source checkout, `./scripts/use-your-db.sh` runs the same kind of
57
+ guided flow plus local repository checks.
58
+
59
+ ## 1. Put the read URL in an environment variable
60
+
61
+ Do not pass connection strings on the command line.
62
+
63
+ ```bash
64
+ export DATABASE_URL="<postgres-or-mysql-read-url>"
65
+ ```
66
+
67
+ Use a read-only credential for inspection and model-facing read/proposal tools.
68
+
69
+ ### TLS notes for AWS RDS and other managed databases
70
+
71
+ If you see an error like:
72
+
73
+ ```text
74
+ self-signed certificate in certificate chain
75
+ ```
76
+
77
+ that is not a Postgres permission issue. It means the client reached the
78
+ database, but your local Node/Postgres TLS stack could not verify the server's
79
+ certificate chain.
80
+
81
+ For disposable dev RDS fixtures, you can allow an insecure retry:
82
+
83
+ ```bash
84
+ ./scripts/use-your-db.sh --allow-insecure-ssl
85
+ ```
86
+
87
+ or put `sslmode=no-verify` in the disposable test URL.
88
+
89
+ For real staging or production-like testing, do not disable verification.
90
+ Install/use the AWS RDS CA bundle and keep certificate verification enabled.
91
+ For example, download the AWS RDS global bundle and configure your Postgres URL
92
+ or client environment so the Node Postgres client trusts that CA. The exact
93
+ mechanism depends on your deployment environment and should be treated like any
94
+ other trusted database TLS setup.
95
+
96
+ ## 2. Inspect metadata
97
+
98
+ ```bash
99
+ npx -y -p @synapsor/runner@alpha synapsor-runner inspect \
100
+ --engine auto \
101
+ --from-env DATABASE_URL \
102
+ --schema public
103
+ ```
104
+
105
+ For a disposable staging URL, this also works:
106
+
107
+ ```bash
108
+ npx -y -p @synapsor/runner@alpha synapsor-runner inspect "$DATABASE_URL" --engine auto --schema public
109
+ ```
110
+
111
+ For automation:
112
+
113
+ ```bash
114
+ npx -y -p @synapsor/runner@alpha synapsor-runner inspect \
115
+ --engine postgres \
116
+ --from-env DATABASE_URL \
117
+ --schema public \
118
+ --json > schema-inspection.json
119
+ ```
120
+
121
+ Inspection reads metadata only by default. It does not sample business rows.
122
+
123
+ ## 3. Start from a recipe when one matches
124
+
125
+ Recipes give you a reviewed starter contract for a common business action. They
126
+ do not silently infer write authority from your schema; you still map the recipe
127
+ to your staging table, primary key, tenant key, conflict column, visible fields,
128
+ allowed write fields, and business limits.
129
+
130
+ ```bash
131
+ npx -y -p @synapsor/runner@alpha synapsor-runner recipes list
132
+ npx -y -p @synapsor/runner@alpha synapsor-runner recipes show billing.late_fee_waiver
133
+ npx -y -p @synapsor/runner@alpha synapsor-runner recipes init billing.late_fee_waiver --output synapsor.runner.json
134
+ ```
135
+
136
+ Use a recipe when the shape is close. Use the guided wizard or explicit flags
137
+ when your action is custom.
138
+
139
+ Built-in recipes are JSON files under `recipes/`. You can copy one and pass the
140
+ edited file path to `recipes show` or `recipes init`; the runtime still serves
141
+ only the capabilities in your generated `synapsor.runner.json`.
142
+
143
+ ## 4. Generate from reviewed selections
144
+
145
+ In an interactive terminal, run the guided wizard:
146
+
147
+ ```bash
148
+ npx -y -p @synapsor/runner@alpha synapsor-runner init --from-env DATABASE_URL --mode read_only --wizard
149
+ ```
150
+
151
+ The generated context and capabilities are based on your selections. Synapsor
152
+ Runner does not force billing, support, order, or any other built-in domain.
153
+ The local shape is:
154
+
155
+ ```text
156
+ trusted context -> capability -> MCP tool
157
+ ```
158
+
159
+ You choose the source object, trusted tenant/principal bindings, namespace,
160
+ object name, lookup argument, visible fields, proposal fields, guards, and
161
+ approval role. You can also provide an optional real object id so Runner writes
162
+ `./.synapsor/smoke-input.json` for the first local tool call. If the read URL
163
+ env var and trusted tenant/principal env vars are already set, the wizard also
164
+ attempts that smoke call immediately and stores the evidence/query audit in the
165
+ local ledger. If not, it prints the exact smoke command to run after you set
166
+ the env vars from `.env.example`.
167
+
168
+ Start with `read_only` to prove safe database reads first. Use `--mode review`
169
+ when you are ready to create proposal tools and guarded writeback setup.
170
+
171
+ The wizard:
172
+
173
+ - asks for the engine and read URL environment-variable name;
174
+ - tests read connectivity through schema inspection;
175
+ - lists discovered schemas and tables/views;
176
+ - asks which source object backs the local context;
177
+ - asks which tenant/scope column and backend session env vars are trusted;
178
+ - asks you to confirm primary key, conflict/version column, visible columns,
179
+ mode, semantic capability names, and proposal patch mappings;
180
+ - asks review-mode users to choose direct guarded SQL writeback, an app-owned
181
+ HTTP handler, or an app-owned command handler;
182
+ - previews the MCP tools and what is not exposed;
183
+ - attempts a first smoke call when you supplied a real object id and the
184
+ required trusted env vars are present;
185
+ - writes the generated config, `.env.example`, and MCP client snippets only
186
+ after final confirmation.
187
+
188
+ For proposal modes, the current runner supports explicit field-update mappings such as:
189
+
190
+ ```text
191
+ late_fee_cents=fixed:0,waiver_reason=arg:reason
192
+ ```
193
+
194
+ Use `--starter` only when you intentionally want the old starter skeleton
195
+ instead of the reviewed wizard.
196
+
197
+ If you already know the reviewed table/action, generate config directly from
198
+ metadata and explicit flags:
199
+
200
+ ```bash
201
+ npx -y -p @synapsor/runner@alpha synapsor-runner init \
202
+ --from-env DATABASE_URL \
203
+ --engine postgres \
204
+ --schema public \
205
+ --table invoices \
206
+ --namespace billing \
207
+ --object-name invoice \
208
+ --mode review \
209
+ --visible-columns id,tenant_id,late_fee_cents,waiver_reason,updated_at \
210
+ --allowed-columns late_fee_cents,waiver_reason \
211
+ --patch-fixed late_fee_cents=0 \
212
+ --patch-from-arg waiver_reason=reason \
213
+ --numeric-bound late_fee_cents=0:5500 \
214
+ --write-url-env SYNAPSOR_DATABASE_WRITE_URL
215
+ ```
216
+
217
+ For app-owned writeback, replace the direct writer env with a handler executor:
218
+
219
+ ```bash
220
+ npx -y -p @synapsor/runner@alpha synapsor-runner init \
221
+ --from-env DATABASE_URL \
222
+ --engine postgres \
223
+ --schema public \
224
+ --table invoices \
225
+ --namespace billing \
226
+ --object-name invoice \
227
+ --mode review \
228
+ --patch-fixed late_fee_cents=0 \
229
+ --patch-from-arg waiver_reason=reason \
230
+ --writeback http_handler \
231
+ --handler-url-env APP_WRITEBACK_URL \
232
+ --handler-token-env APP_WRITEBACK_TOKEN
233
+ ```
234
+
235
+ Use `--writeback command_handler --handler-command-env APP_WRITEBACK_COMMAND`
236
+ when your app-owned writer is a local command/script instead of HTTP.
237
+
238
+ Or generate from a saved inspection snapshot without reconnecting:
239
+
240
+ ```bash
241
+ npx -y -p @synapsor/runner@alpha synapsor-runner init \
242
+ --inspection-json schema-inspection.json \
243
+ --table invoices \
244
+ --namespace billing \
245
+ --object-name invoice \
246
+ --mode review \
247
+ --patch-fixed late_fee_cents=0 \
248
+ --patch-from-arg waiver_reason=reason
249
+ ```
250
+
251
+ The command uses inspected metadata for primary-key, tenant-key, conflict-column,
252
+ and default-visible-column suggestions. If a suggestion is ambiguous or missing,
253
+ pass explicit flags such as `--primary-key`, `--tenant-key`, and
254
+ `--conflict-column`.
255
+
256
+ Review mode requires at least one explicit `--patch-fixed` or
257
+ `--patch-from-arg` mapping. Use `--mode read_only` if you only want an inspect
258
+ tool.
259
+
260
+ For bounded business actions, add reviewed value guards:
261
+
262
+ ```bash
263
+ --numeric-bound credit_cents=0:10000
264
+ --transition-guard status=open:pending_review
265
+ ```
266
+
267
+ `--numeric-bound` keeps a proposed numeric column inside a fixed range before a
268
+ proposal is created. `--transition-guard` keeps a status-like column on an
269
+ approved state path such as `open -> pending_review`. For multiple states, use
270
+ semicolon-separated paths, for example
271
+ `status=open:pending_review;pending_review:resolved`.
272
+
273
+ ## 5. Or create a reviewed selection spec
274
+
275
+ Create `onboarding-selection.json` from one table and one safe business action.
276
+
277
+ ```json
278
+ {
279
+ "version": 1,
280
+ "engine": "postgres",
281
+ "mode": "review",
282
+ "read_url_env": "SYNAPSOR_DATABASE_READ_URL",
283
+ "write_url_env": "SYNAPSOR_DATABASE_WRITE_URL",
284
+ "schema": "public",
285
+ "table": "invoices",
286
+ "primary_key": "id",
287
+ "tenant_key": "tenant_id",
288
+ "conflict_column": "updated_at",
289
+ "namespace": "billing",
290
+ "object_name": "invoice",
291
+ "visible_columns": ["id", "tenant_id", "late_fee_cents", "waiver_reason", "updated_at"],
292
+ "allowed_columns": ["late_fee_cents", "waiver_reason"],
293
+ "patch": {
294
+ "late_fee_cents": { "fixed": 0 },
295
+ "waiver_reason": { "from_arg": "reason" }
296
+ },
297
+ "numeric_bounds": {
298
+ "late_fee_cents": { "minimum": 0, "maximum": 5500 }
299
+ }
300
+ }
301
+ ```
302
+
303
+ The selection file contains reviewed metadata selections only. It must not
304
+ contain database URLs or passwords.
305
+
306
+ ## 6. Generate runner files
307
+
308
+ ```bash
309
+ npx -y -p @synapsor/runner@alpha synapsor-runner init \
310
+ --spec onboarding-selection.json \
311
+ --non-interactive
312
+ ```
313
+
314
+ This creates:
315
+
316
+ - `synapsor.runner.json`
317
+ - `.env.example`
318
+ - `.synapsor/mcp/generic-stdio.json`
319
+ - `.synapsor/mcp/claude-desktop.json`
320
+ - `.synapsor/mcp/cursor.json`
321
+ - `.synapsor/mcp/vscode.json`
322
+
323
+ Use `--force` only if you intentionally want to overwrite existing generated
324
+ files.
325
+
326
+ Generate or refresh MCP client snippets later with:
327
+
328
+ ```bash
329
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp config generic --config ./synapsor.runner.json --store ./.synapsor/local.db
330
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp config claude-desktop --config ./synapsor.runner.json --store ./.synapsor/local.db
331
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp config cursor --config ./synapsor.runner.json --store ./.synapsor/local.db
332
+ npx -y -p @synapsor/runner@alpha synapsor-runner tools preview --config ./synapsor.runner.json --store ./.synapsor/local.db
333
+ ```
334
+
335
+ Call one generated tool locally before wiring an MCP client:
336
+
337
+ ```bash
338
+ npx -y -p @synapsor/runner@alpha synapsor-runner smoke call --config ./synapsor.runner.json --store ./.synapsor/local.db
339
+ ```
340
+
341
+ `smoke call` uses the same runtime as the MCP server. It records evidence and
342
+ query audit for read tools, or creates a proposal for proposal tools, then
343
+ prints the commands to inspect evidence/proposals/replay from the local store.
344
+
345
+ The snippets contain the local command and args. They must not contain database
346
+ URLs, passwords, approval tools, commit tools, or write credentials.
347
+
348
+ ## 7. Validate the config
349
+
350
+ ```bash
351
+ npx -y -p @synapsor/runner@alpha synapsor-runner config validate --config synapsor.runner.json
352
+ npx -y -p @synapsor/runner@alpha synapsor-runner config show --config synapsor.runner.json --redacted
353
+ ```
354
+
355
+ The config stores environment-variable names, not connection-string values.
356
+
357
+ Run doctor after setting the referenced environment variables:
358
+
359
+ ```bash
360
+ npx -y -p @synapsor/runner@alpha synapsor-runner doctor --config synapsor.runner.json
361
+ ```
362
+
363
+ Doctor validates config shape, trusted context env vars, source env vars,
364
+ read/write credential separation, table/column metadata when the read URL is
365
+ available, and the semantic MCP tool boundary. Use JSON for automation:
366
+
367
+ ```bash
368
+ npx -y -p @synapsor/runner@alpha synapsor-runner doctor --config synapsor.runner.json --json
369
+ ```
370
+
371
+ ## 8. Serve semantic MCP tools
372
+
373
+ Use stdio when a local MCP client can launch Synapsor Runner:
374
+
375
+ ```bash
376
+ export SYNAPSOR_TENANT_ID="acme"
377
+ export SYNAPSOR_PRINCIPAL="local_operator"
378
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp serve --config ./synapsor.runner.json --store ./.synapsor/local.db
379
+ ```
380
+
381
+ Use Streamable HTTP when an app/server agent connects through a standard HTTP
382
+ MCP client:
383
+
384
+ ```bash
385
+ export SYNAPSOR_TENANT_ID="acme"
386
+ export SYNAPSOR_PRINCIPAL="local_operator"
387
+ export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
388
+
389
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp serve-streamable-http \
390
+ --config ./synapsor.runner.json \
391
+ --store ./.synapsor/local.db \
392
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
393
+ ```
394
+
395
+ Streamable HTTP defaults to `127.0.0.1:8766` and requires bearer auth by
396
+ default. Use private networking/TLS before exposing it beyond localhost. See
397
+ [HTTP MCP](http-mcp.md). If you want the smaller JSON-RPC bridge instead, use
398
+ `synapsor-runner mcp serve-http`.
399
+
400
+ The model-facing MCP server exposes semantic tools such as:
401
+
402
+ ```text
403
+ billing.inspect_invoice
404
+ billing.propose_invoice_update
405
+ ```
406
+
407
+ Those names come from the example namespace/object. A custom setup might expose
408
+ `clinic.inspect_appointment` and `clinic.propose_appointment_update`, or any
409
+ other reviewed names you generated. It does not expose `execute_sql`, approval
410
+ tools, commit tools, database URLs, write credentials, or tenant authority.
411
+
412
+ ## 9. Review and apply outside MCP
413
+
414
+ Proposal tools leave the source database unchanged. Review locally:
415
+
416
+ ```bash
417
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals list --store ./.synapsor/local.db
418
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals show wrp_123 --store ./.synapsor/local.db
419
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals approve wrp_123 --store ./.synapsor/local.db --actor local_reviewer --yes
420
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals writeback-job wrp_123 --store ./.synapsor/local.db --output job.json
421
+ ```
422
+
423
+ Apply through the trusted worker path with a separate writer credential:
424
+
425
+ ```bash
426
+ export SYNAPSOR_DATABASE_WRITE_URL="<postgres-or-mysql-write-url>"
427
+ SYNAPSOR_ENGINE=postgres \
428
+ npx -y -p @synapsor/runner@alpha synapsor-runner apply --job job.json --config synapsor.runner.json --store ./.synapsor/local.db
429
+ ```
430
+
431
+ For `apply --job ... --config ...`, Runner reads the write credential from the
432
+ source `write_url_env` in `synapsor.runner.json`, such as
433
+ `SYNAPSOR_DATABASE_WRITE_URL`. `SYNAPSOR_DATABASE_URL` is accepted only as a
434
+ legacy fallback for direct worker flows that do not pass a local config.
435
+
436
+ If your application/API should own the business write, use an `http_handler`
437
+ executor instead of direct SQL writeback. Handler URLs and bearer tokens come
438
+ from environment variables, and the handler receives a structured proposal/job
439
+ payload, not arbitrary model SQL:
440
+
441
+ ```bash
442
+ npx -y -p @synapsor/runner@alpha synapsor-runner apply --proposal wrp_123 --config synapsor.runner.json --store ./.synapsor/local.db
443
+ ```
444
+
445
+ See [Writeback Executors](writeback-executors.md).
446
+
447
+ Replay afterward:
448
+
449
+ ```bash
450
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay show wrp_123 --store ./.synapsor/local.db
451
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay export wrp_123 --store ./.synapsor/local.db --output replay.json
452
+ ```
453
+
454
+ ## Boundary
455
+
456
+ MCP tool call equals request/proposal authority. Trusted runner equals
457
+ execution authority.
458
+
459
+ If tenant scope, primary key, allowed columns, expected version, approval state,
460
+ or local config cannot be verified, do not apply the write.