@aspect-warden/mcp-server 0.4.1 → 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.
Files changed (2) hide show
  1. package/dist/index.js +95 -42
  2. 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 saveState(state) {
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
- if (!existsSync(STORAGE_DIR)) {
70
- mkdirSync(STORAGE_DIR, { recursive: true, mode: 0o700 });
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] Wallet state saved to ~/.warden/wallet-state.enc');
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 wallet state:', err);
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 wallet state for recovery across restarts
176
- saveState({
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,6 +347,7 @@ 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) {
325
352
  const currentPolicy = policyEngine.getPolicy();
326
353
  return {
@@ -376,6 +403,8 @@ server.tool('warden_transfer', 'Send ERC-20 tokens with policy enforcement. Eval
376
403
  blockNumber: Number(receipt.blockNumber),
377
404
  gasUsed: receipt.gasUsed,
378
405
  });
406
+ // Persist spending tracker + audit log after successful transfer
407
+ saveFullState();
379
408
  return {
380
409
  content: [{
381
410
  type: 'text',
@@ -492,24 +521,7 @@ server.tool('warden_update_policy', 'Modify policy rules at runtime. Can update
492
521
  }
493
522
  policyEngine.updatePolicy(updates);
494
523
  // Persist updated policy so it survives restarts
495
- if (storedPrivateKey) {
496
- const p = policyEngine.getPolicy();
497
- saveState({
498
- privateKey: storedPrivateKey,
499
- agentId: p.agentId,
500
- policy: {
501
- maxPerTx: p.maxPerTx.toString(),
502
- dailyLimit: p.dailyLimit.toString(),
503
- requireApprovalAbove: p.requireApprovalAbove.toString(),
504
- cooldownMs: p.cooldownMs,
505
- allowedTokens: p.allowedTokens,
506
- blockedTokens: p.blockedTokens,
507
- allowedRecipients: p.allowedRecipients,
508
- blockedRecipients: p.blockedRecipients,
509
- allowedChains: p.allowedChains,
510
- },
511
- });
512
- }
524
+ saveFullState();
513
525
  return {
514
526
  content: [{
515
527
  type: 'text',
@@ -522,6 +534,7 @@ server.tool('warden_update_policy', 'Modify policy rules at runtime. Can update
522
534
  // ============================================================
523
535
  server.tool('warden_freeze', 'EMERGENCY: Freeze all wallet operations immediately. No transfers will be allowed until unfrozen.', {}, async () => {
524
536
  frozen = true;
537
+ saveFullState();
525
538
  return {
526
539
  content: [{
527
540
  type: 'text',
@@ -538,6 +551,7 @@ server.tool('warden_freeze', 'EMERGENCY: Freeze all wallet operations immediatel
538
551
  // ============================================================
539
552
  server.tool('warden_unfreeze', 'Resume wallet operations after an emergency freeze.', {}, async () => {
540
553
  frozen = false;
554
+ saveFullState();
541
555
  return {
542
556
  content: [{
543
557
  type: 'text',
@@ -628,6 +642,7 @@ server.tool('warden_create_session_key', 'Create a scoped session key for a sub-
628
642
  txHash: hash,
629
643
  };
630
644
  sessionKeys.set(agentAddress.toLowerCase(), sessionData);
645
+ saveFullState();
631
646
  return {
632
647
  content: [{
633
648
  type: 'text',
@@ -705,6 +720,7 @@ server.tool('warden_revoke_session_key', 'Revoke a previously created session ke
705
720
  };
706
721
  }
707
722
  session.revoked = true;
723
+ saveFullState();
708
724
  return {
709
725
  content: [{
710
726
  type: 'text',
@@ -728,6 +744,7 @@ server.tool('warden_revoke_session_key', 'Revoke a previously created session ke
728
744
  }
729
745
  }
730
746
  session.revoked = true;
747
+ saveFullState();
731
748
  return {
732
749
  content: [{
733
750
  type: 'text',
@@ -958,6 +975,7 @@ server.tool('warden_grant_permissions', 'Grant scoped permissions to an AI agent
958
975
  txHash,
959
976
  };
960
977
  permissionGrants.set(agentId, grant);
978
+ saveFullState();
961
979
  return {
962
980
  content: [{
963
981
  type: 'text',
@@ -1050,6 +1068,7 @@ server.tool('warden_revoke_permissions', 'Revoke all permissions from an AI agen
1050
1068
  });
1051
1069
  frozen = true;
1052
1070
  permissionGrants.delete(agentId);
1071
+ saveFullState();
1053
1072
  return {
1054
1073
  content: [{
1055
1074
  type: 'text',
@@ -1156,8 +1175,42 @@ async function main() {
1156
1175
  };
1157
1176
  policyEngine = new PolicyEngine(restoredPolicy);
1158
1177
  auditLogger = new AuditLogger({ maxEntries: 10000 });
1159
- frozen = false;
1160
- console.error(`[Warden MCP] Restored wallet ${walletAddress} (agent: ${saved.agentId})`);
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})`);
1161
1214
  }
1162
1215
  catch (err) {
1163
1216
  console.error('[Warden MCP] Failed to restore wallet state:', err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspect-warden/mcp-server",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "warden-mcp": "dist/index.js"