@abstract-foundation/agw-mcp 0.1.0-beta.3 → 0.1.0-beta.5

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 (3) hide show
  1. package/README.md +23 -12
  2. package/dist/index.mjs +55 -10
  3. package/package.json +7 -1
package/README.md CHANGED
@@ -9,13 +9,13 @@ MCP server for [Abstract Global Wallet](https://abs.xyz) session-key workflows
9
9
  ## Quick Start
10
10
 
11
11
  ```bash
12
- npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
12
+ npx -y @abstract-foundation/agw-mcp serve --chain-id 2741
13
13
  ```
14
14
 
15
15
  Or add it to Claude Code directly:
16
16
 
17
17
  ```bash
18
- claude mcp add agw -- npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
18
+ claude mcp add agw -- npx -y @abstract-foundation/agw-mcp serve --chain-id 2741
19
19
  ```
20
20
 
21
21
  ## Setup
@@ -23,10 +23,10 @@ claude mcp add agw -- npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
23
23
  ### 1. Bootstrap a session
24
24
 
25
25
  ```bash
26
- npx -y @abstract-foundation/agw-mcp init --chain-id 11124
26
+ npx -y @abstract-foundation/agw-mcp init --chain-id 2741
27
27
  ```
28
28
 
29
- This opens the hosted onboarding app (`https://app-jarrodwatts.vercel.app`) where you:
29
+ This opens the hosted onboarding app (`https://mcp.abs.xyz` by default) where you:
30
30
 
31
31
  1. Choose a policy preset (or provide custom policy JSON)
32
32
  2. Connect your Abstract Global Wallet
@@ -35,11 +35,12 @@ This opens the hosted onboarding app (`https://app-jarrodwatts.vercel.app`) wher
35
35
  Session data is saved to `~/.agw-mcp/session.json` with `0o600` file permissions. The session signer key is stored separately in `~/.agw-mcp/session-signer.key`.
36
36
  If a previous active session exists locally, the CLI attempts to revoke it on-chain after creating the new one.
37
37
  Bootstrap is single-process per storage directory (lockfile: `~/.agw-mcp/.bootstrap-init.lock`) to prevent concurrent `init` races.
38
+ When local sessions are revoked/cleared, the signer keyfile is deleted as part of local cleanup.
38
39
 
39
40
  ### 2. Start the MCP server
40
41
 
41
42
  ```bash
42
- npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
43
+ npx -y @abstract-foundation/agw-mcp serve --chain-id 2741
43
44
  ```
44
45
 
45
46
  ## Client Configuration
@@ -47,7 +48,7 @@ npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
47
48
  ### Claude Code
48
49
 
49
50
  ```bash
50
- claude mcp add agw -- npx -y @abstract-foundation/agw-mcp serve --chain-id 11124
51
+ claude mcp add agw -- npx -y @abstract-foundation/agw-mcp serve --chain-id 2741
51
52
  ```
52
53
 
53
54
  ### Claude Desktop
@@ -62,7 +63,7 @@ Add to your `claude_desktop_config.json`:
62
63
  "mcpServers": {
63
64
  "agw-mcp": {
64
65
  "command": "npx",
65
- "args": ["-y", "@abstract-foundation/agw-mcp", "serve", "--chain-id", "11124"]
66
+ "args": ["-y", "@abstract-foundation/agw-mcp", "serve", "--chain-id", "2741"]
66
67
  }
67
68
  }
68
69
  }
@@ -78,7 +79,7 @@ Add to your `claude_desktop_config.json`:
78
79
  "mcpServers": {
79
80
  "agw-mcp": {
80
81
  "command": "npx",
81
- "args": ["-y", "@abstract-foundation/agw-mcp", "serve", "--chain-id", "11124"]
82
+ "args": ["-y", "@abstract-foundation/agw-mcp", "serve", "--chain-id", "2741"]
82
83
  }
83
84
  }
84
85
  }
@@ -93,7 +94,7 @@ Use the same JSON block as Claude Desktop in your editor's MCP configuration fil
93
94
  ### Generate config snippet
