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/README.md +126 -60
- package/dist/chunk-D733KAPG.mjs +252 -0
- package/dist/chunk-ZCKNFULF.mjs +613 -0
- package/dist/cli.js +3100 -20
- package/dist/cli.mjs +144 -7
- package/dist/ed25519-EDO4K4EP.mjs +2289 -0
- package/dist/index.d.mts +681 -6
- package/dist/index.d.ts +681 -6
- package/dist/index.js +698 -17
- package/dist/index.mjs +357 -3
- package/dist/utils-IDWBSHJU.mjs +76 -0
- package/package.json +13 -5
- package/dist/chunk-L3QWKSGY.mjs +0 -297
package/dist/cli.mjs
CHANGED
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ProtectGateway,
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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();
|