Skip to content

Commit 1a68ae2

Browse files
authored
feat: model context protocol example (#546)
* feat: initial commit * feat: working claude example * chore: removed wallet read/writes * chore: updated README * chore: added examples to parent READMEs * chore: format/lint * chore: added build to ignore * fix: package dependencies * chore: change build folder to dist * chore: package-lock * chore: fixed package-lock.json * chore: removed dotenv dependency * chore: removed moduleResolution from tsconfig * fix: package-lock * pr feedback * chore: lint format * fix: package-lock * fix: claud config arg for server location * feat: pr feedback
1 parent a98939d commit 1a68ae2

12 files changed

+386
-2
lines changed

CONTRIBUTING.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ agentkit/
2727
│ ├── langchain-privy-chatbot/
2828
│ ├── langchain-solana-chatbot/
2929
│ ├── langchain-twitter-chatbot/
30+
│ ├── model-context-protocol-cdp-server/
3031
│ └── vercel-ai-sdk-cdp-chatbot/
3132
├── python/
3233
│ ├── coinbase-agentkit/

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ agentkit/
150150
│ ├── langchain-privy-chatbot/
151151
│ ├── langchain-solana-chatbot/
152152
│ ├── langchain-twitter-chatbot/
153+
│ ├── model-context-protocol-cdp-server/
153154
│ └── vercel-ai-sdk-cdp-chatbot/
154155
├── python/
155156
│ ├── coinbase-agentkit/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"parser": "@typescript-eslint/parser",
3+
"extends": ["../../.eslintrc.base.json"]
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
docs/
2+
dist/
3+
build/
4+
coverage/
5+
.github/
6+
src/client
7+
**/**/*.json
8+
*.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"tabWidth": 2,
3+
"useTabs": false,
4+
"semi": true,
5+
"singleQuote": false,
6+
"trailingComma": "all",
7+
"bracketSpacing": true,
8+
"arrowParens": "avoid",
9+
"printWidth": 100,
10+
"proseWrap": "never"
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# CDP AgentKit Model Context Protocol Examples - Chatbot Typescript
2+
3+
This example demonstrates an agent setup as a server for Model Context Protocol, allowing Claude Desktop access to the full set of CDP AgentKit actions.
4+
5+
## Ask the chatbot to engage in the Web3 ecosystem!
6+
7+
- "Transfer a portion of your ETH to a random address"
8+
- "What is the price of BTC?"
9+
- "Deploy an NFT that will go super viral!"
10+
- "Deploy an ERC-20 token with total supply 1 billion"
11+
12+
## Prerequisites
13+
14+
### Checking Node Version
15+
16+
Before using the example, ensure that you have the correct version of Node.js installed. The example requires Node.js 18 or higher. You can check your Node version by running:
17+
18+
```bash
19+
node --version
20+
```
21+
22+
If you don't have the correct version, you can install it using [nvm](https://github.com/nvm-sh/nvm):
23+
24+
```bash
25+
nvm install node
26+
```
27+
28+
This will automatically install and use the latest version of Node.
29+
30+
### API Keys
31+
32+
You'll need the following API key:
33+
- [CDP API Key](https://portal.cdp.coinbase.com/access/api)
34+
35+
You'll need to configure the Claude desktop config file with your CDP API keys. Copy the contents from `claude_desktop_config.json` to your Claude desktop config file and update the following:
36+
37+
1. Update the `args` path to match the location of your built index.js file
38+
2. Set your CDP API keys in the `env` section:
39+
- "CDP_API_KEY_NAME"
40+
- "CDP_API_KEY_PRIVATE_KEY"
41+
42+
Then, navigate to the `claude_desktop_config.json` file found in your Claude Desktop apps' settings and update it's contents to the contents of our `claude_desktop_config.json` file.
43+
44+
## Running the example
45+
46+
From the root directory, run:
47+
48+
```bash
49+
npm install
50+
npm run build
51+
```
52+
53+
This will install the dependencies and build the packages locally. The chatbot example uses the local `@coinbase/agentkit-model-context-protocol` and `@coinbase/agentkit` packages. If you make changes to the packages, you can run `npm run build` from root again to rebuild the packages, and your changes will be reflected in the chatbot example.
54+
55+
To use the chatbot, simply open Claude desktop after configuring your API keys. The MCP server will run automatically when you interact with Claude.
56+
57+
## License
58+
59+
Apache-2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"mcpServers": {
3+
"agentkit": {
4+
"command": "node",
5+
"args": [
6+
"/Users/YOUR_USER/YOUR_DIRECTORIES/agentkit/typescript/examples/model-context-protocol-cdp-server/dist/index.js"
7+
],
8+
"env": {
9+
"CDP_API_KEY_NAME": "YOUR_CDP_API_KEY_NAME",
10+
"CDP_API_KEY_PRIVATE_KEY": "YOUR_CDP_API_KEY_PRIVATE_KEY"
11+
}
12+
}
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2+
import { getMcpTools } from "@coinbase/agentkit-model-context-protocol";
3+
import {
4+
AgentKit,
5+
CdpWalletProvider,
6+
wethActionProvider,
7+
walletActionProvider,
8+
erc20ActionProvider,
9+
erc721ActionProvider,
10+
cdpApiActionProvider,
11+
cdpWalletActionProvider,
12+
pythActionProvider,
13+
} from "@coinbase/agentkit";
14+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
16+
17+
/**
18+
* Validates that required environment variables are set
19+
*/
20+
function validateEnvironment(): void {
21+
const requiredVars = ["CDP_API_KEY_NAME", "CDP_API_KEY_PRIVATE_KEY"];
22+
const missingVars = requiredVars.filter(varName => !process.env[varName]);
23+
24+
if (missingVars.length > 0) {
25+
console.error("Error: Required environment variables are not set:");
26+
missingVars.forEach(varName => {
27+
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
28+
});
29+
process.exit(1);
30+
}
31+
32+
if (!process.env.NETWORK_ID) {
33+
console.warn("Warning: NETWORK_ID not set, defaulting to base-sepolia testnet");
34+
}
35+
}
36+
37+
/**
38+
* This function creates a new server instance with the capabilities to handle MCP requests.
39+
* It configures the CDP Wallet Provider with the provided API keys and network ID.
40+
* It then initializes the AgentKit with the configured wallet provider and action providers.
41+
*
42+
* @returns {Promise<Server>} The initialized MCP server
43+
*/
44+
async function initializeServer() {
45+
try {
46+
// Create server instance with capabilities
47+
const server = new Server(
48+
{
49+
name: "cdp-agentkit",
50+
version: "1.0.0",
51+
},
52+
{
53+
capabilities: {
54+
tools: {},
55+
},
56+
},
57+
);
58+
59+
// Configure CDP Wallet Provider
60+
const config = {
61+
apiKeyName: process.env.CDP_API_KEY_NAME!,
62+
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY!,
63+
networkId: process.env.NETWORK_ID || "base-sepolia",
64+
};
65+
66+
const walletProvider = await CdpWalletProvider.configureWithWallet(config);
67+
68+
// Initialize AgentKit
69+
const agentkit = await AgentKit.from({
70+
walletProvider,
71+
actionProviders: [
72+
wethActionProvider(),
73+
pythActionProvider(),
74+
walletActionProvider(),
75+
erc20ActionProvider(),
76+
erc721ActionProvider(),
77+
cdpApiActionProvider({
78+
apiKeyName: config.apiKeyName,
79+
apiKeyPrivateKey: config.apiKeyPrivateKey,
80+
}),
81+
cdpWalletActionProvider({
82+
apiKeyName: config.apiKeyName,
83+
apiKeyPrivateKey: config.apiKeyPrivateKey,
84+
}),
85+
],
86+
});
87+
88+
// Get MCP tools from AgentKit
89+
const { tools, toolHandler } = await getMcpTools(agentkit);
90+
91+
// Set up request handlers
92+
server.setRequestHandler(ListToolsRequestSchema, async () => {
93+
return {
94+
tools,
95+
};
96+
});
97+
98+
server.setRequestHandler(CallToolRequestSchema, async request => {
99+
try {
100+
return await toolHandler(request.params.name, request.params.arguments);
101+
} catch (error) {
102+
console.error(`Error executing tool ${request.params.name}:`, error);
103+
throw new Error(`Tool ${request.params.name} failed: ${error}`);
104+
}
105+
});
106+
107+
return server;
108+
} catch (error) {
109+
console.error("Failed to initialize server:", error);
110+
throw error;
111+
}
112+
}
113+
114+
/**
115+
* Main function to run the MCP server
116+
*/
117+
async function main() {
118+
validateEnvironment();
119+
120+
try {
121+
const server = await initializeServer();
122+
const transport = new StdioServerTransport();
123+
await server.connect(transport);
124+
console.error("CDP AgentKit MCP Server running on stdio");
125+
} catch (error) {
126+
console.error("Fatal error:", error);
127+
process.exit(1);
128+
}
129+
}
130+
131+
if (require.main === module) {
132+
main().catch(error => {
133+
console.error("Fatal error in main():", error);
134+
process.exit(1);
135+
});
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@coinbase/cdp-model-context-protocol-server",
3+
"description": "Coinbase AgentKit Example Model Context Protocol Server",
4+
"version": "1.0.0",
5+
"private": true,
6+
"author": "Coinbase Inc.",
7+
"license": "Apache-2.0",
8+
"bin": {
9+
"agentkit": "./dist/index.js"
10+
},
11+
"scripts": {
12+
"build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
13+
"lint": "eslint -c .eslintrc.json *.ts",
14+
"lint:fix": "eslint -c .eslintrc.json *.ts --fix",
15+
"format": "prettier --write \"**/*.{ts,js,cjs,json,md}\"",
16+
"format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\""
17+
},
18+
"dependencies": {
19+
"@coinbase/agentkit": "^0.2.3",
20+
"@coinbase/agentkit-model-context-protocol": "^0.1.0",
21+
"@modelcontextprotocol/sdk": "^1.6.1",
22+
"zod": "^3.22.4"
23+
},
24+
"devDependencies": {
25+
"nodemon": "^3.1.0",
26+
"ts-node": "^10.9.2"
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"preserveSymlinks": true,
5+
"outDir": "./dist",
6+
"rootDir": ".",
7+
"module": "Node16"
8+
},
9+
"include": ["*.ts"]
10+
}

0 commit comments

Comments
 (0)