94
95
 
95
96
  ```bash
96
- npx -y @abstract-foundation/agw-mcp config --npx --chain-id 11124
97
+ npx -y @abstract-foundation/agw-mcp config --npx --chain-id 2741
97
98
  ```
98
99
 
99
100
  ## Tools
@@ -117,7 +118,7 @@ npx -y @abstract-foundation/agw-mcp config --npx --chain-id 11124
117
118
 
118
119
  ## Network Configuration
119
120
 
120
- Defaults to Abstract testnet (chain ID `11124`). Switch to mainnet or override RPC:
121
+ Defaults to Abstract mainnet (chain ID `2741`). Override RPC or switch to testnet when needed:
121
122
 
122
123
  ```bash
123
124
  # Mainnet
@@ -132,15 +133,16 @@ Environment variables are also supported:
132
133
  ```bash
133
134
  AGW_MCP_CHAIN_ID=2741 npx -y @abstract-foundation/agw-mcp serve
134
135
  AGW_MCP_RPC_URL=https://api.mainnet.abs.xyz npx -y @abstract-foundation/agw-mcp serve
135
- AGW_MCP_APP_URL=http://localhost:3001 npx -y @abstract-foundation/agw-mcp init --chain-id 11124
136
+ AGW_MCP_APP_URL=https://mcp.abs.xyz npx -y @abstract-foundation/agw-mcp init --chain-id 2741
136
137
  ```
137
138
 
138
139
  `init` requires `https://` app URLs except for loopback local development URLs (`http://localhost`, `http://127.0.0.1`, `http://[::1]`).
140
+ `init` defaults to `https://mcp.abs.xyz` if no app URL is configured via `--app-url` or `AGW_MCP_APP_URL`.
139
141
 
140
142
  For local hosted-app development:
141
143
 
142
144
  ```bash
143
- npx -y @abstract-foundation/agw-mcp init --chain-id 11124 --app-url http://localhost:3001
145
+ npx -y @abstract-foundation/agw-mcp init --chain-id 2741 --app-url http://localhost:3001
144
146
  ```
145
147
 
146
148
  ## Security Model
@@ -151,6 +153,15 @@ npx -y @abstract-foundation/agw-mcp init --chain-id 11124 --app-url http://local
151
153
  - **Restrictive file permissions**: Session storage directory `0o700`, files `0o600`.
152
154
  - **Stderr-only logging**: stdout is reserved for MCP stdio transport. All operational logs go to stderr.
153
155
 
156
+ ### Real Funds Checklist
157
+
158
+ For production usage with real money:
159
+
160
+ 1. Use a trusted onboarding host (`--app-url` or `AGW_MCP_APP_URL`) and pin it in deployment config.
161
+ 2. Start with minimal intent scope (prefer payments-only) and shortest practical expiry.
162
+ 3. Keep `execute` off by default and run preview-first workflows where possible.
163
+ 4. Revoke sessions after task completion (`revoke_session`) and confirm status with `get_session_status`.
164
+
154
165
  ## Development
155
166
 
