#
tokens: 3934/50000 5/5 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | dist/
3 | *.log
4 | .env*
5 | PRIVATE_README.md
6 | vscode
7 | .vscode
8 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Free USDC Transfer MCP Server
 2 | 
 3 | An MCP server implementation enabling free USDC transfers on **[Base](https://base.org)** with **[Coinbase CDP](https://docs.cdp.coinbase.com/)** MPC Wallet integration.
 4 | 
 5 | <img width="1374" alt="image" src="https://github.com/user-attachments/assets/3a58a720-8489-4f02-9075-19c8f264e3cc" />
 6 | 
 7 | ## Features
 8 | 
 9 | - Free USDC Transfers: Send USDC to any address or ENS/BaseName domain on Base - no fees, just simple transfers
10 | - Coinbase MPC Wallet: Create and manage your Coinbase MPC wallet for secure, feeless transactions
11 | - Name Resolution: Automatic support for **ENS** and **BaseName** domains
12 | 
13 | ## Functions
14 | 
15 | ### `tranfer-usdc`
16 | - Description: Analyze the value of the purchased items and transfer USDC to the recipient via the Base chain. Due to the uncertainty of blockchain transaction times, the transaction is only scheduled here and will not wait for the transaction to be completed.
17 | - Inputs:
18 |     - usdc_amount (number): USDC amount, greater than 0.
19 |     - recipient (string): Recipient's on-chain address or ENS domain (e.g., example.eth).
20 | - Behavior:
21 |     - Verifies the recipient's address or resolves ENS domains.
22 |     - Schedules a USDC transfer on the Base chain.
23 |     - Provides a link to view transaction details on BaseScan.
24 | 
25 | ### `create_coinbase_mpc_wallet`
26 | - Description: Create a Coinbase MPC wallet address.
27 | - Behavior:
28 |     - Creates a new Coinbase MPC wallet and saves the seed to a secure file.
29 |     - If a wallet already exists, returns the existing wallet address.
30 |     - The seed file for Coinbase MPC wallets is stored in the Documents directory under the file name mpc_info.json.
31 | 
32 | ## Configuration
33 | 
34 | ### Getting an API Key
35 | 1. Sign up for a [Coinbase CDP account](https://portal.cdp.coinbase.com/)
36 | 2. Generate your API key from the developer dashboard
37 | 
38 | ### Usage with Claude Desktop
39 | 
40 | 1. Add this to your `claude_desktop_config.json`:
41 | ```json
42 | {
43 |   "mcpServers": {
44 |     "free-usdc-transfer": {
45 |       "command": "npx",
46 |       "args": [
47 |         "-y",
48 |         "@magnetai/free-usdc-transfer"
49 |       ],
50 |       "env": {
51 |         "COINBASE_CDP_API_KEY_NAME": "YOUR_COINBASE_CDP_API_KEY_NAME",
52 |         "COINBASE_CDP_PRIVATE_KEY": "YOUR_COINBASE_CDP_PRIVATE_KEY"
53 |       }
54 |     }
55 |   }
56 | }
57 | ```
58 | 
59 | 2. Or install the server with **[magnet-desktop](https://github.com/magnetai/magnet-desktop)**
60 | 
61 | ## License
62 | 
63 | This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
64 | 
65 | ---
66 | 
67 | Crafted by [Magnet Labs](https://magnetlabs.xyz) with our vibrant AI & Crypto community
68 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "compilerOptions": {
 3 |         "target": "ES2022",
 4 |         "module": "Node16",
 5 |         "moduleResolution": "Node16",
 6 |         "outDir": "./dist",
 7 |         "rootDir": "./src",
 8 |         "strict": true,
 9 |         "esModuleInterop": true,
10 |         "skipLibCheck": true,
11 |         "forceConsistentCasingInFileNames": true
12 |     },
13 |     "include": [
14 |         "src/**/*"
15 |     ],
16 |     "exclude": [
17 |         "node_modules",
18 |         "dist"
19 |     ]
20 | }
21 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@magnetai/free-usdc-transfer",
 3 |   "version": "0.1.5",
 4 |   "description": "",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\""
 8 |   },
 9 |   "keywords": [],
10 |   "author": "",
11 |   "license": "ISC",
12 |   "dependencies": {
13 |     "@coinbase/coinbase-sdk": "^0.13.0",
14 |     "@ensdomains/ensjs": "^4.0.2",
15 |     "@modelcontextprotocol/sdk": "^1.1.0",
16 |     "@types/global-agent": "^2.1.3",
17 |     "ethers": "^6.13.5",
18 |     "global-agent": "^3.0.0",
19 |     "zod": "^3.24.1"
20 |   },
21 |   "devDependencies": {
22 |     "@types/node": "^22.10.5",
23 |     "ts-node": "^10.9.2",
24 |     "typescript": "^5.7.3"
25 |   },
26 |   "type": "module",
27 |   "bin": {
28 |     "free-usdc-transfer": "dist/index.js"
29 |   },
30 |   "files": [
31 |     "dist"
32 |   ]
33 | }
34 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import {
  6 |     CallToolRequestSchema,
  7 |     ListToolsRequestSchema,
  8 | } from "@modelcontextprotocol/sdk/types.js";
  9 | import { z } from "zod";
 10 | import * as fs from 'fs/promises';
 11 | import { http } from 'viem'
 12 | import { mainnet } from 'viem/chains'
 13 | import { createEnsPublicClient } from '@ensdomains/ensjs'
 14 | import { Coinbase, Wallet } from "@coinbase/coinbase-sdk";
 15 | import { ethers } from "ethers";
 16 | 
 17 | // import { bootstrap } from 'global-agent';
 18 | // process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://127.0.0.1:10080';
 19 | // process.env.GLOBAL_AGENT_HTTPS_PROXY = 'http://127.0.0.1:10080';
 20 | // process.env.GLOBAL_AGENT_NO_PROXY = 'localhost,127.0.0.1';
 21 | // bootstrap();
 22 | 
 23 | // Base scan
 24 | const BASE_SCAN_ADDR = "https://basescan.org/address/"
 25 | 
 26 | // Check for API key
 27 | const COINBASE_CDP_API_KEY_NAME = process.env.COINBASE_CDP_API_KEY_NAME!;
 28 | if (!COINBASE_CDP_API_KEY_NAME) {
 29 |     console.error("Error: COINBASE_CDP_API_KEY_NAME environment variable is required");
 30 |     process.exit(1);
 31 | }
 32 | const COINBASE_CDP_PRIVATE_KEY = process.env.COINBASE_CDP_PRIVATE_KEY!;
 33 | if (!COINBASE_CDP_PRIVATE_KEY) {
 34 |     console.error("Error: COINBASE_CDP_SECRET environment variable is required");
 35 |     process.exit(1);
 36 | }
 37 | Coinbase.configure({ apiKeyName: COINBASE_CDP_API_KEY_NAME, privateKey: COINBASE_CDP_PRIVATE_KEY });
 38 | 
 39 | // Store
 40 | import os from 'os';
 41 | import path from 'path';
 42 | const homeDir = os.homedir();
 43 | const documentsDir = path.join(homeDir, 'Documents');
 44 | const seedFilePath = path.join(documentsDir, "mpc_info.json");
 45 | 
 46 | // Create server instance
 47 | const server = new Server(
 48 |     {
 49 |         name: "free-usdc-transfer",
 50 |         version: "0.1.3",
 51 |     },
 52 |     {
 53 |         capabilities: {
 54 |             tools: {},
 55 |         },
 56 |     }
 57 | );
 58 | 
 59 | // Define Zod schemas for validation
 60 | const BstsArgumentsSchema = z.object({
 61 |     usdc_amount: z.number().gt(0),
 62 |     recipient: z.string()
 63 | });
 64 | 
 65 | // List available tools
 66 | server.setRequestHandler(ListToolsRequestSchema, async () => {
 67 |     return {
 68 |         tools: [
 69 |             {
 70 |                 name: "tranfer-usdc",
 71 |                 description: "Analyze the value of the purchased items and transfer USDC to the recipient via the Base chain. Due to the uncertainty of blockchain transaction times, the transaction is only scheduled here and will not wait for the transaction to be completed.",
 72 |                 inputSchema: {
 73 |                     type: "object",
 74 |                     properties: {
 75 |                         usdc_amount: {
 76 |                             type: "number",
 77 |                             description: "USDC amount, greater than 0",
 78 |                         },
 79 |                         recipient: {
 80 |                             type: "string",
 81 |                             description: "Recipient's on-chain address or ENS addresses ending in .eth",
 82 |                         }
 83 |                     },
 84 |                     required: ["usdc_amount", "recipient"],
 85 |                 },
 86 |             },
 87 |             {
 88 |                 name: "create_coinbase_mpc_wallet",
 89 |                 description: "Used to create your Coinbase MPC wallet address. The newly created wallet cannot be used directly; the user must first deposit USDC. The transfer after creation requires user confirmation",
 90 |                 inputSchema: {
 91 |                     type: "object"
 92 |                 },
 93 |             }
 94 |         ],
 95 |     };
 96 | });
 97 | 
 98 | // Create the client
 99 | const client = createEnsPublicClient({
100 |     chain: mainnet,
101 |     transport: http(),
102 | })
103 | 
104 | // ENS
105 | async function getAddress(recipient: string) {
106 |     if (recipient.toLowerCase().endsWith('.eth')) {
107 |         return (await client.getAddressRecord({ name: recipient }))?.value
108 |     }
109 |     if (!recipient || recipient.length != 42) {
110 |         return undefined
111 |     }
112 |     return recipient;
113 | };
114 | 
115 | async function createMPCWallet() {
116 |     let wallet = await Wallet.create({ networkId: "base-mainnet" });
117 |     wallet.saveSeedToFile(seedFilePath);
118 |     return (await wallet.getDefaultAddress()).getId();
119 | }
120 | 
121 | async function sendUSDCUseMPCWallet(walletId: string, recipientAddr: string, amount: number) {
122 |     const wallet = await Wallet.fetch(walletId)
123 |     await wallet.loadSeedFromFile(seedFilePath)
124 |     const defaultAddress = await wallet.getDefaultAddress()
125 | 
126 |     await defaultAddress.createTransfer({
127 |         amount: amount,
128 |         assetId: Coinbase.assets.Usdc,
129 |         destination: ethers.getAddress(recipientAddr),
130 |         gasless: true
131 |     })
132 | 
133 |     return defaultAddress.getId()
134 | }
135 | 
136 | async function queryMpcWallet() {
137 |     try {
138 |         const jsonString = await fs.readFile(seedFilePath, 'utf8')
139 |         const ids = Object.keys(JSON.parse(jsonString))
140 |         if (!ids || ids.length === 0) {
141 |             return { mpcAddress: "", mpcId: "" }
142 |         }
143 |         const wallet = await Wallet.fetch(ids[0])
144 |         await wallet.loadSeedFromFile(seedFilePath)
145 |         return { mpcAddress: (await wallet.getDefaultAddress()).getId(), mpcId: ids[0] }
146 |     } catch (err) {
147 |         console.error(`${err}`)
148 |         return { mpcAddress: "", mpcId: "" }
149 |     }
150 | }
151 | 
152 | async function queryMpcWalletId() {
153 |     try {
154 |         const jsonString = await fs.readFile(seedFilePath, 'utf8')
155 |         const ids = Object.keys(JSON.parse(jsonString))
156 |         if (!ids || ids.length === 0) {
157 |             return ""
158 |         }
159 |         return ids[0]
160 |     } catch (err) {
161 |         console.error(`${err}`)
162 |         return ""
163 |     }
164 | }
165 | 
166 | // Handle tool execution
167 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
168 |     const { name, arguments: args } = request.params;
169 |     try {
170 |         if (name === "tranfer-usdc") {
171 |             const { usdc_amount, recipient } = BstsArgumentsSchema.parse(args);
172 |             const mpcId= await queryMpcWalletId();
173 |             if (!mpcId) {
174 |                 return {
175 |                     content: [
176 |                         {
177 |                             type: "text",
178 |                             text: "You haven't created a Coinbase MPC wallet yet",
179 |                         },
180 |                     ],
181 |                 };
182 |             }
183 |             const recipientAddr = await getAddress(recipient)
184 |             if (!recipientAddr) {
185 |                 return {
186 |                     content: [
187 |                         {
188 |                             type: "text",
189 |                             text: 'Invalid address or ENS',
190 |                         },
191 |                     ]
192 |                 }
193 |             }
194 |             const addr= await sendUSDCUseMPCWallet(mpcId, recipientAddr, usdc_amount)
195 |             const linkAddr = BASE_SCAN_ADDR + addr + "#tokentxns"
196 |             return {
197 |                 content: [
198 |                     {
199 |                         type: "text",
200 |                         text: `The transaction (sending ${usdc_amount} USDC to ${recipientAddr}) has been scheduled on the Base chain. You can view the details via the link: ${linkAddr}`,
201 |                     },
202 |                 ],
203 |             };
204 |         } else if (name === "create_coinbase_mpc_wallet") {
205 |             const { mpcAddress } = await queryMpcWallet();
206 |             if (!mpcAddress) {
207 |                 const newMpcAddress = await createMPCWallet()
208 |                 return {
209 |                     content: [
210 |                         {
211 |                             type: "text",
212 |                             text: `Your Coinbase MPC wallet address has been successfully created (${newMpcAddress}). Now please transfer USDC to MPC wallet, and you can later use it to transfer funds to others without fees.`,
213 |                         },
214 |                     ],
215 |                 };
216 |             }
217 |             return {
218 |                 content: [
219 |                     {
220 |                         type: "text",
221 |                         text: `You already have an address, which is ${mpcAddress}`,
222 |                     },
223 |                 ],
224 |             };
225 |         }
226 |         else {
227 |             throw new Error(`Unknown tool: ${name}`);
228 |         }
229 |     } catch (error) {
230 |         if (error instanceof z.ZodError) {
231 |             throw new Error(
232 |                 `Invalid arguments: ${error.errors
233 |                     .map((e) => `${e.path.join(".")}: ${e.message}`)
234 |                     .join(", ")}`
235 |             );
236 |         }
237 |         throw error;
238 |     }
239 | });
240 | 
241 | // Start the server
242 | async function main() {
243 |     const transport = new StdioServerTransport();
244 |     await server.connect(transport);
245 |     console.error("Free USDC transfer MCP Server running on stdio");
246 | }
247 | 
248 | main().catch((error) => {
249 |     console.error("Fatal error in main():", error);
250 |     process.exit(1);
251 | });
252 | 
```