@phantom/mcp-server 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.
package/README.md ADDED
@@ -0,0 +1,498 @@
1
+ # @phantom/mcp-server
2
+
3
+ > **⚠️ PREVIEW DISCLAIMER**
4
+ >
5
+ > This MCP server is currently in **preview** and may break or change at any time without notice. Early adopters should **always use a separate Phantom account** specifically for testing with AI agents.
6
+ >
7
+ > **Phantom makes no guarantees whatsoever around anything your agent may do using this MCP server.** Use at your own risk and never use accounts containing significant assets.
8
+
9
+ An MCP (Model Context Protocol) server that provides LLMs like Claude with direct access to Phantom wallet operations. This enables AI assistants to interact with embedded wallets, view addresses, sign transactions, and sign messages across multiple blockchain networks (Solana, Ethereum, Bitcoin, Sui) through natural language interactions.
10
+
11
+ ## Features
12
+
13
+ - **SSO Authentication**: Seamless integration with Phantom's embedded wallet SSO flow (Google/Apple login)
14
+ - **Session Persistence**: Automatic session management with stamper keys stored in `~/.phantom-mcp/session.json`
15
+ - **Multi-Chain Support**: Works with Solana, Ethereum, Bitcoin, and Sui networks
16
+ - **Three MCP Tools**:
17
+ - `get_wallet_addresses` - Get blockchain addresses for the authenticated embedded wallet
18
+ - `sign_transaction` - Sign transactions across supported chains
19
+ - `sign_message` - Sign UTF-8 messages with automatic chain-specific routing
20
+
21
+ ## Installation
22
+
23
+ ### Option 1: npx (Recommended)
24
+
25
+ Use npx to run the server without global installation. This ensures you always use the latest version:
26
+
27
+ ```bash
28
+ npx -y @phantom/mcp-server
29
+ ```
30
+
31
+ ### Option 2: Global Install
32
+
33
+ Install the package globally for faster startup:
34
+
35
+ ```bash
36
+ npm install -g @phantom/mcp-server
37
+ ```
38
+
39
+ Then run:
40
+
41
+ ```bash
42
+ phantom-mcp
43
+ ```
44
+
45
+ ## Getting Your App ID
46
+
47
+ **Important:** Before you can use the MCP server, you must obtain an App ID from the Phantom Portal. This is required for the early release.
48
+
49
+ ### Steps to Get Your App ID:
50
+
51
+ 1. **Visit the Phantom Portal**: Go to [phantom.com/portal](https://phantom.com/portal)
52
+ 2. **Sign in**: Use your Gmail or Apple account to sign in
53
+ 3. **Create an App**: Click "Create App" and fill in the required details
54
+ 4. **Get Your App ID**: Navigate to the "Phantom Connect" tab to find your App ID
55
+ - Your app is automatically approved for development use
56
+ - Copy the App ID for use in the MCP server configuration
57
+
58
+ **Important Note:** The email you use to sign in to the Phantom Portal **must match** the email you use when authenticating in the MCP server. If these don't match, authentication will fail.
59
+
60
+ Once you have your App ID, you can proceed with the configuration below.
61
+
62
+ ## Usage
63
+
64
+ ### Claude Desktop Configuration
65
+
66
+ Add the MCP server to your Claude Desktop configuration file:
67
+
68
+ **Location:**
69
+
70
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
71
+ - Windows: `%APPDATA%/Claude/claude_desktop_config.json`
72
+
73
+ **Using npx (Recommended):**
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "phantom": {
79
+ "command": "npx",
80
+ "args": ["-y", "@phantom/mcp-server"],
81
+ "env": {
82
+ "PHANTOM_APP_ID": "your_app_id_from_portal"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ **Using global install:**
90
+
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "phantom": {
95
+ "command": "phantom-mcp",
96
+ "env": {
97
+ "PHANTOM_APP_ID": "your_app_id_from_portal"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ After updating the config, restart Claude Desktop to load the server.
105
+
106
+ ### Environment Variables
107
+
108
+ Configure the server behavior using environment variables:
109
+
110
+ **App ID / OAuth Client Credentials:**
111
+
112
+ ```bash
113
+ PHANTOM_APP_ID=your_app_id # Required (App ID from Phantom Portal)
114
+ # OR
115
+ PHANTOM_CLIENT_ID=your_client_id # Alternative to PHANTOM_APP_ID
116
+
117
+ PHANTOM_CLIENT_SECRET=your_client_secret # Optional (for confidential clients)
118
+ ```
119
+
120
+ **Client Types:**
121
+
122
+ - **Public client** (recommended): Provide only `PHANTOM_APP_ID` (or `PHANTOM_CLIENT_ID`). Uses PKCE for security, similar to browser SDK.
123
+ - **Confidential client**: Provide both `PHANTOM_APP_ID` and `PHANTOM_CLIENT_SECRET`. Uses HTTP Basic Auth + PKCE.
124
+
125
+ **Note:** You must obtain your App ID from the [Phantom Portal](https://phantom.com/portal) before using the MCP server. See the "Getting Your App ID" section above for detailed instructions. Both `PHANTOM_APP_ID` and `PHANTOM_CLIENT_ID` are supported for backwards compatibility.
126
+
127
+ **Advanced Configuration (Optional):**
128
+
129
+ Most users won't need to change these settings. Available options:
130
+
131
+ - `PHANTOM_CALLBACK_PORT` - OAuth callback port (default: `8080`)
132
+ - `PHANTOM_CALLBACK_PATH` - OAuth callback path (default: `/callback`)
133
+ - `PHANTOM_MCP_DEBUG` - Enable debug logging (set to `1`)
134
+
135
+ **In Claude Desktop:**
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "phantom": {
141
+ "command": "npx",
142
+ "args": ["-y", "@phantom/mcp-server"],
143
+ "env": {
144
+ "PHANTOM_APP_ID": "your_app_id_from_portal",
145
+ "PHANTOM_CLIENT_SECRET": "your_client_secret"
146
+ }
147
+ }
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### Authentication Flow
153
+
154
+ On first run, the server will:
155
+
156
+ 1. **App ID**: Use App ID from `PHANTOM_APP_ID` (or `PHANTOM_CLIENT_ID`) environment variable
157
+ 2. **Browser Authentication**: Open your default browser to `https://connect.phantom.app` for Google/Apple login
158
+ - **Important**: Use the same email address that you used to sign in to the Phantom Portal
159
+ 3. **SSO Callback**: Start a local server on port 8080 to receive the SSO callback
160
+ 4. **Session Storage**: Save your session (including wallet ID, organization ID, and stamper keys) to `~/.phantom-mcp/session.json`
161
+
162
+ The session file is secured with restrictive permissions (0o600) and contains:
163
+
164
+ - Wallet and organization identifiers
165
+ - Stamper keypair (public key registered with auth server, secret key for signing API requests)
166
+ - User authentication details
167
+
168
+ Sessions use stamper keys which don't expire. The embedded wallet is created during SSO authentication and persists across sessions.
169
+
170
+ ### Manual Testing
171
+
172
+ Test the server directly using the MCP inspector:
173
+
174
+ ```bash
175
+ npx @modelcontextprotocol/inspector npx -y @phantom/mcp-server
176
+ ```
177
+
178
+ This opens an interactive web UI where you can test tool calls without Claude Desktop.
179
+
180
+ ## Available Tools
181
+
182
+ ### 1. get_wallet_addresses
183
+
184
+ Gets all blockchain addresses for the authenticated embedded wallet (Solana, Ethereum, Bitcoin, Sui).
185
+
186
+ **Parameters:**
187
+
188
+ - `derivationIndex` (optional, number): Derivation index for the addresses (default: 0)
189
+
190
+ **Example:**
191
+
192
+ ```json
193
+ {
194
+ "derivationIndex": 0
195
+ }
196
+ ```
197
+
198
+ **Response:**
199
+
200
+ ```json
201
+ {
202
+ "walletId": "05307b6d-2d5a-43d6-8d11-08db650a169b",
203
+ "organizationId": "9b0ea123-5e7f-4dbe-88c5-7d769e2f8c8e",
204
+ "addresses": [
205
+ {
206
+ "addressType": "Solana",
207
+ "address": "H8FpYTgx4Uy9aF9Nk9fCTqKKFLYQ9KfC6UJhMkMDzCBh"
208
+ },
209
+ {
210
+ "addressType": "Ethereum",
211
+ "address": "0x8d8b06e017944f5951418b1182d119a376efb39d"
212
+ },
213
+ {
214
+ "addressType": "BitcoinSegwit",
215
+ "address": "bc1qkce5fvaxe759yu5xle5axlh8c7durjsx2wfhr9"
216
+ },
217
+ {
218
+ "addressType": "Sui",
219
+ "address": "0x039039cf69a336cb84e4c1dbcb3fa0c3f133d11b8146c6f7ed0d9f6817529a62"
220
+ }
221
+ ]
222
+ }
223
+ ```
224
+
225
+ ### 2. sign_transaction
226
+
227
+ Signs a transaction using the authenticated embedded wallet. Supports Solana, Ethereum, Bitcoin, and other chains.
228
+
229
+ **Parameters:**
230
+
231
+ - `walletId` (optional, string): The wallet ID to use for signing (defaults to authenticated wallet)
232
+ - `transaction` (required, string): The transaction to sign (format depends on chain: base64url for Solana, RLP-encoded hex for Ethereum)
233
+ - `networkId` (required, string): Network identifier (e.g., "eip155:1" for Ethereum mainnet, "solana:mainnet" for Solana)
234
+ - `derivationIndex` (optional, number): Derivation index for the account (default: 0)
235
+ - `account` (optional, string): Specific account address to use for simulation/signing
236
+
237
+ **Example:**
238
+
239
+ ```json
240
+ {
241
+ "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQABAgMEBQYH...",
242
+ "networkId": "solana:mainnet",
243
+ "derivationIndex": 0
244
+ }
245
+ ```
246
+
247
+ **Response:**
248
+
249
+ ```json
250
+ {
251
+ "signedTransaction": "base64url-encoded-signed-transaction"
252
+ }
253
+ ```
254
+
255
+ ### 3. sign_message
256
+
257
+ Signs a UTF-8 message using the authenticated embedded wallet. Automatically routes to the correct signing method based on the network (Ethereum vs other chains).
258
+
259
+ **Parameters:**
260
+
261
+ - `walletId` (optional, string): The wallet ID to use for signing (defaults to authenticated wallet)
262
+ - `message` (required, string): The UTF-8 message to sign
263
+ - `networkId` (required, string): Network identifier (e.g., "eip155:1" for Ethereum mainnet, "solana:mainnet" for Solana)
264
+ - `derivationIndex` (optional, number): Derivation index for the account (default: 0)
265
+
266
+ **Example:**
267
+
268
+ ```json
269
+ {
270
+ "message": "Hello, Phantom!",
271
+ "networkId": "solana:mainnet"
272
+ }
273
+ ```
274
+
275
+ **Response:**
276
+
277
+ ```json
278
+ {
279
+ "signature": "base64url-encoded-signature"
280
+ }
281
+ ```
282
+
283
+ ## Configuration
284
+
285
+ ### Environment Variables
286
+
287
+ The MCP server supports the following environment variables:
288
+
289
+ #### Debug Logging
290
+
291
+ Enable debug logging to see detailed execution traces:
292
+
293
+ - `DEBUG=1` - Enable debug logging
294
+ - `PHANTOM_MCP_DEBUG=1` - Enable debug logging (alternative)
295
+
296
+ Debug logs are written to stderr and appear in Claude Desktop's MCP server logs.
297
+
298
+ ### Session Storage
299
+
300
+ Sessions are stored in `~/.phantom-mcp/session.json` with the following security measures:
301
+
302
+ - Directory permissions: `0o700` (rwx for user only)
303
+ - File permissions: `0o600` (rw for user only)
304
+ - Contains: Wallet ID, organization ID, stamper keys, user authentication details
305
+
306
+ **Session persistence:**
307
+
308
+ - Sessions use stamper keypair authentication which doesn't expire
309
+ - Stamper public key is registered with the auth server during SSO
310
+ - Stamper secret key is used to sign all API requests
311
+ - Sessions persist indefinitely until explicitly deleted
312
+
313
+ **To reset your session:**
314
+
315
+ 1. Delete the session file:
316
+ ```bash
317
+ rm ~/.phantom-mcp/session.json
318
+ ```
319
+ 2. Restart Claude Desktop (the server will re-authenticate on next use)
320
+
321
+ ## Security
322
+
323
+ ### OAuth Flow Security
324
+
325
+ - Uses PKCE (Proof Key for Code Exchange) for secure OAuth authentication
326
+ - App IDs are pre-registered through the Phantom Portal
327
+ - Session ID validation prevents replay attacks
328
+ - Callback server uses ephemeral localhost binding
329
+
330
+ ### Session Security
331
+
332
+ - Session files have restrictive Unix permissions (user-only read/write)
333
+ - API keys are generated using cryptographically secure random sources
334
+ - Tokens are encrypted in transit (HTTPS)
335
+ - No plaintext credentials are stored
336
+
337
+ ### Network Security
338
+
339
+ - All API requests use HTTPS
340
+ - Request signing with API key stamper prevents tampering
341
+ - Session tokens are bearer tokens with limited scope
342
+
343
+ ## Troubleshooting
344
+
345
+ ### Browser Doesn't Open
346
+
347
+ **Problem:** The OAuth flow tries to open your browser but fails.
348
+
349
+ **Solutions:**
350
+
351
+ - Ensure you have a default browser configured
352
+ - Manually visit the URL shown in the logs
353
+ - Check if the `open` command works in your terminal: `open https://phantom.app`
354
+
355
+ ### Port 8080 Already in Use
356
+
357
+ **Problem:** Cannot bind OAuth callback server to port 8080.
358
+
359
+ **Error:** `EADDRINUSE: address already in use :::8080`
360
+
361
+ **Solutions:**
362
+
363
+ - Stop the process using port 8080: `lsof -ti:8080 | xargs kill`
364
+ - Change the callback port: Set `PHANTOM_CALLBACK_PORT` environment variable to a different port
365
+
366
+ ### Authentication Email Mismatch
367
+
368
+ **Problem:** Authentication fails or you can't access your wallet.
369
+
370
+ **Solution:** Ensure you're using the **same email address** for both:
371
+
372
+ - Signing in to the Phantom Portal (where you created your app)
373
+ - Authenticating in the MCP server (Google/Apple login)
374
+
375
+ If the emails don't match, authentication will fail.
376
+
377
+ ### Session Not Persisting
378
+
379
+ **Problem:** The server asks you to authenticate every time.
380
+
381
+ **Solutions:**
382
+
383
+ - Check session file exists: `ls -la ~/.phantom-mcp/session.json`
384
+ - Verify file permissions: `chmod 600 ~/.phantom-mcp/session.json`
385
+ - Check logs for session expiry messages
386
+ - Ensure `~/.phantom-mcp` directory has correct permissions: `chmod 700 ~/.phantom-mcp`
387
+
388
+ ### MCP Server Not Loading in Claude
389
+
390
+ **Problem:** Claude Desktop doesn't show the Phantom tools.
391
+
392
+ **Solutions:**
393
+
394
+ 1. Verify config file syntax is valid JSON
395
+ 2. Check Claude Desktop logs:
396
+ - macOS: `~/Library/Logs/Claude/`
397
+ - Windows: `%APPDATA%/Claude/logs/`
398
+ 3. Restart Claude Desktop after config changes
399
+ 4. Test the server manually with MCP inspector (see Manual Testing section)
400
+
401
+ ### Authentication Timeout
402
+
403
+ **Problem:** Authentication flow times out before you complete it.
404
+
405
+ **Solutions:**
406
+
407
+ - The OAuth callback server waits 5 minutes by default
408
+ - Complete the authentication flow promptly
409
+ - If timeout occurs, restart Claude Desktop to retry
410
+
411
+ ### Invalid Session Error
412
+
413
+ **Problem:** Session exists but is rejected by API.
414
+
415
+ **Solutions:**
416
+
417
+ - Verify your App ID is correct (check the Phantom Portal)
418
+ - Ensure the email used for authentication matches the Portal email
419
+ - Delete session file: `rm ~/.phantom-mcp/session.json`
420
+ - Restart Claude Desktop
421
+ - Re-authenticate when prompted
422
+
423
+ ## Development
424
+
425
+ ### Prerequisites
426
+
427
+ - Node.js 18+ and yarn
428
+ - TypeScript 5+
429
+
430
+ ### Building
431
+
432
+ ```bash
433
+ # Install dependencies
434
+ yarn install
435
+
436
+ # Build the project
437
+ yarn build
438
+
439
+ # Watch mode for development
440
+ yarn dev
441
+ ```
442
+
443
+ ### Testing
444
+
445
+ ```bash
446
+ # Run all tests
447
+ yarn test
448
+
449
+ # Watch mode
450
+ yarn test:watch
451
+
452
+ # Check types
453
+ yarn check-types
454
+ ```
455
+
456
+ ### Linting
457
+
458
+ ```bash
459
+ # Run ESLint
460
+ yarn lint
461
+
462
+ # Format code with Prettier
463
+ yarn prettier
464
+ ```
465
+
466
+ ### Running Locally
467
+
468
+ You can test the MCP server locally before installing:
469
+
470
+ ```bash
471
+ # Build first
472
+ yarn build
473
+
474
+ # Run directly
475
+ node dist/index.js
476
+
477
+ # Or using the bin wrapper
478
+ ./bin/phantom-mcp
479
+ ```
480
+
481
+ ## Contributing
482
+
483
+ This package is part of the [Phantom Connect SDK](https://github.com/phantom/phantom-connect-sdk) monorepo. Please refer to the main repository for contribution guidelines.
484
+
485
+ ## License
486
+
487
+ See the main repository [LICENSE](../../LICENSE) file.
488
+
489
+ ## Support
490
+
491
+ - [Phantom Documentation](https://docs.phantom.com)
492
+ - [GitHub Issues](https://github.com/phantom/phantom-connect-sdk/issues)
493
+
494
+ ## Related Packages
495
+
496
+ - [@phantom/server-sdk](../server-sdk) - Server-side SDK for Phantom integration
497
+ - [@phantom/client](../client) - Client library for Phantom API
498
+ - [@phantom/react-sdk](../react-sdk) - React SDK for browser applications
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("../dist/index.js");
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import { PhantomClient } from '@phantom/client';
3
+
4
+ /**
5
+ * Complete session data stored on disk
6
+ *
7
+ * Note: SSO flow uses stamper keys for API authentication, not OAuth tokens
8
+ */
9
+ interface SessionData {
10
+ walletId: string;
11
+ organizationId: string;
12
+ authUserId: string;
13
+ stamperKeys: {
14
+ publicKey: string;
15
+ secretKey: string;
16
+ };
17
+ createdAt: number;
18
+ updatedAt: number;
19
+ }
20
+
21
+ /**
22
+ * SessionManager orchestrates the complete session lifecycle:
23
+ * - Loads existing sessions from storage
24
+ * - Handles authentication when needed
25
+ * - Creates and manages PhantomClient instances
26
+ * - Provides session data access
27
+ */
28
+
29
+ /**
30
+ * Configuration options for SessionManager
31
+ */
32
+ interface SessionManagerOptions {
33
+ /** Base URL for OAuth authorization server (default: https://auth.phantom.app or PHANTOM_AUTH_BASE_URL env var) */
34
+ authBaseUrl?: string;
35
+ /** Base URL for Phantom Connect (default: https://connect.phantom.app or PHANTOM_CONNECT_BASE_URL env var) */
36
+ connectBaseUrl?: string;
37
+ /** Base URL for Phantom API (default: https://api.phantom.app or PHANTOM_API_BASE_URL env var) */
38
+ apiBaseUrl?: string;
39
+ /** Port for local OAuth callback server (default: 8080 or PHANTOM_CALLBACK_PORT env var) */
40
+ callbackPort?: number;
41
+ /** Path for OAuth callback (default: /callback or PHANTOM_CALLBACK_PATH env var) */
42
+ callbackPath?: string;
43
+ /** Application identifier prefix (default: phantom-mcp) */
44
+ appId?: string;
45
+ /** Directory to store session data (default: ~/.phantom-mcp) */
46
+ sessionDir?: string;
47
+ }
48
+ /**
49
+ * SessionManager handles session lifecycle, auto-authentication, and PhantomClient creation
50
+ *
51
+ * Usage:
52
+ * ```typescript
53
+ * const manager = new SessionManager();
54
+ * await manager.initialize(); // Loads session or authenticates
55
+ * const client = manager.getClient();
56
+ * const session = manager.getSession();
57
+ * ```
58
+ */
59
+ declare class SessionManager {
60
+ private readonly authBaseUrl;
61
+ private readonly connectBaseUrl?;
62
+ private readonly apiBaseUrl;
63
+ private readonly callbackPort;
64
+ private readonly callbackPath;
65
+ private readonly appId;
66
+ private readonly storage;
67
+ private readonly logger;
68
+ private session;
69
+ private client;
70
+ /**
71
+ * Creates a new SessionManager
72
+ *
73
+ * @param options - Configuration options
74
+ */
75
+ constructor(options?: SessionManagerOptions);
76
+ /**
77
+ * Initializes the session manager
78
+ * Loads existing session or authenticates if needed
79
+ *
80
+ * @throws Error if authentication fails
81
+ */
82
+ initialize(): Promise<void>;
83
+ /**
84
+ * Returns the initialized PhantomClient
85
+ *
86
+ * @returns PhantomClient instance
87
+ * @throws Error if not initialized
88
+ */
89
+ getClient(): PhantomClient;
90
+ /**
91
+ * Returns the current session data
92
+ *
93
+ * @returns Current session data
94
+ * @throws Error if not initialized
95
+ */
96
+ getSession(): SessionData;
97
+ /**
98
+ * Resets the session by clearing stored data and re-authenticating
99
+ *
100
+ * @throws Error if authentication fails
101
+ */
102
+ resetSession(): Promise<void>;
103
+ /**
104
+ * Executes the SSO flow and creates a new session
105
+ * Steps:
106
+ * 1. Execute SSO flow to get wallet/org IDs and stamper keypair
107
+ * 2. Create SessionData with SSO result and stamper keys
108
+ * 3. Save to storage
109
+ * 4. Create PhantomClient
110
+ *
111
+ * Note: Stamper keypair is generated during SSO flow and public key is sent to auth server
112
+ *
113
+ * @throws Error if SSO flow fails
114
+ */
115
+ private authenticate;
116
+ /**
117
+ * Creates a PhantomClient instance from the current session
118
+ * Steps:
119
+ * 1. Create ApiKeyStamper with session keypair
120
+ * 2. Create PhantomClient with stamper, organizationId, and app headers
121
+ * 3. Set walletType to 'user-wallet'
122
+ *
123
+ * @throws Error if session is not available
124
+ */
125
+ private createClient;
126
+ }
127
+
128
+ export { SessionManager };