156
167
  ```bash
package/dist/index.mjs CHANGED
@@ -117,12 +117,24 @@ var SessionStorage = class {
117
117
  }
118
118
  resolveSignerRefForRuntime(data) {
119
119
  if (data.sessionSignerRef.kind === "keyfile") {
120
- if (!fs.existsSync(data.sessionSignerRef.value)) return null;
120
+ if (!fs.existsSync(data.sessionSignerRef.value)) {
121
+ if (data.status === "revoked") return {
122
+ ...data,
123
+ sessionSignerRef: {
124
+ kind: "raw",
125
+ value: REDACTED_SIGNER_REF$1
126
+ }
127
+ };
128
+ return null;
129
+ }
121
130
  return data;
122
131
  }
123
132
  const rawValue = data.sessionSignerRef.value.trim();
124
133
  if (rawValue === REDACTED_SIGNER_REF$1) {
125
- if (!fs.existsSync(this.signerKeyPath)) return null;
134
+ if (!fs.existsSync(this.signerKeyPath)) {
135
+ if (data.status === "revoked") return data;
136
+ return null;
137
+ }
126
138
  return {
127
139
  ...data,
128
140
  sessionSignerRef: {
@@ -131,7 +143,16 @@ var SessionStorage = class {
131
143
  }
132
144
  };
133
145
  }
134
- if (!PRIVATE_KEY_PATTERN$4.test(rawValue)) return null;
146
+ if (!PRIVATE_KEY_PATTERN$4.test(rawValue)) {
147
+ if (data.status === "revoked") return {
148
+ ...data,
149
+ sessionSignerRef: {
150
+ kind: "raw",
151
+ value: REDACTED_SIGNER_REF$1
152
+ }
153
+ };
154
+ return null;
155
+ }
135
156
  return {
136
157
  ...data,
137
158
  sessionSignerRef: {
@@ -172,11 +193,23 @@ var SessionStorage = class {
172
193
  } : data;
173
194
  fs.writeFileSync(this.filePath, JSON.stringify(this.sanitizeForPersistence(normalized), null, 2), { mode: 384 });
174
195
  }
175
- delete() {
196
+ deleteFile(filePath, wipe = false) {
176
197
  try {
177
- if (fs.existsSync(this.filePath)) fs.unlinkSync(this.filePath);
198
+ if (!fs.existsSync(filePath)) return;
199
+ if (wipe) try {
200
+ const stats = fs.statSync(filePath);
201
+ if (stats.isFile() && stats.size > 0) fs.writeFileSync(filePath, Buffer.alloc(stats.size), { flag: "r+" });
202
+ } catch {}
203
+ fs.unlinkSync(filePath);
178
204
  } catch {}
179
205
  }
206
+ deleteSignerKeyfile() {
207
+ this.deleteFile(this.signerKeyPath, true);
208
+ }
209
+ delete() {
210
+ this.deleteFile(this.filePath);
211
+ this.deleteSignerKeyfile();
212
+ }
180
213
  };
181
214
 
182
215
  //#endregion
@@ -206,7 +239,7 @@ const SUPPORTED_CHAINS = {
206
239
  [abstractTestnet.id]: abstractTestnet,
207
240
  [abstract.id]: abstract
208
241
  };
209
- const DEFAULT_CHAIN_ID = abstractTestnet.id;
242
+ const DEFAULT_CHAIN_ID = abstract.id;
210
243
  function normalizeOptionalString(value) {
211
244
  if (typeof value !== "string") return;
212
245
  const normalized = value.trim();
@@ -891,6 +924,7 @@ const LOOPBACK_HOSTS = new Set([
891
924
  "[::1]"
892
925
  ]);
893
926
  const BOOTSTRAP_LOCK_STALE_MS = 1800 * 1e3;
927
+ const DEFAULT_ONBOARDING_APP_URL = "https://mcp.abs.xyz";
894
928
  function ensurePrivateDir$1(dir) {
895
929
  fs.mkdirSync(dir, {
896
930
  recursive: true,
@@ -1019,7 +1053,11 @@ function isLoopbackHostname(hostname) {
1019
1053
  return LOOPBACK_HOSTS.has(hostname);
1020
1054
  }
1021
1055
  function resolveAppUrl(options) {
1022
- return options.appUrl ?? process.env.AGW_MCP_APP_URL ?? "https://app-jarrodwatts.vercel.app";
1056
+ const explicitAppUrl = options.appUrl?.trim();
1057
+ if (explicitAppUrl) return explicitAppUrl;
1058
+ const envAppUrl = process.env.AGW_MCP_APP_URL?.trim();
1059
+ if (envAppUrl) return envAppUrl;
1060
+ return DEFAULT_ONBOARDING_APP_URL;
1023
1061
  }
1024
1062
  function validateAppUrl(appUrl) {
1025
1063
  let appOrigin;
@@ -1406,7 +1444,7 @@ var AuditLog = class {
1406
1444
  const ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
1407
1445
  const SELECTOR_PATTERN = /^0x[0-9a-fA-F]{8}$/;
1408
1446
  const UINT_PATTERN = /^\d+$/;
1409
- const DEFAULT_MAX_SESSION_LIFETIME_SECONDS = 1440 * 60;
1447
+ const DEFAULT_MAX_SESSION_LIFETIME_SECONDS = 720 * 60 * 60;
1410
1448
  const EQUAL_CONDITION = 1n;
1411
1449
  const UNSAFE_DESTINATION_SELECTORS = new Set([
1412
1450
  "0xa22cb465",
@@ -1644,7 +1682,7 @@ var SessionManager = class {
1644
1682
  constructor(logger, options = {}) {
1645
1683
  this.logger = logger.child("session");
1646
1684
  this.storage = new SessionStorage(options.storageDir);
1647
- this.chainId = options.chainId ?? 11124;
1685
+ this.chainId = options.chainId ?? DEFAULT_CHAIN_ID;
1648
1686
  this.rpcUrl = options.rpcUrl;
1649
1687
  }
1650
1688
  initialize() {
@@ -1678,6 +1716,12 @@ var SessionManager = class {
1678
1716
  source: "local",
1679
1717
  checkedAt
1680
1718
  };
1719
+ if (this.session.status === "revoked") return {
1720
+ status: "Closed",
1721
+ statusCode: 2,
1722
+ source: "local",
1723
+ checkedAt
1724
+ };
1681
1725
  const networkConfig = this.getNetworkConfig(this.session.chainId);
1682
1726
  const sessionClient = createSessionClientFromSessionData({
1683
1727
  session: this.session,
@@ -1714,6 +1758,7 @@ var SessionManager = class {
1714
1758
  updatedAt: updatedAtUnixSeconds
1715
1759
  };
1716
1760
  this.storage.save(this.session);
1761
+ this.storage.deleteSignerKeyfile();
1717
1762
  }
1718
1763
  getChainId() {
1719
1764
  return this.chainId;
@@ -3315,7 +3360,7 @@ var AgwMcpServer = class {
3315
3360
  const logger = new Logger("agw-mcp");
3316
3361
  const program = new Command();
3317
3362
  program.name("agw-mcp").description("Local MCP server for AGW session-key workflows").version("0.1.0");
3318
- program.command("init").description("Bootstrap local AGW MCP session storage").option("--chain-id <chainId>", "EVM chain id (env: AGW_MCP_CHAIN_ID)").option("--rpc-url <rpcUrl>", "RPC URL override (env: AGW_MCP_RPC_URL)").option("--app-url <url>", "Hosted session onboarding URL (env: AGW_MCP_APP_URL)").option("--storage-dir <dir>", "Session storage directory").action(async (options) => {
3363
+ program.command("init").description("Bootstrap local AGW MCP session storage").option("--chain-id <chainId>", "EVM chain id (env: AGW_MCP_CHAIN_ID)").option("--rpc-url <rpcUrl>", "RPC URL override (env: AGW_MCP_RPC_URL)").option("--app-url <url>", "Hosted session onboarding URL (defaults to https://mcp.abs.xyz; env: AGW_MCP_APP_URL)").option("--storage-dir <dir>", "Session storage directory").action(async (options) => {
3319
3364
  const networkConfig = resolveNetworkConfig({
3320
3365
  chainId: options.chainId,
3321
3366
  rpcUrl: options.rpcUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abstract-foundation/agw-mcp",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "MCP server for Abstract Global Wallet session-key workflows",
5
5
  "license": "MIT",
6
6
  "author": "Abstract Foundation",
@@ -71,5 +71,11 @@
71
71
  "tsdown": "^0.20.3",
72
72
  "tsx": "^4.19.3",
73
73
  "typescript": "^5.8.3"
74
+ },
75
+ "pnpm": {
76
+ "overrides": {
77
+ "ajv": "6.14.0",
78
+ "minimatch": "10.2.1"
79
+ }
74
80
  }
75
81
  }