@vaultys/mcp-agent 0.1.0

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.
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SRP Policy Grant CLI
4
+ *
5
+ * Uses the VaultysID Secure Remote Protocol (SRP) to establish a policy
6
+ * agreement between a producer (authority) and an executor (MCP server).
7
+ * This is the canonical VaultysID pattern — both sides exchange certificates,
8
+ * and the policy is cryptographically bound to the SRP metadata.
9
+ *
10
+ * This replaces the manual file-based grant-policy.ts when both parties
11
+ * can communicate over a channel (e.g. in-process via MemoryChannel,
12
+ * or over a network via WebSocket/TCP).
13
+ *
14
+ * Usage:
15
+ * pnpm srp-grant # runs a local in-process SRP handshake
16
+ *
17
+ * What happens:
18
+ * 1. Producer and executor identities are loaded or created
19
+ * 2. A MemoryChannel (bidirectional) connects them
20
+ * 3. grantPolicy() and acceptPolicy() run concurrently
21
+ * 4. Both sides store the agreed policy + SRP certificate
22
+ * 5. The executor can now evaluate intents against the stored policy
23
+ */
24
+ import * as fs from "node:fs";
25
+ import * as path from "node:path";
26
+ import { IdManager, MemoryStorage, MemoryChannel, ExecutionManager, } from "@vaultys/id";
27
+ // ── Configuration ──
28
+ const args = process.argv.slice(2);
29
+ function getArg(name, defaultValue) {
30
+ const idx = args.indexOf(`--${name}`);
31
+ if (idx !== -1 && args[idx + 1])
32
+ return args[idx + 1];
33
+ return defaultValue;
34
+ }
35
+ const policyFile = getArg("policy", "policy.example.json");
36
+ const hours = parseInt(getArg("hours", "24"), 10);
37
+ const workspaceRoot = path.resolve(getArg("workspace", "workspace"));
38
+ const PRODUCER_FILE = "authority.secret.json";
39
+ const EXECUTOR_FILE = "server.identity.json";
40
+ // ── Helpers ──
41
+ async function loadOrCreateIdentity(filePath, label) {
42
+ if (fs.existsSync(filePath)) {
43
+ const data = fs.readFileSync(filePath, "utf-8");
44
+ const store = MemoryStorage().fromString(data);
45
+ const idm = await IdManager.fromStore(store);
46
+ idm.setProtocolVersion(1);
47
+ console.log(`✓ Loaded ${label}: ${idm.vaultysId.did}`);
48
+ return idm;
49
+ }
50
+ const store = MemoryStorage();
51
+ const idm = await IdManager.fromStore(store);
52
+ idm.setProtocolVersion(1);
53
+ fs.writeFileSync(filePath, store.toString());
54
+ console.log(`✓ Generated ${label}: ${idm.vaultysId.did}`);
55
+ console.log(` Saved to: ${filePath}`);
56
+ return idm;
57
+ }
58
+ // ── Main ──
59
+ async function main() {
60
+ console.log("╔══════════════════════════════════════════════════╗");
61
+ console.log("║ VaultysID SRP Policy Agreement ║");
62
+ console.log("╚══════════════════════════════════════════════════╝");
63
+ console.log();
64
+ // 1. Load or create identities
65
+ const producer = await loadOrCreateIdentity(PRODUCER_FILE, "producer (authority)");
66
+ const executor = await loadOrCreateIdentity(EXECUTOR_FILE, "executor (MCP server)");
67
+ // 2. Load and prepare policy
68
+ if (!fs.existsSync(policyFile)) {
69
+ console.error(`✗ Policy file not found: ${policyFile}`);
70
+ process.exit(1);
71
+ }
72
+ const policyTemplate = JSON.parse(fs.readFileSync(policyFile, "utf-8"));
73
+ // Substitute {{WORKSPACE}} placeholders
74
+ if (policyTemplate.scopes) {
75
+ for (const [key, patterns] of Object.entries(policyTemplate.scopes)) {
76
+ if (Array.isArray(patterns)) {
77
+ policyTemplate.scopes[key] = patterns.map((p) => p.replace(/\{\{WORKSPACE\}\}/g, workspaceRoot));
78
+ }
79
+ }
80
+ }
81
+ // Add time bounds
82
+ const now = Date.now();
83
+ const policy = {
84
+ ...policyTemplate,
85
+ not_before: now,
86
+ not_after: now + hours * 3600_000,
87
+ };
88
+ console.log();
89
+ console.log(`✓ Policy loaded: ${policyFile}`);
90
+ console.log(` Workspace root: ${workspaceRoot}`);
91
+ console.log(` Valid from: ${new Date(policy.not_before).toISOString()}`);
92
+ console.log(` Expires: ${new Date(policy.not_after).toISOString()}`);
93
+ // 3. Create ExecutionManagers
94
+ const producerEm = new ExecutionManager(producer);
95
+ const executorEm = new ExecutionManager(executor);
96
+ // 4. Create bidirectional channel (in-process demo)
97
+ const channel = MemoryChannel.createBidirectionnal();
98
+ if (!channel.otherend) {
99
+ throw new Error("Failed to create bidirectional channel");
100
+ }
101
+ console.log();
102
+ console.log("⟳ Starting SRP handshake...");
103
+ // 5. Run grantPolicy / acceptPolicy concurrently
104
+ const [counterpart, result] = await Promise.all([
105
+ producerEm.grantPolicy(channel, policy),
106
+ executorEm.acceptPolicy(channel.otherend),
107
+ ]);
108
+ // 6. Verify
109
+ console.log();
110
+ console.log("✓ SRP handshake complete!");
111
+ console.log(` Producer sees executor: ${counterpart.did}`);
112
+ console.log(` Executor sees producer: ${result.counterpart.did}`);
113
+ // Verify the stored policy certificates
114
+ const producerStored = producerEm.getPolicy(executor.vaultysId.did);
115
+ const executorStored = executorEm.getPolicy(producer.vaultysId.did);
116
+ if (producerStored) {
117
+ const valid = await ExecutionManager.verifyStoredPolicy(producerStored);
118
+ console.log(` Producer certificate: ${valid ? "✓ valid" : "✗ INVALID"}`);
119
+ }
120
+ if (executorStored) {
121
+ const valid = await ExecutionManager.verifyStoredPolicy(executorStored);
122
+ console.log(` Executor certificate: ${valid ? "✓ valid" : "✗ INVALID"}`);
123
+ }
124
+ // 7. Persist updated identities (with policy substore)
125
+ // The stores were already modified by grantPolicy/acceptPolicy
126
+ fs.writeFileSync(PRODUCER_FILE, producer.store.toString());
127
+ fs.writeFileSync(EXECUTOR_FILE, executor.store.toString());
128
+ // 8. Also export files for backward compatibility with file-based server.ts
129
+ // Export the signed policy (from the SRP-stored version)
130
+ if (executorStored) {
131
+ const signedPolicy = executorStored.policy;
132
+ // For file-based usage, we also need the authority's signed version
133
+ // The SRP approach stores the policy without a separate signature field;
134
+ // instead the SRP certificate binds the policy cryptographically.
135
+ // For the file-based server flow, we sign the policy explicitly too.
136
+ const explicitSigned = await producerEm.signPolicy(policy);
137
+ const serializable = {
138
+ ...explicitSigned,
139
+ signature: explicitSigned.signature
140
+ ? Buffer.from(explicitSigned.signature).toString("base64")
141
+ : undefined,
142
+ };
143
+ fs.writeFileSync("policy.signed.json", JSON.stringify(serializable, null, 2));
144
+ console.log(` policy.signed.json: written (for file-based server mode)`);
145
+ }
146
+ // Export authority public identity
147
+ const vid = producer.vaultysId;
148
+ fs.writeFileSync("authority.identity.json", JSON.stringify({
149
+ did: vid.did,
150
+ id: Buffer.from(vid.id).toString("base64"),
151
+ fingerprint: vid.fingerprint,
152
+ }, null, 2));
153
+ // 9. Summary
154
+ console.log();
155
+ console.log("┌──────────────────────────────────────────────┐");
156
+ console.log("│ SRP Policy Agreement Complete! │");
157
+ console.log("├──────────────────────────────────────────────┤");
158
+ console.log(`│ Producer DID: ${producer.vaultysId.did.slice(0, 28)}...│`);
159
+ console.log(`│ Executor DID: ${executor.vaultysId.did.slice(0, 28)}...│`);
160
+ console.log("│ │");
161
+ console.log("│ Policy stored in both identity stores │");
162
+ console.log("│ SRP certificates bind the agreement │");
163
+ console.log("│ │");
164
+ console.log("│ To start the MCP server: │");
165
+ console.log("│ pnpm start │");
166
+ console.log("│ │");
167
+ console.log("│ The server will load the policy from its │");
168
+ console.log("│ identity store (SRP) or from signed file. │");
169
+ console.log("└──────────────────────────────────────────────┘");
170
+ // Show stored policies
171
+ const allPolicies = executorEm.listPolicies();
172
+ console.log(`\nExecutor has ${allPolicies.length} stored policy agreement(s).`);
173
+ }
174
+ main().catch((err) => {
175
+ console.error("Error:", err);
176
+ process.exit(1);
177
+ });
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@vaultys/mcp-agent",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Policy-enforced MCP server powered by VaultysID — cryptographic authorization and audit trail for every AI tool call",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/nicmusic/vaultysid",
10
+ "directory": "typescript/demos/mcp-agent"
11
+ },
12
+ "keywords": [
13
+ "mcp",
14
+ "model-context-protocol",
15
+ "vaultysid",
16
+ "policy",
17
+ "audit",
18
+ "ai-safety",
19
+ "cryptographic",
20
+ "tool-use"
21
+ ],
22
+ "main": "./dist/src/server.js",
23
+ "bin": {
24
+ "vaultys-mcp-agent": "./dist/bin/server.js",
25
+ "vaultys-mcp-init": "./dist/bin/init.js",
26
+ "vaultys-mcp-grant": "./dist/bin/grant-policy.js",
27
+ "vaultys-mcp-audit": "./dist/bin/audit.js"
28
+ },
29
+ "files": [
30
+ "dist/",
31
+ "policy.example.json",
32
+ "README.md",
33
+ "LICENSE"
34
+ ],
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.26.0",
37
+ "@vaultys/id": "^3.0.0-beta7",
38
+ "buffer": "^6.0.3",
39
+ "zod": "^3.25.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.0.0",
43
+ "tsx": "^4.21.0",
44
+ "typescript": "^5.9.0"
45
+ },
46
+ "scripts": {
47
+ "start": "tsx src/server.ts",
48
+ "init": "tsx bin/init.ts",
49
+ "grant-policy": "tsx grant-policy.ts",
50
+ "srp-grant": "tsx srp-grant-policy.ts",
51
+ "audit": "tsx src/audit.ts",
52
+ "test": "tsx test-e2e.ts",
53
+ "build": "tsc && chmod +x dist/bin/*.js"
54
+ }
55
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "version": "1.0",
3
+ "scopes": {
4
+ "fs.read": [
5
+ "{{WORKSPACE}}/**"
6
+ ],
7
+ "fs.write": [
8
+ "{{WORKSPACE}}/**"
9
+ ],
10
+ "fs.list": [
11
+ "{{WORKSPACE}}/**"
12
+ ],
13
+ "proc.exec": [
14
+ "ls",
15
+ "cat",
16
+ "echo",
17
+ "wc",
18
+ "head",
19
+ "tail",
20
+ "grep",
21
+ "find"
22
+ ],
23
+ "net.egress.http": [
24
+ "*"
25
+ ]
26
+ },
27
+ "denied": [
28
+ "secrets.*",
29
+ "pkg.system",
30
+ "proc.privilege"
31
+ ],
32
+ "constraints": {
33
+ "max_runtime": 300,
34
+ "no_shell_features": true
35
+ }
36
+ }