@synapsor/runner 1.5.0 → 1.5.4

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 (83) hide show
  1. package/CHANGELOG.md +134 -3
  2. package/README.md +108 -113
  3. package/dist/authoring.d.ts +23 -0
  4. package/dist/authoring.d.ts.map +1 -0
  5. package/dist/authoring.mjs +1318 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +4 -5
  8. package/dist/local-ui.d.ts +14 -0
  9. package/dist/local-ui.d.ts.map +1 -1
  10. package/dist/runner.mjs +12334 -5256
  11. package/dist/runtime.mjs +817 -149
  12. package/dist/shadow.d.ts +36 -0
  13. package/dist/shadow.d.ts.map +1 -0
  14. package/dist/shadow.mjs +5362 -0
  15. package/docs/README.md +25 -2
  16. package/docs/alternatives.md +122 -0
  17. package/docs/capability-authoring.md +49 -1
  18. package/docs/client-recipes.md +218 -0
  19. package/docs/cloud-mode.md +18 -0
  20. package/docs/contract-testing.md +9 -7
  21. package/docs/cursor-plugin.md +78 -0
  22. package/docs/database-enforced-scope.md +8 -0
  23. package/docs/dsl-reference.md +45 -4
  24. package/docs/effect-regression.md +39 -2
  25. package/docs/fresh-developer-usability.md +110 -0
  26. package/docs/getting-started-own-database.md +102 -5
  27. package/docs/guarded-crud-writeback.md +10 -1
  28. package/docs/host-compatibility.md +59 -0
  29. package/docs/http-mcp.md +222 -207
  30. package/docs/limitations.md +9 -2
  31. package/docs/local-mode.md +11 -7
  32. package/docs/mcp-audit.md +166 -2
  33. package/docs/mcp-client-setup.md +28 -11
  34. package/docs/mcp-clients.md +43 -8
  35. package/docs/openai-agents-sdk.md +16 -2
  36. package/docs/oss-vs-cloud.md +3 -0
  37. package/docs/production.md +72 -7
  38. package/docs/release-notes.md +131 -4
  39. package/docs/runner-bundles.md +7 -2
  40. package/docs/runner-config-reference.md +96 -6
  41. package/docs/running-a-runner-fleet.md +42 -13
  42. package/docs/security-boundary.md +58 -8
  43. package/docs/shadow-studies.md +47 -4
  44. package/docs/store-lifecycle.md +93 -5
  45. package/docs/troubleshooting-first-run.md +46 -0
  46. package/examples/openai-agents-http/README.md +10 -4
  47. package/examples/openai-agents-http/agent.py +2 -2
  48. package/examples/runner-fleet/Dockerfile +7 -2
  49. package/examples/runner-fleet/README.md +11 -7
  50. package/examples/runner-fleet/docker-compose.yml +4 -4
  51. package/examples/runner-fleet/mint-dev-token.mjs +1 -1
  52. package/examples/runner-fleet/start-tls-runner.sh +21 -0
  53. package/examples/runner-fleet/synapsor.runner.json +27 -1
  54. package/examples/support-billing-agent/README.md +18 -0
  55. package/examples/support-billing-agent/app/README.md +6 -0
  56. package/examples/support-billing-agent/app/contract.ts +28 -0
  57. package/examples/support-billing-agent/app/effect-adapter.mjs +23 -0
  58. package/examples/support-billing-agent/app/record-shadow-outcomes.mjs +44 -0
  59. package/examples/support-billing-agent/scripts/run-evaluation.sh +6 -5
  60. package/examples/support-plan-credit/README.md +54 -3
  61. package/examples/support-plan-credit/mcp-client-examples/README.md +23 -0
  62. package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +34 -0
  63. package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +24 -0
  64. package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +35 -0
  65. package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.json +4 -1
  66. package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.mjs +31 -0
  67. package/examples/support-plan-credit/mcp-client-examples/google-adk.py +40 -0
  68. package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +29 -0
  69. package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +36 -0
  70. package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
  71. package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +5 -1
  72. package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +20 -0
  73. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.contract-tests.generated.json +221 -0
  74. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.ts +34 -0
  75. package/examples/support-plan-credit/synapsor.contract.json +0 -3
  76. package/fixtures/mcp-audit/README.md +14 -0
  77. package/fixtures/mcp-audit/cursor-bypass-config.json +38 -0
  78. package/fixtures/mcp-audit/dangerous-tools-list.json +33 -0
  79. package/fixtures/mcp-audit/reviewed-proposal-tools-list.json +60 -0
  80. package/package.json +18 -3
  81. package/schemas/mcp-audit-report.schema.json +100 -1
  82. package/schemas/synapsor.contract-tests.schema.json +1 -1
  83. package/schemas/synapsor.runner.schema.json +80 -0
