@strixgov/mcp-proxy 0.2.0 → 0.2.1
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.
- package/CHANGELOG.md +9 -0
- package/examples/youspot/README.md +58 -0
- package/examples/youspot-proxy-config.json +236 -0
- package/package.json +6 -2
- package/scripts/run-tests.mjs +41 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1]
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `npm test` in the published package crashed with a missing
|
|
14
|
+
`scripts/preflight.mjs` (the tarball did not ship `scripts/`). `test` now runs
|
|
15
|
+
`scripts/run-tests.mjs`, which runs the suite in a source checkout and, in an
|
|
16
|
+
installed copy, exits non-zero with an explanation rather than crashing or
|
|
17
|
+
passing on zero tests. Reported by an external tester.
|
|
18
|
+
|
|
10
19
|
## [0.2.0]
|
|
11
20
|
|
|
12
21
|
Minor, not a patch: this release carries the already-merged shadow-discovery
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Govern YouSpot with the Strix MCP proxy
|
|
2
|
+
|
|
3
|
+
This example uses YouSpot's official stdio bridge as the upstream server:
|
|
4
|
+
|
|
5
|
+
`MCP host -> @strixgov/mcp-proxy -> @youspot/mcp -> https://youspot.com/mcp/v1`
|
|
6
|
+
|
|
7
|
+
No YouSpot tool implementation is changed.
|
|
8
|
+
|
|
9
|
+
## 1. Provide the YouSpot token to the host environment
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
export YOUSPOT_TOKEN="..."
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`@youspot/mcp` requires the token for `tools/call`. The example config intentionally does not store it; the proxy passes the process environment through to the upstream when `upstream.env` is omitted.
|
|
16
|
+
|
|
17
|
+
## 2. Point the MCP host at Strix
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"youspot-governed": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": [
|
|
25
|
+
"-y",
|
|
26
|
+
"@strixgov/mcp-proxy",
|
|
27
|
+
"--config",
|
|
28
|
+
"/absolute/path/to/youspot-proxy-config.json"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The same shape works in stdio-capable hosts that accept command/args MCP definitions. Use the host's normal secure environment mechanism for `YOUSPOT_TOKEN`.
|
|
36
|
+
|
|
37
|
+
## 3. What the starter policy does
|
|
38
|
+
|
|
39
|
+
- reviewed read tools -> explicit ALLOW rules
|
|
40
|
+
- every other LOW/MEDIUM/HIGH tool -> APPROVAL_REQUIRED
|
|
41
|
+
- CRITICAL -> DENY
|
|
42
|
+
- policy default -> DENY
|
|
43
|
+
|
|
44
|
+
The explicit rules are intentional. `@strixgov/mcp-proxy` documents name heuristics as a discovery fallback, not a production security boundary. A YouSpot tool added after this example was reviewed therefore does not inherit an automatic LOW/ALLOW path.
|
|
45
|
+
- bundled approval channel -> disabled
|
|
46
|
+
|
|
47
|
+
With approval disabled, any call that evaluates to `APPROVAL_REQUIRED` is refused rather than auto-approved. Configure a separate approver only when the response channel is not writable by the acting agent.
|
|
48
|
+
|
|
49
|
+
## 4. Verify
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx @strixgov/verifier chain ~/.strix-mcp-proxy/youspot/receipts.jsonl \
|
|
53
|
+
--jwks ~/.strix-mcp-proxy/youspot/keys/public-jwk.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Coverage boundary
|
|
57
|
+
|
|
58
|
+
Only traffic routed through this local proxy is governed. Direct `https://youspot.com/mcp/v1` connections remain outside this example. Remote-HTTP-only clients require a hosted Strix gateway.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
{
|
|
2
|
+
"serverId": "youspot",
|
|
3
|
+
"upstream": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": [
|
|
6
|
+
"-y",
|
|
7
|
+
"@youspot/mcp"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"capabilities": [
|
|
11
|
+
{
|
|
12
|
+
"id": "mcp.youspot.get_graph_object",
|
|
13
|
+
"name": "get_graph_object",
|
|
14
|
+
"risk": "LOW",
|
|
15
|
+
"mode": "READ",
|
|
16
|
+
"description": "Read one object from the YouSpot graph."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "mcp.youspot.list_graph_objects",
|
|
20
|
+
"name": "list_graph_objects",
|
|
21
|
+
"risk": "LOW",
|
|
22
|
+
"mode": "READ",
|
|
23
|
+
"description": "List objects from the YouSpot graph."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "mcp.youspot.search_graph_objects",
|
|
27
|
+
"name": "search_graph_objects",
|
|
28
|
+
"risk": "LOW",
|
|
29
|
+
"mode": "READ",
|
|
30
|
+
"description": "Search the YouSpot graph."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "mcp.youspot.get_gmail_message",
|
|
34
|
+
"name": "get_gmail_message",
|
|
35
|
+
"risk": "LOW",
|
|
36
|
+
"mode": "READ",
|
|
37
|
+
"description": "Read one Gmail message."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "mcp.youspot.search_gmail_messages",
|
|
41
|
+
"name": "search_gmail_messages",
|
|
42
|
+
"risk": "LOW",
|
|
43
|
+
"mode": "READ",
|
|
44
|
+
"description": "Search connected Gmail mailboxes."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "mcp.youspot.list_gmail_labels",
|
|
48
|
+
"name": "list_gmail_labels",
|
|
49
|
+
"risk": "LOW",
|
|
50
|
+
"mode": "READ",
|
|
51
|
+
"description": "List Gmail labels."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "mcp.youspot.get_calendar_events",
|
|
55
|
+
"name": "get_calendar_events",
|
|
56
|
+
"risk": "LOW",
|
|
57
|
+
"mode": "READ",
|
|
58
|
+
"description": "Read calendar events."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "mcp.youspot.list_google_calendars",
|
|
62
|
+
"name": "list_google_calendars",
|
|
63
|
+
"risk": "LOW",
|
|
64
|
+
"mode": "READ",
|
|
65
|
+
"description": "List Google calendars."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "mcp.youspot.list_slack_channels",
|
|
69
|
+
"name": "list_slack_channels",
|
|
70
|
+
"risk": "LOW",
|
|
71
|
+
"mode": "READ",
|
|
72
|
+
"description": "List Slack channels known to YouSpot."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "mcp.youspot.get_slack_thread",
|
|
76
|
+
"name": "get_slack_thread",
|
|
77
|
+
"risk": "LOW",
|
|
78
|
+
"mode": "READ",
|
|
79
|
+
"description": "Read a saved Slack thread."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "mcp.youspot.search_slack_messages",
|
|
83
|
+
"name": "search_slack_messages",
|
|
84
|
+
"risk": "LOW",
|
|
85
|
+
"mode": "READ",
|
|
86
|
+
"description": "Search saved Slack messages."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"id": "mcp.youspot.list_integrations",
|
|
90
|
+
"name": "list_integrations",
|
|
91
|
+
"risk": "LOW",
|
|
92
|
+
"mode": "READ",
|
|
93
|
+
"description": "List connected integrations."
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "mcp.youspot.add_slack_reaction",
|
|
97
|
+
"name": "add_slack_reaction",
|
|
98
|
+
"risk": "MEDIUM",
|
|
99
|
+
"mode": "WRITE",
|
|
100
|
+
"description": "Add a reaction to a Slack message."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "mcp.youspot.archive_gmail_message",
|
|
104
|
+
"name": "archive_gmail_message",
|
|
105
|
+
"risk": "MEDIUM",
|
|
106
|
+
"mode": "WRITE",
|
|
107
|
+
"description": "Archive a Gmail message."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "mcp.youspot.connect_graph_objects",
|
|
111
|
+
"name": "connect_graph_objects",
|
|
112
|
+
"risk": "MEDIUM",
|
|
113
|
+
"mode": "WRITE",
|
|
114
|
+
"description": "Create a directed edge between graph objects."
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "mcp.youspot.create_gmail_draft",
|
|
118
|
+
"name": "create_gmail_draft",
|
|
119
|
+
"risk": "MEDIUM",
|
|
120
|
+
"mode": "WRITE",
|
|
121
|
+
"description": "Create a Gmail draft."
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "mcp.youspot.create_graph_object",
|
|
125
|
+
"name": "create_graph_object",
|
|
126
|
+
"risk": "MEDIUM",
|
|
127
|
+
"mode": "WRITE",
|
|
128
|
+
"description": "Create a YouSpot graph object."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"id": "mcp.youspot.disconnect_graph_objects",
|
|
132
|
+
"name": "disconnect_graph_objects",
|
|
133
|
+
"risk": "MEDIUM",
|
|
134
|
+
"mode": "WRITE",
|
|
135
|
+
"description": "Remove a graph-object edge."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "mcp.youspot.set_follow_up",
|
|
139
|
+
"name": "set_follow_up",
|
|
140
|
+
"risk": "MEDIUM",
|
|
141
|
+
"mode": "WRITE",
|
|
142
|
+
"description": "Create a dated follow-up fact."
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "mcp.youspot.set_notification_emails",
|
|
146
|
+
"name": "set_notification_emails",
|
|
147
|
+
"risk": "MEDIUM",
|
|
148
|
+
"mode": "WRITE",
|
|
149
|
+
"description": "Change YouSpot notification-email settings."
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "mcp.youspot.update_graph_object",
|
|
153
|
+
"name": "update_graph_object",
|
|
154
|
+
"risk": "MEDIUM",
|
|
155
|
+
"mode": "WRITE",
|
|
156
|
+
"description": "Update fields on a graph object."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "mcp.youspot.create_calendar_event",
|
|
160
|
+
"name": "create_calendar_event",
|
|
161
|
+
"risk": "HIGH",
|
|
162
|
+
"mode": "WRITE",
|
|
163
|
+
"description": "Create or queue a calendar event."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "mcp.youspot.send_email",
|
|
167
|
+
"name": "send_email",
|
|
168
|
+
"risk": "HIGH",
|
|
169
|
+
"mode": "WRITE",
|
|
170
|
+
"description": "Send email to the signed-in user's own address."
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "mcp.youspot.send_slack_message",
|
|
174
|
+
"name": "send_slack_message",
|
|
175
|
+
"risk": "HIGH",
|
|
176
|
+
"mode": "WRITE",
|
|
177
|
+
"description": "Post a Slack message or DM."
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"id": "mcp.youspot.send_text_to_user",
|
|
181
|
+
"name": "send_text_to_user",
|
|
182
|
+
"risk": "HIGH",
|
|
183
|
+
"mode": "WRITE",
|
|
184
|
+
"description": "Send an iMessage/SMS to the user's verified phone."
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"id": "mcp.youspot.delete_graph_object",
|
|
188
|
+
"name": "delete_graph_object",
|
|
189
|
+
"risk": "CRITICAL",
|
|
190
|
+
"mode": "WRITE",
|
|
191
|
+
"description": "Delete one graph object."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "mcp.youspot.delete_graph_objects",
|
|
195
|
+
"name": "delete_graph_objects",
|
|
196
|
+
"risk": "CRITICAL",
|
|
197
|
+
"mode": "WRITE",
|
|
198
|
+
"description": "Delete all graph objects of one type."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "mcp.youspot.disconnect_integration",
|
|
202
|
+
"name": "disconnect_integration",
|
|
203
|
+
"risk": "CRITICAL",
|
|
204
|
+
"mode": "WRITE",
|
|
205
|
+
"description": "Disconnect an integration; may delete synced records."
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"policy": {
|
|
209
|
+
"rules": {
|
|
210
|
+
"mcp.youspot.get_graph_object": "ALLOW",
|
|
211
|
+
"mcp.youspot.list_graph_objects": "ALLOW",
|
|
212
|
+
"mcp.youspot.search_graph_objects": "ALLOW",
|
|
213
|
+
"mcp.youspot.get_gmail_message": "ALLOW",
|
|
214
|
+
"mcp.youspot.search_gmail_messages": "ALLOW",
|
|
215
|
+
"mcp.youspot.list_gmail_labels": "ALLOW",
|
|
216
|
+
"mcp.youspot.get_calendar_events": "ALLOW",
|
|
217
|
+
"mcp.youspot.list_google_calendars": "ALLOW",
|
|
218
|
+
"mcp.youspot.list_slack_channels": "ALLOW",
|
|
219
|
+
"mcp.youspot.get_slack_thread": "ALLOW",
|
|
220
|
+
"mcp.youspot.search_slack_messages": "ALLOW",
|
|
221
|
+
"mcp.youspot.list_integrations": "ALLOW"
|
|
222
|
+
},
|
|
223
|
+
"riskOverrides": {
|
|
224
|
+
"LOW": "APPROVAL_REQUIRED",
|
|
225
|
+
"MEDIUM": "APPROVAL_REQUIRED",
|
|
226
|
+
"HIGH": "APPROVAL_REQUIRED",
|
|
227
|
+
"CRITICAL": "DENY"
|
|
228
|
+
},
|
|
229
|
+
"default": "DENY"
|
|
230
|
+
},
|
|
231
|
+
"approval": {
|
|
232
|
+
"enabled": false
|
|
233
|
+
},
|
|
234
|
+
"storagePath": "~/.strix-mcp-proxy/youspot",
|
|
235
|
+
"keyPath": "~/.strix-mcp-proxy/youspot/keys"
|
|
236
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strixgov/mcp-proxy",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Strix MCP Proxy — the distribution wedge for governed agent tool execution. Point your MCP client at the proxy instead of the underlying server; every callTool is intercepted, evaluated against policy, and emits a signed Ed25519 receipt. Designed so Mode 3 (capability-enforced) is the v0.2.0 upgrade path, not a rewrite.",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"@strixgov/mcp-adapter": "^0.1.0",
|
|
21
21
|
"@strixgov/tool-gateway": "^0.5.0"
|
|
22
22
|
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"zod": "^3.25.76"
|
|
25
|
+
},
|
|
23
26
|
"keywords": [
|
|
24
27
|
"strix",
|
|
25
28
|
"mcp",
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
"files": [
|
|
41
44
|
"src/",
|
|
42
45
|
"bin/",
|
|
46
|
+
"scripts/run-tests.mjs",
|
|
43
47
|
"examples/",
|
|
44
48
|
"README.md",
|
|
45
49
|
"CHANGELOG.md",
|
|
@@ -54,6 +58,6 @@
|
|
|
54
58
|
"node": ">=18.0.0"
|
|
55
59
|
},
|
|
56
60
|
"scripts": {
|
|
57
|
-
"test": "node scripts/
|
|
61
|
+
"test": "node scripts/run-tests.mjs"
|
|
58
62
|
}
|
|
59
63
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `npm test` entry point for @strixgov/mcp-proxy.
|
|
4
|
+
*
|
|
5
|
+
* The suite needs a source checkout (devDependencies and pnpm workspace links),
|
|
6
|
+
* so the npm tarball does not ship test/ or scripts/preflight.mjs. Previously
|
|
7
|
+
* `test` called preflight.mjs directly, so `npm test` in an installed copy died
|
|
8
|
+
* with "Cannot find module scripts/preflight.mjs". This wrapper fails closed
|
|
9
|
+
* with an explanation instead: it never exits 0 having run zero tests.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync } from "node:fs";
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
16
|
+
const testDir = fileURLToPath(new URL("../test", import.meta.url));
|
|
17
|
+
const preflight = fileURLToPath(new URL("./preflight.mjs", import.meta.url));
|
|
18
|
+
|
|
19
|
+
if (!existsSync(testDir) || !existsSync(preflight)) {
|
|
20
|
+
process.stderr.write(
|
|
21
|
+
[
|
|
22
|
+
"",
|
|
23
|
+
" @strixgov/mcp-proxy: no tests to run here.",
|
|
24
|
+
"",
|
|
25
|
+
" The test suite is not included in the published npm package.",
|
|
26
|
+
" Run it from a source checkout of https://github.com/Strixgov/strix:",
|
|
27
|
+
"",
|
|
28
|
+
" pnpm install",
|
|
29
|
+
" pnpm --dir packages/strixgov-mcp-proxy test",
|
|
30
|
+
"",
|
|
31
|
+
" Exiting non-zero on purpose: zero tests run must not read as a pass.",
|
|
32
|
+
"",
|
|
33
|
+
].join("\n"),
|
|
34
|
+
);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const args of [[preflight], ["--test"]]) {
|
|
39
|
+
const r = spawnSync(process.execPath, args, { cwd: root, stdio: "inherit" });
|
|
40
|
+
if (r.status !== 0) process.exit(r.status ?? 1);
|
|
41
|
+
}
|