@synapsor/runner 0.1.0-alpha.1 → 0.1.0-alpha.11

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 (66) hide show
  1. package/README.md +426 -19
  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 +12958 -0
  7. package/docs/README.md +53 -0
  8. package/docs/app-owned-executors.md +21 -0
  9. package/docs/cloud-mode.md +24 -0
  10. package/docs/current-scope.md +24 -0
  11. package/docs/dependency-license-inventory.md +35 -0
  12. package/docs/getting-started-own-database.md +460 -0
  13. package/docs/http-mcp.md +276 -0
  14. package/docs/licensing.md +36 -0
  15. package/docs/limitations.md +95 -0
  16. package/docs/local-mode.md +351 -0
  17. package/docs/mcp-audit.md +152 -0
  18. package/docs/mcp-client-setup.md +270 -0
  19. package/docs/openai-agents-sdk.md +57 -0
  20. package/docs/recipes.md +61 -0
  21. package/docs/release-notes.md +158 -0
  22. package/docs/security-boundary.md +94 -0
  23. package/docs/troubleshooting-first-run.md +248 -0
  24. package/docs/use-your-own-database.md +18 -0
  25. package/docs/writeback-executors.md +220 -0
  26. package/examples/app-owned-writeback/README.md +120 -0
  27. package/examples/app-owned-writeback/business-actions.md +221 -0
  28. package/examples/app-owned-writeback/command-handler.mjs +46 -0
  29. package/examples/app-owned-writeback/node-fastify-handler.mjs +55 -0
  30. package/examples/app-owned-writeback/python-fastapi-handler.py +57 -0
  31. package/examples/dangerous-mcp-tools.json +88 -0
  32. package/examples/mcp-postgres-billing-app-handler/README.md +82 -0
  33. package/examples/mcp-postgres-billing-app-handler/app-handler.mjs +197 -0
  34. package/examples/mcp-postgres-billing-app-handler/docker-compose.yml +13 -0
  35. package/examples/mcp-postgres-billing-app-handler/schema.sql +59 -0
  36. package/examples/mcp-postgres-billing-app-handler/scripts/run-demo.sh +99 -0
  37. package/examples/mcp-postgres-billing-app-handler/seed.sql +39 -0
  38. package/examples/mcp-postgres-billing-app-handler/synapsor.runner.json +157 -0
  39. package/examples/openai-agents-http/README.md +64 -0
  40. package/examples/openai-agents-http/agent.py +54 -0
  41. package/examples/openai-agents-http/requirements.txt +1 -0
  42. package/examples/openai-agents-stdio/README.md +66 -0
  43. package/examples/openai-agents-stdio/agent.py +72 -0
  44. package/examples/openai-agents-stdio/requirements.txt +1 -0
  45. package/examples/reference-support-billing-app/README.md +137 -0
  46. package/examples/reference-support-billing-app/docker-compose.yml +13 -0
  47. package/examples/reference-support-billing-app/mcp-client.generic.json +11 -0
  48. package/examples/reference-support-billing-app/schema.sql +68 -0
  49. package/examples/reference-support-billing-app/scripts/run-demo.sh +7 -0
  50. package/examples/reference-support-billing-app/seed.sql +33 -0
  51. package/examples/reference-support-billing-app/synapsor.runner.json +241 -0
  52. package/fixtures/benchmark/mcp-efficiency.json +53 -0
  53. package/fixtures/benchmark/mcp-efficiency.txt +25 -0
  54. package/fixtures/protocol/MANIFEST.json +54 -0
  55. package/fixtures/protocol/change-set.late-fee-waiver.v1.json +72 -0
  56. package/fixtures/protocol/execution-receipt.applied.v1.json +14 -0
  57. package/fixtures/protocol/execution-receipt.conflict.v1.json +15 -0
  58. package/fixtures/protocol/runner-registration.v1.json +22 -0
  59. package/fixtures/protocol/writeback-job.late-fee-waiver.v1.json +44 -0
  60. package/package.json +14 -4
  61. package/recipes/accounts.trial_extension.json +42 -0
  62. package/recipes/billing.late_fee_waiver.json +46 -0
  63. package/recipes/credits.account_credit.json +45 -0
  64. package/recipes/orders.refund_review.json +57 -0
  65. package/recipes/support.ticket_resolution.json +51 -0
  66. package/dist/bin.cjs +0 -13
@@ -0,0 +1,351 @@
1
+ # Local mode
2
+
3
+ Local mode runs Synapsor Runner inside the developer or customer environment. No Synapsor Cloud account is required for local review flows.
4
+
5
+ Command model:
6
+
7
+ - `./scripts/demo-docker.sh` runs the no-install Docker demo.
8
+ - `synapsor <command>` is the public CLI surface.
9
+ - From a source checkout, use `./bin/synapsor-runner <command>` if the global binary is
10
+ not linked yet.
11
+
12
+ `synapsor-runner demo --quick` creates a fixture ledger for learning and CLI
13
+ verification. In a terminal it walks through the safety model step by step; in
14
+ CI or piped output it prints a short summary and exits without waiting for
15
+ Enter. It does not read or write external Postgres/MySQL. Local mode with
16
+ `synapsor-runner mcp serve`, `synapsor-runner demo`, or an own generated config is the real
17
+ Postgres/MySQL path. Use `synapsor-runner demo inspect` to print the follow-up
18
+ commands for the quick-demo fixture.
19
+
20
+ Current local-mode foundation:
21
+
22
+ - strict JSON capability config validation in `packages/config`;
23
+ - local SQLite proposal/event/evidence/query-audit/writeback/replay store in `packages/proposal-store`;
24
+ - local proposal review CLI in `apps/runner`;
25
+ - local localhost proposal review UI through `synapsor-runner ui`;
26
+ - static MCP database risk review with `synapsor-runner audit`;
27
+ - local stdio MCP server with semantic read/proposal tools;
28
+ - authenticated HTTP MCP server for app/server agents;
29
+ - MCP resource reads for `synapsor://proposals/*`, `synapsor://evidence/*`, and `synapsor://replay/*`;
30
+ - local approved proposal to `synapsor.writeback-job.v1` job generation;
31
+ - guarded Postgres/MySQL writeback adapters for approved structured jobs.
32
+
33
+ Still pending:
34
+
35
+ The alpha package requires Node >= 22.5.0 because the local evidence/replay
36
+ ledger uses Node's `node:sqlite` runtime. The published package declares that
37
+ engine requirement and the CLI exits early with a clear message on older Node
38
+ versions. The Docker-backed demo remains the recommended path when you do not
39
+ want to change your host Node version.
40
+
41
+ ## Initialize a config
42
+
43
+ Create a starter config without putting credentials in the file:
44
+
45
+ ```bash
46
+ npx -y -p @synapsor/runner@alpha synapsor-runner init --engine postgres --mode review
47
+ ```
48
+
49
+ For MySQL:
50
+
51
+ ```bash
52
+ npx -y -p @synapsor/runner@alpha synapsor-runner init --engine mysql --mode review --output synapsor.mysql.runner.json
53
+ ```
54
+
55
+ The generated config uses environment-variable names for read/write URLs and trusted context. Edit the table, column, and capability names before serving tools.
56
+
57
+ Do not include credential-bearing columns in reviewed visible fields or
58
+ evidence. The local store rejects obvious database URLs, bearer tokens, runner
59
+ tokens, private-key blocks, and secret-like field names before they can be
60
+ persisted into proposals, evidence, query audit, runner state, or replay.
61
+
62
+ For a reviewed own-database setup generated from explicit selections, use:
63
+
64
+ ```bash
65
+ npx -y -p @synapsor/runner@alpha synapsor-runner init --spec onboarding-selection.json --non-interactive
66
+ npx -y -p @synapsor/runner@alpha synapsor-runner doctor --config synapsor.runner.json
67
+ ```
68
+
69
+ `doctor --config` checks config validation, required environment variables,
70
+ read/write credential separation, metadata visibility for configured targets,
71
+ and the semantic MCP tool boundary without printing credential values.
72
+
73
+ ## Stdio Vs HTTP MCP
74
+
75
+ Use stdio for local MCP clients that launch Synapsor Runner:
76
+
77
+ ```bash
78
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp serve \
79
+ --config ./synapsor.runner.json \
80
+ --store ./.synapsor/local.db
81
+ ```
82
+
83
+ Use Streamable HTTP when your app/server agent connects through a standard HTTP
84
+ MCP client:
85
+
86
+ ```bash
87
+ export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
88
+
89
+ npx -y -p @synapsor/runner@alpha synapsor-runner mcp serve-streamable-http \
90
+ --config ./synapsor.runner.json \
91
+ --store ./.synapsor/local.db \
92
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
93
+ ```
94
+
95
+ Streamable HTTP defaults to `127.0.0.1:8766`, requires bearer auth by default,
96
+ and should run behind private networking/TLS before production-like exposure.
97
+ Use `synapsor-runner mcp serve-http` only when you explicitly want the smaller
98
+ JSON-RPC bridge. Details: [HTTP MCP](http-mcp.md).
99
+
100
+ ## Local safety modes
101
+
102
+ The local runner modes are intentionally narrow:
103
+
104
+ - `read_only`: exposes read tools only; proposal tools fail closed.
105
+ - `shadow`: lets proposal tools create local proposals, evidence, query audit, and replay records, but approval and writeback-job creation are disabled.
106
+ - `review`: lets proposal tools create local proposals, then a human/operator can approve and create a guarded writeback job.
107
+ - `cloud`: delegates reviewed tools to Synapsor Cloud through a runner token.
108
+
109
+ Use `shadow` when you want to test the shape of proposals without any path to mutate the source database. Use `review` only when you are ready to exercise the trusted writeback worker.
110
+
111
+ ## Store path
112
+
113
+ Commands use `--store` or `SYNAPSOR_LOCAL_STORE`.
114
+
115
+ ```bash
116
+ export SYNAPSOR_LOCAL_STORE="./.synapsor/local.db"
117
+ ```
118
+
119
+ If neither is set, the CLI uses:
120
+
121
+ ```text
122
+ ./.synapsor/local.db
123
+ ```
124
+
125
+ ## Proposal review
126
+
127
+ List proposals:
128
+
129
+ ```bash
130
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals list --store ./.synapsor/local.db
131
+ ```
132
+
133
+ Show a proposal:
134
+
135
+ ```bash
136
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals show wrp_123 --store ./.synapsor/local.db
137
+ ```
138
+
139
+ Approve:
140
+
141
+ ```bash
142
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals approve wrp_123 \
143
+ --store ./.synapsor/local.db \
144
+ --actor local_reviewer \
145
+ --yes
146
+ ```
147
+
148
+ Before approval, the CLI prints the reviewer-critical proposal details: trusted principal, tenant, target row, primary key, required role, proposal hash/version, allowed columns, conflict guard, evidence bundle/query fingerprint, writeback boundary, source mutation state, and exact before/after diff. Interactive approval still requires typing `yes`; noninteractive scripts must pass `--yes`.
149
+
150
+ Create a guarded writeback job from an approved proposal:
151
+
152
+ ```bash
153
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals writeback-job wrp_123 \
154
+ --store ./.synapsor/local.db \
155
+ --project local \
156
+ --runner local_runner \
157
+ --output job.json
158
+ ```
159
+
160
+ The generated job uses the public `synapsor.writeback-job.v1` protocol and can be applied by the guarded worker:
161
+
162
+ ```bash
163
+ export SYNAPSOR_DATABASE_WRITE_URL="postgresql://writer:<password>@localhost:5432/app"
164
+ SYNAPSOR_ENGINE=postgres \
165
+ npx -y -p @synapsor/runner@alpha synapsor-runner apply --job job.json --config synapsor.runner.json --store ./.synapsor/local.db
166
+ ```
167
+
168
+ When `--config` is passed, direct SQL writeback reads the writer connection from
169
+ the source `write_url_env` in that config. `SYNAPSOR_DATABASE_URL` is only a
170
+ legacy fallback for direct worker flows without a local runner config.
171
+
172
+ Passing `--store` records the terminal `synapsor.execution-receipt.v1` locally. Replay then links the proposal, approval, writeback job, applied/conflict/failed receipt, evidence, and query audit.
173
+
174
+ Reject:
175
+
176
+ ```bash
177
+ npx -y -p @synapsor/runner@alpha synapsor-runner proposals reject wrp_123 \
178
+ --store ./.synapsor/local.db \
179
+ --reason "policy evidence is incomplete" \
180
+ --yes
181
+ ```
182
+
183
+ `approve` and `reject` require either interactive confirmation or explicit `--yes`.
184
+
185
+ Approval records the approver against the exact proposal hash/version. The proposal patch is immutable after creation.
186
+
187
+ Shadow-mode proposals are inspectable through `proposals show` and `replay show`, but `proposals approve` and `proposals writeback-job` reject them. Shadow mode never mutates Postgres/MySQL.
188
+
189
+ ## Browser review UI
190
+
191
+ Start a localhost-only review UI:
192
+
193
+ ```bash
194
+ npx -y -p @synapsor/runner@alpha synapsor-runner ui --config synapsor.runner.json --store ./.synapsor/local.db
195
+ ```
196
+
197
+ The UI shows setup summary, semantic tools, proposal states, exact diffs,
198
+ evidence, approval state, receipts, and replay. It binds to `127.0.0.1` by
199
+ default, uses a per-run local session token, and requires CSRF protection for
200
+ approve/reject actions.
201
+
202
+ The UI does not expose raw SQL, database URLs, password values, MCP approval
203
+ tools, MCP commit tools, or controls that widen configured tables/columns.
204
+
205
+ ## Replay
206
+
207
+ Show replay:
208
+
209
+ ```bash
210
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay show wrp_123 --store ./.synapsor/local.db
211
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay show --proposal wrp_123 --store ./.synapsor/local.db
212
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay show --replay replay_wrp_123 --store ./.synapsor/local.db
213
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay show --evidence ev_123 --store ./.synapsor/local.db
214
+ ```
215
+
216
+ Export replay:
217
+
218
+ ```bash
219
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay export wrp_123 \
220
+ --store ./.synapsor/local.db \
221
+ --output replay.json
222
+
223
+ npx -y -p @synapsor/runner@alpha synapsor-runner replay export --proposal wrp_123 \
224
+ --format markdown \
225
+ --store ./.synapsor/local.db \
226
+ --output replay.md
227
+ ```
228
+
229
+ Replay records include proposal metadata, before/after diff, events, writeback receipts, evidence summaries, and query audit rows currently stored for the proposal.
230
+ Human output is concise by default. Use `--details` for reviewer metadata or
231
+ `--json` for complete machine-readable records.
232
+
233
+ ## Local evidence, query audit, and receipts
234
+
235
+ The local SQLite store is also searchable without relying on `latest`:
236
+
237
+ ```bash
238
+ synapsor-runner activity search \
239
+ --tenant acme \
240
+ --object invoice:INV-3001 \
241
+ --store ./.synapsor/local.db
242
+
243
+ synapsor-runner evidence list \
244
+ --tenant acme \
245
+ --capability billing.inspect_invoice \
246
+ --source app_postgres \
247
+ --table invoices \
248
+ --store ./.synapsor/local.db
249
+
250
+ synapsor-runner evidence show ev_123 --store ./.synapsor/local.db
251
+ synapsor-runner query-audit list --evidence ev_123 --store ./.synapsor/local.db
252
+ synapsor-runner receipts list --proposal wrp_123 --store ./.synapsor/local.db
253
+ synapsor-runner receipts show <receipt_id> --store ./.synapsor/local.db
254
+ ```
255
+
256
+ Default inspection output is intentionally short. Add `--details` when you need
257
+ target URIs, primary keys, proposal hash/version, conflict guards, query
258
+ fingerprints, event timestamps, or receipt internals.
259
+
260
+ Read-only MCP tools record evidence bundles and query-audit rows and return an
261
+ evidence handle. Use `evidence show`, `evidence list`, and `query-audit list`
262
+ to inspect those captured rows and fingerprints later without rerunning the
263
+ external database read.
264
+
265
+ This is local indexed search over the runner's SQLite ledger. It is not
266
+ external Postgres/MySQL time travel, not native branching, and not a hosted
267
+ cross-runner audit ledger.
268
+
269
+ ## Local store maintenance
270
+
271
+ The local ledger is a developer/staging SQLite file. You can inspect, compact,
272
+ or prune it without touching your source Postgres/MySQL database:
273
+
274
+ ```bash
275
+ synapsor-runner store stats --store ./.synapsor/local.db
276
+ synapsor-runner store vacuum --store ./.synapsor/local.db
277
+ synapsor-runner store prune --store ./.synapsor/local.db --older-than 30d --dry-run
278
+ synapsor-runner store prune --store ./.synapsor/local.db --older-than 30d --yes
279
+ ```
280
+
281
+ `store prune` defaults to dry-run. Use `--yes` only after reviewing the row
282
+ counts it will remove.
283
+
284
+ ## Boundary
285
+
286
+ Local mode does not expose `approve_proposal` or `commit_proposal` as model-callable MCP tools. The intended flow is:
287
+
288
+ ```text
289
+ MCP tool call
290
+ -> reviewed semantic proposal
291
+ -> local store
292
+ -> human/operator approval outside the model
293
+ -> guarded worker writeback
294
+ -> terminal receipt
295
+ -> replay
296
+ ```
297
+
298
+ The external Postgres/MySQL database is not physically branched. It remains unchanged until a trusted runner applies an approved writeback job.
299
+
300
+ ## Local MCP smoke
301
+
302
+ The repository includes a one-command Docker-only local demo:
303
+
304
+ ```bash
305
+ ./scripts/demo-docker.sh
306
+ ```
307
+
308
+ This path requires Docker only. It builds the runner image locally, starts disposable Postgres/MySQL fixtures, runs the stdio MCP proof, and tears down the disposable resources. No Synapsor Cloud account, API key, hosted workspace, or host Node/Corepack setup is required.
309
+
310
+ If you already have Node/Corepack installed for contributor work, the direct wrapper is also available:
311
+
312
+ ```bash
313
+ ./scripts/demo-local.sh
314
+ corepack pnpm demo:local
315
+ ```
316
+
317
+ The Docker-only script is also available through pnpm after dependencies are installed:
318
+
319
+ ```bash
320
+ corepack pnpm demo:docker
321
+ ```
322
+
323
+ The contributor script checks Docker/Corepack, installs dependencies if needed, starts disposable Postgres/MySQL containers, and runs the stdio MCP proof flow.
324
+
325
+ For CI or direct verification, use:
326
+
327
+ ```bash
328
+ corepack pnpm test:mcp-local
329
+ ```
330
+
331
+ It launches the official MCP stdio client transport against `synapsor-runner mcp serve`, exercises the Postgres billing, Postgres support, and MySQL orders examples, checks that source rows are unchanged before approval, approves locally, generates versioned writeback jobs, applies them, retries idempotently, and then proves stale-row conflict:
332
+
333
+ ```text
334
+ The business state changed after the agent saw it, so Synapsor refused to commit.
335
+ ```
336
+
337
+ ## Optional MCP client configs
338
+
339
+ After the Docker demo passes, developers who want to attach an MCP client can use the checked-in stdio config shapes in:
340
+
341
+ ```text
342
+ examples/mcp-client-configs/
343
+ ```
344
+
345
+ Verify those config files without launching any client UI:
346
+
347
+ ```bash
348
+ corepack pnpm test:mcp-client-configs
349
+ ```
350
+
351
+ That command starts the runner through each config shape, calls MCP `tools/list`, and verifies that the server exposes semantic tools such as `billing.inspect_invoice` and `billing.propose_late_fee_waiver` without exposing raw SQL, approval, or commit tools. It verifies the stdio contract and config shape; it does not claim that a specific client application's UI was manually tested.
@@ -0,0 +1,152 @@
1
+ # MCP database risk review
2
+
3
+ `npx -y -p @synapsor/runner@alpha synapsor-runner audit <target>` performs a
4
+ static MCP database risk review over an exported tool manifest, a remote MCP
5
+ `tools/list` endpoint, or a stdio MCP server. The `mcp audit` subcommand is also
6
+ available for users who look for the command under the MCP namespace.
7
+
8
+ From a source checkout, use `./bin/synapsor-runner ...` if the global binary is not
9
+ linked yet.
10
+
11
+ It does not call business tools. It only inspects names, descriptions, input schemas, output schemas, annotations, and examples when those are present.
12
+
13
+ Every report includes this disclaimer:
14
+
15
+ ```text
16
+ This is a static risk review, not proof that an MCP server is secure.
17
+ ```
18
+
19
+ MCP annotations are treated as hints, not enforcement.
20
+
21
+ ## Usage
22
+
23
+ Built-in database MCP risk example:
24
+
25
+ ```bash
26
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit --example dangerous-db-mcp
27
+ ```
28
+
29
+ This bundled example does not require a source checkout or local examples file.
30
+ It audits a deliberately risky database MCP shape with `execute_sql`,
31
+ `run_query`, model-callable approval/update/delete tools, arbitrary
32
+ table/column inputs, and model-controlled tenant/principal fields.
33
+
34
+ Human-readable output:
35
+
36
+ ```bash
37
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit ./tools-list.json
38
+ ```
39
+
40
+ Remote `tools/list` endpoint with a bearer token kept in the environment:
41
+
42
+ ```bash
43
+ SYNAPSOR_MCP_AUDIT_BEARER="..." \
44
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit https://mcp.example.com --format json
45
+ ```
46
+
47
+ Remote endpoint with a custom bearer-token environment variable:
48
+
49
+ ```bash
50
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit https://mcp.example.com --bearer-env MCP_AUDIT_TOKEN --format json
51
+ ```
52
+
53
+ Stdio MCP server:
54
+
55
+ ```bash
56
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit 'stdio:node ./server.mjs' --timeout-ms 5000
57
+ ```
58
+
59
+ JSON output:
60
+
61
+ ```bash
62
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit ./tools-list.json --format json
63
+ ```
64
+
65
+ Markdown output for issues, PRs, or security review notes:
66
+
67
+ ```bash
68
+ npx -y -p @synapsor/runner@alpha synapsor-runner audit --example dangerous-db-mcp --format markdown
69
+ ```
70
+
71
+ During local development, the repo-local wrapper can run the same command:
72
+
73
+ ```bash
74
+ ./bin/synapsor-runner audit ./tools-list.json
75
+ ./bin/synapsor-runner audit ./tools-list.json --format json
76
+ ```
77
+
78
+ ## Supported inputs
79
+
80
+ The audit accepts common exported shapes:
81
+
82
+ ```json
83
+ { "tools": [] }
84
+ ```
85
+
86
+ ```json
87
+ { "result": { "tools": [] } }
88
+ ```
89
+
90
+ ```json
91
+ { "data": { "tools": [] } }
92
+ ```
93
+
94
+ It also scans nested `adapter`, `mcpServers`, and `servers` blocks when they include tool metadata.
95
+
96
+ For live targets, the audit calls only JSON-RPC `tools/list`. It does not call business tools, approval tools, commit tools, or writeback tools.
97
+
98
+ ## Findings
99
+
100
+ The audit flags database-commit risks such as:
101
+
102
+ - generic `execute_sql`, `run_query`, or raw SQL tools;
103
+ - tools accepting arbitrary SQL, schema, table, or column identifiers;
104
+ - tools accepting `tenant_id`, `principal`, source ids, allowed columns, row versions, or approval identity as model input;
105
+ - model-callable approval, commit, apply, settle, merge, or writeback tools;
106
+ - write-like tools with no visible proposal, approval, or guarded-writeback boundary;
107
+ - missing structured output schemas;
108
+ - missing idempotency/request-key metadata for direct write-like tools;
109
+ - missing row-version/conflict-guard metadata for direct write-like tools;
110
+ - ambiguous read/write tool boundaries;
111
+ - missing business descriptions, annotations, or fixture examples.
112
+
113
+ ## Recommended target shape
114
+
115
+ A safer model-facing database MCP tool should look like a reviewed semantic proposal capability:
116
+
117
+ ```json
118
+ {
119
+ "name": "billing.propose_late_fee_waiver",
120
+ "description": "Create an evidence-backed proposal for support lead approval before trusted writeback.",
121
+ "inputSchema": {
122
+ "type": "object",
123
+ "properties": {
124
+ "invoice_id": { "type": "string" },
125
+ "reason": { "type": "string" }
126
+ },
127
+ "required": ["invoice_id", "reason"]
128
+ },
129
+ "outputSchema": {
130
+ "type": "object",
131
+ "properties": {
132
+ "status": { "type": "string" },
133
+ "proposal_id": { "type": "string" },
134
+ "evidence_bundle_id": { "type": "string" },
135
+ "source_database_changed": { "type": "boolean" }
136
+ },
137
+ "required": ["status", "proposal_id", "source_database_changed"]
138
+ },
139
+ "annotations": {
140
+ "readOnlyHint": false,
141
+ "destructiveHint": false
142
+ },
143
+ "examples": [
144
+ {
145
+ "invoice_id": "INV-3001",
146
+ "reason": "customer requested review"
147
+ }
148
+ ]
149
+ }
150
+ ```
151
+
152
+ Trusted values such as tenant, principal, source, allowed columns, approval identity, row-version guard, and database credentials must come from Synapsor/runner context, not from model-facing arguments.