@@ -0,0 +1,23 @@
1
+ # Support Plan Credit MCP Recipes
2
+
3
+ These files connect different MCP hosts and frameworks to the same reviewed
4
+ Runner tools:
5
+
6
+ - `support.inspect_customer`
7
+ - `support.propose_plan_credit`
8
+
9
+ Every proposal example uses:
10
+
11
+ ```json
12
+ {
13
+ "customer_id": "CUS-3001",
14
+ "credit_cents": 2500,
15
+ "reason": "SLA outage ticket SUP-481"
16
+ }
17
+ ```
18
+
19
+ The expected call result has `source_database_changed: false`. Human review,
20
+ approval, and apply stay outside every model-facing recipe.
21
+
22
+ See [Client And Framework Recipes](../../../docs/client-recipes.md) for setup,
23
+ evidence labels, official references, and verification commands.
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Run from the repository root. Credentials and trusted scope are inherited
5
+ # from the shell that starts Claude Code; they are not written to .mcp.json.
6
+ claude mcp add-json --scope project synapsor "$(cat <<'JSON'
7
+ {
8
+ "type": "stdio",
9
+ "command": "npx",
10
+ "args": [
11
+ "-y",
12
+ "-p",
13
+ "@synapsor/runner",
14
+ "synapsor-runner",
15
+ "mcp",
16
+ "serve",
17
+ "--config",
18
+ "./examples/support-plan-credit/synapsor.runner.json",
19
+ "--store",
20
+ "./tmp/support-plan-credit/local.db"
21
+ ]
22
+ }
23
+ JSON
24
+ )"
25
+
26
+ claude mcp get synapsor
27
+
28
+ cat <<'PROMPT'
29
+ List the Synapsor MCP tools. Confirm there is no SQL, approval, apply, commit,
30
+ activation, or revert tool. Then call support.propose_plan_credit with
31
+ customer_id CUS-3001, credit_cents 2500, and reason "SLA outage ticket
32
+ SUP-481". Show the exact proposal effect and confirm source_database_changed is
33
+ false. Stop for human review outside Claude Code.
34
+ PROMPT
@@ -0,0 +1,24 @@
1
+ # Merge this table into a trusted project's .codex/config.toml and start Codex
2
+ # from the repository root. Credentials and trusted scope remain inherited
3
+ # environment variables; do not add their values here.
4
+ [mcp_servers.synapsor]
5
+ command = "npx"
6
+ args = [
7
+ "-y",
8
+ "-p",
9
+ "@synapsor/runner",
10
+ "synapsor-runner",
11
+ "mcp",
12
+ "serve",
13
+ "--config",
14
+ "./examples/support-plan-credit/synapsor.runner.json",
15
+ "--store",
16
+ "./tmp/support-plan-credit/local.db",
17
+ ]
18
+
19
+ # Prompt:
20
+ # List the Synapsor tools, then call support.propose_plan_credit with
21
+ # customer_id CUS-3001, credit_cents 2500, and reason "SLA outage ticket
22
+ # SUP-481". Confirm source_database_changed is false and stop for human review
23
+ # outside Codex. Never request an approval, apply, commit, activation, or revert
24
+ # MCP tool.
@@ -0,0 +1,35 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+
4
+ const transport = new StdioClientTransport({
5
+ command: "npx",
6
+ args: [
7
+ "-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
8
+ "--config", "./examples/support-plan-credit/synapsor.runner.json",
9
+ "--store", "./tmp/support-plan-credit/local.db",
10
+ ],
11
+ env: process.env,
12
+ stderr: "inherit",
13
+ });
14
+ const client = new Client({ name: "synapsor-generic-stdio", version: "1.0.0" });
15
+
16
+ await client.connect(transport);
17
+ try {
18
+ const listed = await client.listTools();
19
+ const names = listed.tools.map((tool) => tool.name);
20
+ const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
21
+ if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
22
+
23
+ const result = await client.callTool({
24
+ name: "support.propose_plan_credit",
25
+ arguments: {
26
+ customer_id: "CUS-3001",
27
+ credit_cents: 2500,
28
+ reason: "SLA outage ticket SUP-481",
29
+ },
30
+ });
31
+ console.log(JSON.stringify(result, null, 2));
32
+ console.error("Proposal only. Review and apply outside the model-facing MCP client.");
33
+ } finally {
34
+ await client.close();
35
+ }
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "name": "synapsor-runner",
3
3
  "transport": "streamable-http",
