protect-mcp 0.1.1 → 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/dist/cli.mjs CHANGED
@@ -1,27 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ProtectGateway,
4
- loadPolicy
5
- } from "./chunk-L3QWKSGY.mjs";
4
+ initSigning,
5
+ loadPolicy,
6
+ validateCredentials
7
+ } from "./chunk-ZCKNFULF.mjs";
6
8
 
7
9
  // src/cli.ts
8
10
  function printHelp() {
9
11
  process.stderr.write(`
10
- @scopeblind/protect-mcp \u2014 Security gateway for MCP servers
12
+ protect-mcp \u2014 Shadow-mode security gateway for MCP servers
11
13
 
12
14
  Usage:
13
15
  protect-mcp [options] -- <command> [args...]
16
+ protect-mcp init [--dir <path>]
14
17
 
15
18
  Options:
16
- --policy <path> Policy JSON file (default: allow-all)
19
+ --policy <path> Policy/config JSON file (default: allow-all)
17
20
  --slug <slug> ScopeBlind tenant slug (optional)
18
- --enforce Enable enforcement mode (default: observe-only)
21
+ --enforce Enable enforcement mode (default: shadow mode)
19
22
  --verbose Enable debug logging to stderr
20
23
  --help Show this help
21
24
 
25
+ Commands:
26
+ init Generate config template, Ed25519 keypair, and sample policy
27
+
22
28
  Examples:
23
29
  protect-mcp -- node my-server.js
24
- protect-mcp --policy policy.json --enforce -- node my-server.js
30
+ protect-mcp --policy protect-mcp.json -- node my-server.js
31
+ protect-mcp --policy protect-mcp.json --enforce -- node my-server.js
32
+ protect-mcp init
25
33
 
26
34
  `);
27
35
  }
@@ -64,20 +72,133 @@ function parseArgs(argv) {
64
72
  }
65
73
  return { policyPath, slug, enforce, verbose, childCommand };
66
74
  }
