@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.
Files changed (2) hide show
  1. package/dist/index.js +101 -24
  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,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
- frozen = false;
1136
- 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})`);
1137
1214
  }
1138
1215
  catch (err) {
1139
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.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "warden-mcp": "dist/index.js"