4
- "url": "http://127.0.0.1:8766/mcp"
4
+ "url": "http://127.0.0.1:8766/mcp",
5
+ "headers_from_env": {
6
+ "Authorization": "Bearer $SYNAPSOR_RUNNER_HTTP_TOKEN"
7
+ }
5
8
  }
@@ -0,0 +1,31 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+
4
+ const token = process.env.SYNAPSOR_RUNNER_HTTP_TOKEN;
5
+ if (!token) throw new Error("set SYNAPSOR_RUNNER_HTTP_TOKEN in the launching environment");
6
+
7
+ const transport = new StreamableHTTPClientTransport(new URL("http://127.0.0.1:8766/mcp"), {
8
+ requestInit: { headers: { Authorization: `Bearer ${token}` } },
9
+ });
10
+ const client = new Client({ name: "synapsor-generic-http", version: "1.0.0" });
11
+
12
+ await client.connect(transport);
13
+ try {
14
+ const listed = await client.listTools();
15
+ const names = listed.tools.map((tool) => tool.name);
16
+ const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
17
+ if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
18
+
19
+ const result = await client.callTool({
20
+ name: "support.propose_plan_credit",
21
+ arguments: {
22
+ customer_id: "CUS-3001",
23
+ credit_cents: 2500,
24
+ reason: "SLA outage ticket SUP-481",
25
+ },
26
+ });
27
+ console.log(JSON.stringify(result, null, 2));
28
+ console.error("Proposal only. Review and apply outside the model-facing MCP client.");
29
+ } finally {
30
+ await client.close();
31
+ }
@@ -0,0 +1,40 @@
1
+ import os
2
+
3
+ from google.adk.agents import LlmAgent
4
+ from google.adk.tools.mcp_tool import McpToolset
5
+ from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
6
+ from mcp import StdioServerParameters
7
+
8
+
9
+ root_agent = LlmAgent(
10
+ model=os.environ["GOOGLE_ADK_MODEL"],
11
+ name="synapsor_support_agent",
12
+ instruction=(
13
+ "List your Synapsor tools. Call support.propose_plan_credit with "
14
+ "customer_id CUS-3001, credit_cents 2500, and reason "
15
+ "'SLA outage ticket SUP-481'. Confirm source_database_changed is false "
16
+ "and stop for human review outside the model-facing agent."
17
+ ),
18
+ tools=[
19
+ McpToolset(
20
+ connection_params=StdioConnectionParams(
21
+ server_params=StdioServerParameters(
22
+ command="npx",
23
+ args=[
24
+ "-y", "-p", "@synapsor/runner", "synapsor-runner",
25
+ "mcp", "serve", "--config",
26
+ "./examples/support-plan-credit/synapsor.runner.json",
27
+ "--store", "./tmp/support-plan-credit/local.db",
28
+ ],
29
+ )
30
+ ),
31
+ tool_filter=[
32
+ "support.inspect_customer",
33
+ "support.propose_plan_credit",
34
+ ],
35
+ )
36
+ ],
37
+ )
38
+
39
+ # Run root_agent with your normal ADK runner. Approval, apply, commit,
40
+ # activation, and revert are intentionally not MCP tools.
@@ -0,0 +1,29 @@
1
+ import { MultiServerMCPClient } from "@langchain/mcp-adapters";
2
+
3
+ const client = new MultiServerMCPClient({
4
+ synapsor: {
5
+ transport: "stdio",
6
+ command: "npx",
7
+ args: [
8
+ "-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
9
+ "--config", "./examples/support-plan-credit/synapsor.runner.json",
10
+ "--store", "./tmp/support-plan-credit/local.db",
11
+ ],
12
+ },
13
+ });
14
+
15
+ const tools = await client.getTools();
16
+ const names = tools.map((tool) => tool.name);
17
+ const forbidden = names.filter((name) => /sql|approve|apply|commit|activate|revert/i.test(name));
18
+ if (forbidden.length) throw new Error(`unsafe model-facing tools: ${forbidden.join(", ")}`);
19
+
20
+ const proposal = tools.find((tool) => tool.name === "support.propose_plan_credit");
21
+ if (!proposal) throw new Error(`proposal tool missing; available: ${names.join(", ")}`);
22
+
23
+ const result = await proposal.invoke({
24
+ customer_id: "CUS-3001",
25
+ credit_cents: 2500,
26
+ reason: "SLA outage ticket SUP-481",
27
+ });
28
+ console.log(result);
29
+ console.error("Proposal only. Review and apply outside the model-facing LangChain/LangGraph agent.");
@@ -0,0 +1,36 @@
1
+ import asyncio
2
+
3
+ from llama_index.tools.mcp import BasicMCPClient
4
+
5
+
6
+ async def main() -> None:
7
+ client = BasicMCPClient(
8
+ "npx",
9
+ args=[
10
+ "-y", "-p", "@synapsor/runner", "synapsor-runner", "mcp", "serve",
11
+ "--config", "./examples/support-plan-credit/synapsor.runner.json",
12
+ "--store", "./tmp/support-plan-credit/local.db",
13
+ ],
14
+ )
15
+ tools = await client.list_tools()
16
+ names = [tool.name for tool in tools.tools]
17
+ forbidden = [name for name in names if any(part in name.lower() for part in (
18
+ "sql", "approve", "apply", "commit", "activate", "revert"
19
+ ))]
20
+ if forbidden:
21
+ raise RuntimeError(f"unsafe model-facing tools: {forbidden}")
22
+
23
+ result = await client.call_tool(
24
+ "support.propose_plan_credit",
25
+ {
26
+ "customer_id": "CUS-3001",
27
+ "credit_cents": 2500,
28
+ "reason": "SLA outage ticket SUP-481",
29
+ },
30
+ )
31
+ print(result)
32
+ print("Proposal only. Review and apply outside the model-facing LlamaIndex agent.")
33
+
34
+
35
+ if __name__ == "__main__":
36
+ asyncio.run(main())
@@ -9,7 +9,7 @@ await synapsor.connect();
9
9
  try {
10
10
  const agent = new Agent({
11
11
  name: "Support operations agent",
12
- instructions: "Use only Synapsor business tools. Inspect evidence before proposing a plan credit.",
12
+ instructions: "Use only the listed Synapsor business tools. Inspect evidence before proposing a plan credit. Never request approval or apply authority; stop for human review outside the agent.",
13
13
  mcpServers: [synapsor],
14
14
  });
15
15
  const result = await run(agent, "Inspect customer CUS-3001 and propose a $25 plan credit for SLA outage ticket SUP-481.");
@@ -1,16 +1,20 @@
1
1
  import { Agent, MCPServerStreamableHttp, run } from "@openai/agents";
2
2
 
3
3
  // Start Runner with --alias-mode openai so model-visible tool names are valid OpenAI function names.
4
+ const token = process.env.SYNAPSOR_RUNNER_HTTP_TOKEN;
5
+ if (!token) throw new Error("set SYNAPSOR_RUNNER_HTTP_TOKEN in the launching environment");
6
+
4
7
  const synapsor = new MCPServerStreamableHttp({
5
8
  name: "Synapsor Runner",
6
9
  url: "http://127.0.0.1:8766/mcp",
10
+ requestInit: { headers: { Authorization: `Bearer ${token}` } },
7
11
  });
8
12
 
9
13
  await synapsor.connect();
10
14
  try {
11
15
  const agent = new Agent({
12
16
  name: "Support operations agent",
13
- instructions: "Use only Synapsor business tools. Inspect evidence before proposing a plan credit.",
17
+ instructions: "Use only the listed Synapsor business tools. Inspect evidence before proposing a plan credit. Never request approval or apply authority; stop for human review outside the agent.",
14
18
  mcpServers: [synapsor],
15
19
  });
16
20
  const result = await run(agent, "Inspect customer CUS-3001 and propose a $25 plan credit for SLA outage ticket SUP-481.");
@@ -0,0 +1,20 @@
1
+ {
2
+ "servers": {
3
+ "synapsor": {
4
+ "type": "stdio",
5
+ "command": "npx",
6
+ "args": [
7
+ "-y",
8
+ "-p",
9
+ "@synapsor/runner",
10
+ "synapsor-runner",
11
+ "mcp",
12
+ "serve",
13
+ "--config",
14
+ "./examples/support-plan-credit/synapsor.runner.json",
15
+ "--store",
16
+ "./tmp/support-plan-credit/local.db"
17
+ ]
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,221 @@
1
+ {
2
+ "$schema": "https://schemas.synapsor.ai/synapsor.contract-tests.schema.json",
3
+ "version": 1,
4
+ "name": "support.propose_plan_credit generated contract boundary",
5
+ "tests": [
6
+ {
7
+ "id": "support.propose_plan_credit-operator-boundary",
8
+ "kind": "operator_boundary",
9
+ "capability": "support.propose_plan_credit"
10
+ },
11
+ {
12
+ "id": "support.propose_plan_credit-proposal-effect",
13
+ "kind": "proposal_effect",
14
+ "capability": "support.propose_plan_credit",
15
+ "expected": {
16
+ "action": "grant_plan_credit",
17
+ "operation": {
18
+ "kind": "update"
19
+ },
20
+ "allowed_fields": [
21
+ "plan_credit_cents",
22
+ "credit_reason"
23
+ ],
24
+ "patch": {
25
+ "plan_credit_cents": {
26
+ "from_arg": "credit_cents"
27
+ },
28
+ "credit_reason": {
29
+ "from_arg": "reason"
30
+ }
31
+ },
32
+ "numeric_bounds": {
33
+ "plan_credit_cents": {
34
+ "minimum": 1,
35
+ "maximum": 50000
36
+ }
37
+ },
38
+ "transition_guards": {},
39
+ "conflict_guard": {
40
+ "column": "updated_at"
41
+ },
42
+ "approval": {
43
+ "mode": "policy",
44
+ "policy": "support_propose_plan_credit_auto_approval",
45
+ "required_role": "support_reviewer"
46
+ },
47
+ "writeback": {
48
+ "mode": "direct_sql"
49
+ },
50
+ "max_rows": 1
51
+ }
52
+ },
53
+ {
54
+ "id": "support.propose_plan_credit-kept-out-fields",
55
+ "kind": "hide_fields",
56
+ "capability": "support.propose_plan_credit",
57
+ "args": {
58
+ "customer_id": "replace-customer_id",
59
+ "credit_cents": 1,
60
+ "reason": "replace-reason"
61
+ },
62
+ "trusted_context": {
63
+ "tenant_id": "replace-with-test-tenant",
64
+ "principal": "contract_test"
65
+ },
66
+ "fields": [
67
+ "card_token",
68
+ "raw_payment_method",
69
+ "internal_risk_score",
70
+ "private_notes"
71
+ ]
72
+ },
73
+ {
74
+ "id": "support.propose_plan_credit-conflict-guard",
75
+ "kind": "conflict_guard",
76
+ "capability": "support.propose_plan_credit",
77
+ "expected": {
78
+ "column": "updated_at"
79
+ }
80
+ },
81
+ {
82
+ "id": "support.propose_plan_credit-trusted-scope",
83
+ "kind": "trusted_scope",
84
+ "capability": "support.propose_plan_credit",
85
+ "expected": {
86
+ "context": "support_agent_context",
87
+ "tenant_key": "tenant_id",
88
+ "tenant_binding": "tenant_id",
89
+ "tenant_authority": {
90
+ "source": "environment",
91
+ "key": "SYNAPSOR_TENANT_ID",
92
+ "required": true
93
+ },
94
+ "principal_binding": "principal",
95
+ "principal_authority": {
96
+ "source": "environment",
97
+ "key": "SYNAPSOR_PRINCIPAL",
98
+ "required": true
99
+ }
100
+ }
101
+ },
102
+ {
103
+ "id": "support.propose_plan_credit-evidence-required",
104
+ "kind": "evidence_requirement",
105
+ "capability": "support.propose_plan_credit",
106
+ "expected": {
107
+ "required": true,
108
+ "query_audit": true
109
+ }
110
+ },
111
+ {
112
+ "id": "support.propose_plan_credit-approval-boundary",
113
+ "kind": "approval_boundary",
114
+ "capability": "support.propose_plan_credit",
115
+ "expected": {
116
+ "approval": {
117
+ "mode": "policy",
118
+ "policy": "support_propose_plan_credit_auto_approval",
119
+ "required_role": "support_reviewer"
120
+ },
121
+ "policy": {
122
+ "kind": "approval",
123
+ "limits": [
124
+ {
125
+ "kind": "count",
126
+ "max": 20,
127
+ "period": "day",
128
+ "scope": "tenant_policy"
129
+ },
130
+ {
131
+ "field": "plan_credit_cents",
132
+ "kind": "total",
133
+ "max": 100000,
134
+ "period": "day",
135
+ "scope": "tenant_policy"
136
+ }
137
+ ],
138
+ "mode": "green",
139
+ "name": "support_propose_plan_credit_auto_approval",
140
+ "rules": [
141
+ {
142
+ "field": "plan_credit_cents",
143
+ "max": 2500
144
+ }
145
+ ]
146
+ }
147
+ }
148
+ },
149
+ {
150
+ "id": "support.propose_plan_credit-allowed-effect",
151
+ "kind": "tool_allow",
152
+ "capability": "support.propose_plan_credit",
153
+ "args": {
154
+ "customer_id": "replace-customer_id",
155
+ "credit_cents": 1,
156
+ "reason": "replace-reason"
157
+ },
158
+ "trusted_context": {
159
+ "tenant_id": "replace-with-test-tenant",
160
+ "principal": "contract_test"
161
+ }
162
+ },
163
+ {
164
+ "id": "support.propose_plan_credit-other-tenant-denied",
165
+ "kind": "tool_deny",
166
+ "capability": "support.propose_plan_credit",
167
+ "args": {
168
+ "customer_id": "replace-with-other-tenant-customer_id",
169
+ "credit_cents": 1,
170
+ "reason": "replace-reason"
171
+ },
172
+ "trusted_context": {
173
+ "tenant_id": "replace-with-test-tenant",
174
+ "principal": "contract_test"
175
+ },
176
+ "expected_code": "NOT_FOUND_IN_TENANT"
177
+ },
178
+ {
179
+ "id": "support.propose_plan_credit-source-unchanged",
180
+ "kind": "source_unchanged_before_approval",
181
+ "capability": "support.propose_plan_credit",
182
+ "args": {
183
+ "customer_id": "replace-customer_id",
184
+ "credit_cents": 1,
185
+ "reason": "replace-reason"
186
+ },
187
+ "trusted_context": {
188
+ "tenant_id": "replace-with-test-tenant",
189
+ "principal": "contract_test"
190
+ }
191
+ },
192
+ {
193
+ "id": "support.propose_plan_credit-customer_id-constraints",
194
+ "kind": "argument_constraint",
195
+ "capability": "support.propose_plan_credit",
196
+ "argument": "customer_id",
197
+ "expected": {
198
+ "max_length": 128
199
+ }
200
+ },
201
+ {
202
+ "id": "support.propose_plan_credit-credit_cents-constraints",
203
+ "kind": "argument_constraint",
204
+ "capability": "support.propose_plan_credit",
205
+ "argument": "credit_cents",
206
+ "expected": {
207
+ "minimum": 1,
208
+ "maximum": 50000
209
+ }
210
+ },
211
+ {
212
+ "id": "support.propose_plan_credit-reason-constraints",
213
+ "kind": "argument_constraint",
214
+ "capability": "support.propose_plan_credit",
215
+ "argument": "reason",
216
+ "expected": {
217
+ "max_length": 500
218
+ }
219
+ }
220
+ ]
221
+ }
@@ -0,0 +1,34 @@
1
+ import { defineCapability } from "@synapsor/runner/authoring";
2
+
3
+ // Disabled authoring source. Editing this file never changes active Runner tools.
4
+ export default defineCapability({
5
+ name: "support.propose_plan_credit",
6
+ description: "Propose one bounded support plan credit after reviewing customer evidence.",
7
+ kind: "proposal",
8
+ context: "support_agent_context",
9
+ source: "local_postgres",
10
+ subject: {
11
+ schema: "public", table: "customers", primary_key: "id",
12
+ tenant_key: "tenant_id", conflict_key: "updated_at",
13
+ },
14
+ args: {
15
+ customer_id: { type: "string", required: true, max_length: 128 },
16
+ credit_cents: { type: "number", required: true, minimum: 1, maximum: 50000 },
17
+ reason: { type: "string", required: true, max_length: 500 },
18
+ },
19
+ lookup: { id_from_arg: "customer_id" },
20
+ visible_fields: ["id", "tenant_id", "customer_id", "plan", "invoice_status", "support_ticket_reason", "plan_credit_cents", "credit_reason", "updated_at"],
21
+ kept_out_fields: ["card_token", "raw_payment_method", "internal_risk_score", "private_notes"],
22
+ evidence: { required: true, query_audit: true },
23
+ max_rows: 1,
24
+ proposal: {
25
+ action: "grant_plan_credit",
26
+ operation: { kind: "update" },
27
+ allowed_fields: ["plan_credit_cents", "credit_reason"],
28
+ patch: { plan_credit_cents: { from_arg: "credit_cents" }, credit_reason: { from_arg: "reason" } },
29
+ numeric_bounds: { plan_credit_cents: { minimum: 1, maximum: 50000 } },
30
+ conflict_guard: { column: "updated_at" },
31
+ approval: { mode: "policy", policy: "support_propose_plan_credit_auto_approval", required_role: "support_reviewer" },
32
+ writeback: { mode: "direct_sql" },
33
+ },
34
+ });
@@ -182,9 +182,6 @@
182
182
  "mode": "human",
183
183
  "required_role": "support_reviewer"
184
184
  },
185
- "conflict_guard": {
186
- "weak_guard_ack": true
187
- },
188
185
  "numeric_bounds": {
189
186
  "amount_cents": {
190
187
  "maximum": 50000,
@@ -0,0 +1,14 @@
1
+ # MCP audit fixtures
2
+
3
+ These deterministic fixtures exercise the public `synapsor-runner audit`
4
+ contract without a database, account, telemetry, or business-tool call.
5
+
6
+ - `dangerous-tools-list.json` contains raw SQL and model-callable commit
7
+ authority.
8
+ - `reviewed-proposal-tools-list.json` contains a semantic read and an exact
9
+ proposal boundary.
10
+ - `cursor-bypass-config.json` proves that a reviewed Synapsor server does not
11
+ govern a second model-visible server that still exposes `execute_sql`.
12
+
13
+ Static config auditing never launches either command in the Cursor fixture.
14
+ Live inspection requires one exact `--live-server` name and explicit `--yes`.
@@ -0,0 +1,38 @@
1
+ {
2
+ "mcpServers": {
3
+ "synapsor": {
4
+ "type": "stdio",
5
+ "command": "npx",
6
+ "args": [
7
+ "-y",
8
+ "-p",
9
+ "@synapsor/runner@1.5.3",
10
+ "synapsor-runner",
11
+ "mcp",
12
+ "serve",
13
+ "--config",
14
+ "${workspaceFolder}/synapsor.runner.json",
15
+ "--store",
16
+ "${workspaceFolder}/.synapsor/local.db"
17
+ ]
18
+ },
19
+ "legacy-database": {
20
+ "type": "stdio",
21
+ "command": "legacy-database-mcp",
22
+ "args": [],
23
+ "tools": [
24
+ {
25
+ "name": "execute_sql",
26
+ "description": "Execute arbitrary SQL.",
27
+ "inputSchema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "sql": { "type": "string" }
31
+ },
32
+ "required": ["sql"]
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ }