@aspect-warden/mcp-server 0.4.0 → 0.5.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.
- package/dist/index.js +101 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -64,18 +64,58 @@ function decrypt(payload, passphrase) {
|
|
|
64
64
|
return decrypted;
|
|
65
65
|
}
|
|
66
66
|
const STORAGE_KEY = process.env.WARDEN_STORAGE_KEY || `warden-${homedir()}`;
|
|
67
|
-
function
|
|
67
|
+
function ensureStorageDir() {
|
|
68
|
+
if (!existsSync(STORAGE_DIR)) {
|
|
69
|
+
mkdirSync(STORAGE_DIR, { recursive: true, mode: 0o700 });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function saveFullState() {
|
|
73
|
+
if (!storedPrivateKey || !policyEngine)
|
|
74
|
+
return;
|
|
68
75
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
ensureStorageDir();
|
|
77
|
+
const p = policyEngine.getPolicy();
|
|
78
|
+
const state = {
|
|
79
|
+
privateKey: storedPrivateKey,
|
|
80
|
+
agentId: p.agentId,
|
|
81
|
+
policy: {
|
|
82
|
+
maxPerTx: p.maxPerTx.toString(),
|
|
83
|
+
dailyLimit: p.dailyLimit.toString(),
|
|
84
|
+
requireApprovalAbove: p.requireApprovalAbove.toString(),
|
|
85
|
+
cooldownMs: p.cooldownMs,
|
|
86
|
+
allowedTokens: p.allowedTokens,
|
|
87
|
+
blockedTokens: p.blockedTokens,
|
|
88
|
+
allowedRecipients: p.allowedRecipients,
|
|
89
|
+
blockedRecipients: p.blockedRecipients,
|
|
90
|
+
allowedChains: p.allowedChains,
|
|
91
|
+
},
|
|
92
|
+
tracker: policyEngine.exportTracker(),
|
|
93
|
+
auditLog: auditLogger?.exportEntries() ?? [],
|
|
94
|
+
frozen,
|
|
95
|
+
sessionKeys: Array.from(sessionKeys.entries()).map(([key, data]) => ({
|
|
96
|
+
key,
|
|
97
|
+
data: {
|
|
98
|
+
agentAddress: data.agentAddress,
|
|
99
|
+
maxPerTx: data.maxPerTx.toString(),
|
|
100
|
+
dailyLimit: data.dailyLimit.toString(),
|
|
101
|
+
validUntil: data.validUntil,
|
|
102
|
+
cooldownSeconds: data.cooldownSeconds,
|
|
103
|
+
createdAt: data.createdAt,
|
|
104
|
+
revoked: data.revoked,
|
|
105
|
+
txHash: data.txHash,
|
|
106
|
+
},
|
|
107
|
+
})),
|
|
108
|
+
permissionGrants: Array.from(permissionGrants.entries()).map(([key, data]) => ({
|
|
109
|
+
key, data,
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
72
112
|
const json = JSON.stringify(state);
|
|
73
113
|
const encrypted = encrypt(json, STORAGE_KEY);
|
|
74
114
|
writeFileSync(STATE_FILE, encrypted, { mode: 0o600 });
|
|
75
|
-
console.error('[Warden MCP]
|
|
115
|
+
console.error('[Warden MCP] Full state saved to ~/.warden/wallet-state.enc');
|
|
76
116
|
}
|
|
77
117
|
catch (err) {
|
|
78
|
-
console.error('[Warden MCP] Failed to save
|
|
118
|
+
console.error('[Warden MCP] Failed to save state:', err);
|
|
79
119
|
}
|
|
80
120
|
}
|
|
81
121
|
function loadState() {
|
|
@@ -172,22 +212,8 @@ server.tool('warden_create_wallet', 'Create a new policy-enforced wallet for the
|
|
|
172
212
|
policyEngine = new PolicyEngine(policy);
|
|
173
213
|
auditLogger = new AuditLogger({ maxEntries: 10000 });
|
|
174
214
|
frozen = false;
|
|
175
|
-
// Persist
|
|
176
|
-
|
|
177
|
-
privateKey: storedPrivateKey,
|
|
178
|
-
agentId,
|
|
179
|
-
policy: {
|
|
180
|
-
maxPerTx: policy.maxPerTx.toString(),
|
|
181
|
-
dailyLimit: policy.dailyLimit.toString(),
|
|
182
|
-
requireApprovalAbove: policy.requireApprovalAbove.toString(),
|
|
183
|
-
cooldownMs: policy.cooldownMs,
|
|
184
|
-
allowedTokens: policy.allowedTokens,
|
|
185
|
-
blockedTokens: policy.blockedTokens,
|
|
186
|
-
allowedRecipients: policy.allowedRecipients,
|
|
187
|
-
blockedRecipients: policy.blockedRecipients,
|
|
188
|
-
allowedChains: policy.allowedChains,
|
|
189
|
-
},
|
|
190
|
-
});
|
|
215
|
+
// Persist full state for recovery across restarts
|
|
216
|
+
saveFullState();
|
|
191
217
|
return {
|
|
192
218
|
content: [{
|
|
193
219
|
type: 'text',
|
|
@@ -321,7 +347,9 @@ server.tool('warden_transfer', 'Send ERC-20 tokens with policy enforcement. Eval
|
|
|
321
347
|
const valueMicro = BigInt(Math.round(amount * 1e6));
|
|
322
348
|
const decision = policyEngine.evaluate(recipient, valueMicro, tokenAddress, 'ethereum');
|
|
323
349
|
auditLogger.log(decision);
|
|
350
|
+
saveFullState(); // Persist audit log entry (blocked or approved)
|
|
324
351
|
if (!decision.approved) {
|
|
352
|
+
const currentPolicy = policyEngine.getPolicy();
|
|
325
353
|
return {
|
|
326
354
|
content: [{
|
|
327
355
|
type: 'text',
|
|
@@ -331,6 +359,10 @@ server.tool('warden_transfer', 'Send ERC-20 tokens with policy enforcement. Eval
|
|
|
331
359
|
reason: decision.reason,
|
|
332
360
|
ruleTriggered: decision.ruleTriggered,
|
|
333
361
|
riskScore: decision.riskScore,
|
|
362
|
+
currentLimits: {
|
|
363
|
+
maxPerTx: `${Number(currentPolicy.maxPerTx) / 1e6} USDT`,
|
|
364
|
+
dailyLimit: `${Number(currentPolicy.dailyLimit) / 1e6} USDT`,
|
|
365
|
+
},
|
|
334
366
|
}),
|
|
335
367
|
}],
|
|
336
368
|
};
|
|
@@ -371,6 +403,8 @@ server.tool('warden_transfer', 'Send ERC-20 tokens with policy enforcement. Eval
|
|
|
371
403
|
blockNumber: Number(receipt.blockNumber),
|
|
372
404
|
gasUsed: receipt.gasUsed,
|
|
373
405
|
});
|
|
406
|
+
// Persist spending tracker + audit log after successful transfer
|
|
407
|
+
saveFullState();
|
|
374
408
|
return {
|
|
375
409
|
content: [{
|
|
376
410
|
type: 'text',
|
|
@@ -486,6 +520,8 @@ server.tool('warden_update_policy', 'Modify policy rules at runtime. Can update
|
|
|
486
520
|
updatedFields.push('cooldownMs');
|
|
487
521
|
}
|
|
488
522
|
policyEngine.updatePolicy(updates);
|
|
523
|
+
// Persist updated policy so it survives restarts
|
|
524
|
+
saveFullState();
|
|
489
525
|
return {
|
|
490
526
|
content: [{
|
|
491
527
|
type: 'text',
|
|
@@ -498,6 +534,7 @@ server.tool('warden_update_policy', 'Modify policy rules at runtime. Can update
|
|
|
498
534
|
// ============================================================
|
|
499
535
|
server.tool('warden_freeze', 'EMERGENCY: Freeze all wallet operations immediately. No transfers will be allowed until unfrozen.', {}, async () => {
|
|
500
536
|
frozen = true;
|
|
537
|
+
saveFullState();
|
|
501
538
|
return {
|
|
502
539
|
content: [{
|
|
503
540
|
type: 'text',
|
|
@@ -514,6 +551,7 @@ server.tool('warden_freeze', 'EMERGENCY: Freeze all wallet operations immediatel
|
|
|
514
551
|
// ============================================================
|
|
515
552
|
server.tool('warden_unfreeze', 'Resume wallet operations after an emergency freeze.', {}, async () => {
|
|
516
553
|
frozen = false;
|
|
554
|
+
saveFullState();
|
|
517
555
|
return {
|
|
518
556
|
content: [{
|
|
519
557
|
type: 'text',
|
|
@@ -604,6 +642,7 @@ server.tool('warden_create_session_key', 'Create a scoped session key for a sub-
|
|
|
604
642
|
txHash: hash,
|
|
605
643
|
};
|
|
606
644
|
sessionKeys.set(agentAddress.toLowerCase(), sessionData);
|
|
645
|
+
saveFullState();
|
|
607
646
|
return {
|
|
608
647
|
content: [{
|
|
609
648
|
type: 'text',
|
|
@@ -681,6 +720,7 @@ server.tool('warden_revoke_session_key', 'Revoke a previously created session ke
|
|
|
681
720
|
};
|
|
682
721
|
}
|
|
683
722
|
session.revoked = true;
|
|
723
|
+
saveFullState();
|
|
684
724
|
return {
|
|
685
725
|
content: [{
|
|
686
726
|
type: 'text',
|
|
@@ -704,6 +744,7 @@ server.tool('warden_revoke_session_key', 'Revoke a previously created session ke
|
|
|
704
744
|
}
|
|
705
745
|
}
|
|
706
746
|
session.revoked = true;
|
|
747
|
+
saveFullState();
|
|
707
748
|
return {
|
|
708
749
|
content: [{
|
|
709
750
|
type: 'text',
|
|
@@ -934,6 +975,7 @@ server.tool('warden_grant_permissions', 'Grant scoped permissions to an AI agent
|
|
|
934
975
|
txHash,
|
|
935
976
|
};
|
|
936
977
|
permissionGrants.set(agentId, grant);
|
|
978
|
+
saveFullState();
|
|
937
979
|
return {
|
|
938
980
|
content: [{
|
|
939
981
|
type: 'text',
|
|
@@ -1026,6 +1068,7 @@ server.tool('warden_revoke_permissions', 'Revoke all permissions from an AI agen
|
|
|
1026
1068
|
});
|
|
1027
1069
|
frozen = true;
|
|
1028
1070
|
permissionGrants.delete(agentId);
|
|
1071
|
+
saveFullState();
|
|
1029
1072
|
return {
|
|
1030
1073
|
content: [{
|
|
1031
1074
|
type: 'text',
|
|
@@ -1132,8 +1175,42 @@ async function main() {
|
|
|
1132
1175
|
};
|
|
1133
1176
|
policyEngine = new PolicyEngine(restoredPolicy);
|
|
1134
1177
|
auditLogger = new AuditLogger({ maxEntries: 10000 });
|
|
1135
|
-
|
|
1136
|
-
|
|
1178
|
+
// Restore spending tracker
|
|
1179
|
+
if (saved.tracker) {
|
|
1180
|
+
policyEngine.importTracker(saved.tracker);
|
|
1181
|
+
console.error('[Warden MCP] Restored spending tracker');
|
|
1182
|
+
}
|
|
1183
|
+
// Restore audit log
|
|
1184
|
+
if (saved.auditLog && Array.isArray(saved.auditLog)) {
|
|
1185
|
+
auditLogger.loadEntries(saved.auditLog);
|
|
1186
|
+
console.error(`[Warden MCP] Restored ${saved.auditLog.length} audit log entries`);
|
|
1187
|
+
}
|
|
1188
|
+
// Restore frozen state
|
|
1189
|
+
frozen = saved.frozen ?? false;
|
|
1190
|
+
// Restore session keys
|
|
1191
|
+
if (saved.sessionKeys) {
|
|
1192
|
+
for (const { key, data } of saved.sessionKeys) {
|
|
1193
|
+
sessionKeys.set(key, {
|
|
1194
|
+
agentAddress: data.agentAddress,
|
|
1195
|
+
maxPerTx: BigInt(data.maxPerTx || '0'),
|
|
1196
|
+
dailyLimit: BigInt(data.dailyLimit || '0'),
|
|
1197
|
+
validUntil: data.validUntil,
|
|
1198
|
+
cooldownSeconds: data.cooldownSeconds,
|
|
1199
|
+
createdAt: data.createdAt,
|
|
1200
|
+
revoked: data.revoked,
|
|
1201
|
+
txHash: data.txHash,
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
console.error(`[Warden MCP] Restored ${saved.sessionKeys.length} session keys`);
|
|
1205
|
+
}
|
|
1206
|
+
// Restore permission grants
|
|
1207
|
+
if (saved.permissionGrants) {
|
|
1208
|
+
for (const { key, data } of saved.permissionGrants) {
|
|
1209
|
+
permissionGrants.set(key, data);
|
|
1210
|
+
}
|
|
1211
|
+
console.error(`[Warden MCP] Restored ${saved.permissionGrants.length} permission grants`);
|
|
1212
|
+
}
|
|
1213
|
+
console.error(`[Warden MCP] Restored wallet ${walletAddress} (agent: ${saved.agentId}, frozen: ${frozen})`);
|
|
1137
1214
|
}
|
|
1138
1215
|
catch (err) {
|
|
1139
1216
|
console.error('[Warden MCP] Failed to restore wallet state:', err);
|