75
+ async function handleInit(argv) {
76
+ const { writeFileSync, existsSync, mkdirSync } = await import("fs");
77
+ const { join } = await import("path");
78
+ let dir = process.cwd();
79
+ const dirIdx = argv.indexOf("--dir");
80
+ if (dirIdx !== -1 && argv[dirIdx + 1]) {
81
+ dir = argv[dirIdx + 1];
82
+ }
83
+ const configPath = join(dir, "protect-mcp.json");
84
+ const keysDir = join(dir, "keys");
85
+ const keyPath = join(keysDir, "gateway.json");
86
+ if (existsSync(configPath)) {
87
+ process.stderr.write(`[PROTECT_MCP] Config already exists at ${configPath}
88
+ `);
89
+ process.stderr.write("[PROTECT_MCP] Delete it first if you want to regenerate.\n");
90
+ process.exit(1);
91
+ }
92
+ let keypair;
93
+ try {
94
+ const artifacts = await import("@veritasacta/artifacts");
95
+ keypair = artifacts.generateKeypair();
96
+ } catch {
97
+ const { randomBytes } = await import("crypto");
98
+ const { ed25519 } = await import("./ed25519-EDO4K4EP.mjs");
99
+ const { bytesToHex } = await import("./utils-IDWBSHJU.mjs");
100
+ const privateKey = randomBytes(32);
101
+ const publicKey = ed25519.getPublicKey(privateKey);
102
+ keypair = {
103
+ privateKey: bytesToHex(privateKey),
104
+ publicKey: bytesToHex(publicKey),
105
+ kid: "generated"
106
+ };
107
+ }
108
+ if (!existsSync(keysDir)) {
109
+ mkdirSync(keysDir, { recursive: true });
110
+ }
111
+ writeFileSync(keyPath, JSON.stringify({
112
+ privateKey: keypair.privateKey,
113
+ publicKey: keypair.publicKey,
114
+ kid: keypair.kid,
115
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
116
+ warning: "KEEP THIS FILE SECRET. Never commit to version control."
117
+ }, null, 2) + "\n");
118
+ const gitignorePath = join(keysDir, ".gitignore");
119
+ if (!existsSync(gitignorePath)) {
120
+ writeFileSync(gitignorePath, "# Never commit signing keys\n*.json\n");
121
+ }
122
+ const config = {
123
+ tools: {
124
+ "*": {
125
+ rate_limit: "100/hour"
126
+ },
127
+ "delete_file": {
128
+ block: true,
129
+ min_tier: "privileged"
130
+ },
131
+ "write_file": {
132
+ min_tier: "signed-known",
133
+ rate_limit: "10/minute"
134
+ },
135
+ "read_file": {
136
+ rate_limit: "50/minute"
137
+ }
138
+ },
139
+ default_tier: "unknown",
140
+ signing: {
141
+ key_path: "./keys/gateway.json",
142
+ issuer: "protect-mcp",
143
+ enabled: true
144
+ },
145
+ credentials: {
146
+ _example_api: {
147
+ inject: "header",
148
+ name: "Authorization",
149
+ value_env: "EXAMPLE_API_KEY",
150
+ _comment: "Remove the underscore prefix and set EXAMPLE_API_KEY in your environment"
151
+ }
152
+ }
153
+ };
154
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
155
+ process.stderr.write(`
156
+ ${bold("protect-mcp initialized!")}
157
+
158
+ Created:
159
+ ${configPath} Config with shadow mode + optional local signing
160
+ ${keyPath} Ed25519 signing keypair
161
+
162
+ ${bold("Next steps:")}
163
+ 1. Edit protect-mcp.json to match your MCP server's tools
164
+ 2. Set any credential environment variables
165
+ 3. Run: protect-mcp --policy protect-mcp.json -- <your-mcp-server>
166
+
167
+ ${bold("Your gateway public key:")}
168
+ ${keypair.publicKey}
169
+
170
+ ${bold("Key ID (kid):")}
171
+ ${keypair.kid}
172
+
173
+ Shadow mode is the default \u2014 all tool calls are logged and nothing is blocked.
174
+ Run with the generated policy file if you also want local signed receipts. Add --enforce when ready.
175
+ `);
176
+ }
177
+ function bold(s) {
178
+ return process.env.NO_COLOR ? s : `\x1B[1m${s}\x1B[0m`;
179
+ }
67
180
  async function main() {
68
181
  const args = process.argv.slice(2);
69
182
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
70
183
  printHelp();
71
184
  process.exit(0);
72
185
  }
186
+ if (args[0] === "init") {
187
+ await handleInit(args.slice(1));
188
+ process.exit(0);
189
+ }
73
190
  const { policyPath, slug, enforce, verbose, childCommand } = parseArgs(args);
74
191
  let policy = null;
75
192
  let policyDigest = "none";
193
+ let credentials;
194
+ let signing;
76
195
  if (policyPath) {
77
196
  try {
78
197
  const loaded = loadPolicy(policyPath);
79
198
  policy = loaded.policy;
80
199
  policyDigest = loaded.digest;
200
+ credentials = loaded.credentials;
201
+ signing = loaded.signing;
81
202
  if (verbose) {
82
203
  process.stderr.write(`[PROTECT_MCP] Loaded policy from ${policyPath} (digest: ${policyDigest})
83
204
  `);
@@ -88,6 +209,20 @@ async function main() {
88
209
  process.exit(1);
89
210
  }
90
211
  }
212
+ if (signing) {
213
+ const warnings = await initSigning(signing);
214
+ for (const w of warnings) {
215
+ process.stderr.write(`[PROTECT_MCP] Warning: ${w}
216
+ `);
217
+ }
218
+ }
219
+ if (credentials) {
220
+ const warnings = validateCredentials(credentials);
221
+ for (const w of warnings) {
222
+ process.stderr.write(`[PROTECT_MCP] Warning: ${w}
223
+ `);
224
+ }
225
+ }
91
226
  const config = {
92
227
  command: childCommand[0],
93
228
  args: childCommand.slice(1),
@@ -95,7 +230,9 @@ async function main() {
95
230
  policyDigest,
96
231
  slug,
97
232
  enforce,
98
- verbose
233
+ verbose,
234
+ signing,
235
+ credentials
99
236
  };
100
237
  const gateway = new ProtectGateway(config);
101
238
  await gateway.start();