diff --git a/typescript/create-onchain-agent/templates/mcp/.gitignore b/typescript/create-onchain-agent/templates/mcp/.gitignore new file mode 100644 index 000000000..ba816227e --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +build \ No newline at end of file diff --git a/typescript/create-onchain-agent/templates/mcp/README.md b/typescript/create-onchain-agent/templates/mcp/README.md new file mode 100644 index 000000000..e9eec0439 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/README.md @@ -0,0 +1,58 @@ +# Onchain Claude Desktop Powered by AgentKit + +This is a [Model Context Protocol Server](https://modelcontextprotocol.io/quickstart/server) project bootstrapped with `create-onchain-agent`. + +It integrates [AgentKit](https://github.com/coinbase/agentkit) to provide Claude Desktop with AI-driven interactions with on-chain capabilities. + +## Getting Started + +First, install dependencies: + +```sh +npm install +``` + +Then, configure your environment variables in, `claude_desktop_config.json`, if needed. For example, if you are using CDP, you will need to fill in your CDP API key. + +Copy the `claude_desktop_config.json` file to your Claude Desktop config directory: + +```sh +cp claude_desktop_config.json ~/Library/Application\ Support/Claude/claude_desktop_config.json +``` + +Build the server: + +```sh +npm run build +``` + +Open Claude Desktop and start prompting Claude to do things onchain! For example, ask it to print your wallet details. + +## Configuring Your Agent + +You can [modify your configuration](https://github.com/coinbase/agentkit/tree/main/typescript/agentkit#usage) of the agent. By default, your agentkit configuration occurs in the `./src/getAgentKit.ts` file, and agent instantiation + server setup occurs in the `./src/index.ts` file. + +--- + +## Next Steps + +- Explore the AgentKit README: [AgentKit Documentation](https://github.com/coinbase/agentkit) +- Learn more about available Wallet Providers & Action Providers. +- Experiment with custom Action Providers for your specific use case. + +--- + +## Learn More + +- [Learn more about CDP](https://docs.cdp.coinbase.com/) +- [Learn more about AgentKit](https://docs.cdp.coinbase.com/agentkit/docs/welcome) +- [Learn more about Model Context Protocol](https://modelcontextprotocol.io/quickstart/server) + +--- + +## Contributing + +Interested in contributing to AgentKit? Follow the contribution guide: + +- [Contribution Guide](https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md) +- Join the discussion on [Discord](https://discord.gg/CDP) diff --git a/typescript/create-onchain-agent/templates/mcp/package.json b/typescript/create-onchain-agent/templates/mcp/package.json new file mode 100644 index 000000000..4bd8474f4 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/package.json @@ -0,0 +1,26 @@ +{ + "name": "mcp", + "version": "0.1.0", + "private": true, + "main": "index.js", + "type": "module", + "bin": { + "agentkit": "./build/index.js" + }, + "scripts": { + "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"" + }, + "dependencies": { + "@coinbase/agentkit": "^0.3.0", + "@coinbase/agentkit-model-context-protocol": "^0.2.0", + "@modelcontextprotocol/sdk": "^1.6.1", + "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0", + "viem": "^2.23.7" + }, + "devDependencies": { + "@types/node": "^22.13.9", + "tsx": "^4.19.3", + "typescript": "^5.8.2" + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/claude_desktop_config.json new file mode 100644 index 000000000..03df432bb --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/claude_desktop_config.json @@ -0,0 +1,15 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "PRIVATE_KEY": "", + "RPC_URL": "", + "CHAIN_ID": "", + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/getAgentKit.ts new file mode 100644 index 000000000..29adf7426 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/custom-emv/viem/getAgentKit.ts @@ -0,0 +1,77 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + pythActionProvider, + ViemWalletProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import { createWalletClient, Hex, http } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + let privateKey = process.env.PRIVATE_KEY as Hex; + if (!privateKey) { + privateKey = generatePrivateKey(); + } + const account = privateKeyToAccount(privateKey); + + const rpcUrl = process.env.RPC_URL as string; + const chainId = process.env.CHAIN_ID as string; + const client = createWalletClient({ + account, + // Customize the chain metadata to match your custom chain + chain: { + id: parseInt(chainId), + rpcUrls: { + default: { + http: [rpcUrl], + }, + }, + name: "Custom Chain", + nativeCurrency: { + name: "Ether", + symbol: "ETH", + decimals: 18, + }, + }, + transport: http(), + }); + const walletProvider = new ViemWalletProvider(client); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/claude_desktop_config.json new file mode 100644 index 000000000..bdbcda1bc --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/claude_desktop_config.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "", + "NETWORK_ID": "base-sepolia" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/getAgentKit.ts new file mode 100644 index 000000000..279a2a791 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/cdp/getAgentKit.ts @@ -0,0 +1,50 @@ +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + CdpWalletProvider, + erc20ActionProvider, + pythActionProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const walletProvider = await CdpWalletProvider.configureWithWallet({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + networkId: process.env.NETWORK_ID || "base-sepolia", + }); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + cdpWalletActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ], + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/claude_desktop_config.json new file mode 100644 index 000000000..05fef20fd --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/claude_desktop_config.json @@ -0,0 +1,18 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "PRIVY_APP_ID": "", + "PRIVY_APP_SECRET": "", + "PRIVY_WALLET_ID": "", + "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY": "", + "PRIVY_WALLET_AUTHORIZATION_KEY_ID": "", + "CHAIN_ID": "84532", + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/getAgentKit.ts new file mode 100644 index 000000000..38d5726fe --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/privy/getAgentKit.ts @@ -0,0 +1,62 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + PrivyWalletConfig, + PrivyWalletProvider, + pythActionProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const config: PrivyWalletConfig = { + appId: process.env.PRIVY_APP_ID as string, + appSecret: process.env.PRIVY_APP_SECRET as string, + walletId: process.env.PRIVY_WALLET_ID as string, + chainId: process.env.CHAIN_ID, + authorizationPrivateKey: process.env.PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + }; + + if (!config.chainId) { + console.log("Warning: CHAIN_ID not set, defaulting to 84532 (base-sepolia)"); + config.chainId = "84532"; + } + const walletProvider = await PrivyWalletProvider.configureWithWallet(config); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/claude_desktop_config.json new file mode 100644 index 000000000..b1efe5c6f --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/claude_desktop_config.json @@ -0,0 +1,14 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "", + "PRIVATE_KEY": "", + "NETWORK_ID": "base-sepolia" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/getAgentKit.ts new file mode 100644 index 000000000..18519bb09 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/smart/getAgentKit.ts @@ -0,0 +1,60 @@ +import { + AgentKit, + cdpApiActionProvider, + cdpWalletActionProvider, + erc20ActionProvider, + pythActionProvider, + SmartWalletProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import { Hex } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + let privateKey: Hex | null = null; + + if (!privateKey) { + privateKey = (process.env.PRIVATE_KEY || generatePrivateKey()) as Hex; + } + + const signer = privateKeyToAccount(privateKey); + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const walletProvider = await SmartWalletProvider.configureWithWallet({ + networkId: process.env.NETWORK_ID || "base-sepolia", + signer, + paymasterUrl: undefined, // Sponsor transactions: https://docs.cdp.coinbase.com/paymaster/docs/welcome + }); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + cdpWalletActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ], + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/claude_desktop_config.json new file mode 100644 index 000000000..44200e4b9 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/claude_desktop_config.json @@ -0,0 +1,14 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "PRIVATE_KEY": "", + "NETWORK_ID": "base-sepolia", + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/getAgentKit.ts new file mode 100644 index 000000000..9691d13e1 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/evm/viem/getAgentKit.ts @@ -0,0 +1,64 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + erc20ActionProvider, + NETWORK_ID_TO_VIEM_CHAIN, + pythActionProvider, + ViemWalletProvider, + walletActionProvider, + wethActionProvider, +} from "@coinbase/agentkit"; +import { createWalletClient, Hex, http } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + let privateKey = process.env.PRIVATE_KEY as Hex; + if (!privateKey) { + privateKey = generatePrivateKey(); + } + + const account = privateKeyToAccount(privateKey); + const networkId = process.env.NETWORK_ID as string; + + const client = createWalletClient({ + account, + chain: NETWORK_ID_TO_VIEM_CHAIN[networkId], + transport: http(), + }); + const walletProvider = new ViemWalletProvider(client); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + wethActionProvider(), + pythActionProvider(), + walletActionProvider(), + erc20ActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/claude_desktop_config.json new file mode 100644 index 000000000..0408c2e4f --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/claude_desktop_config.json @@ -0,0 +1,18 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "PRIVY_APP_ID": "", + "PRIVY_APP_SECRET": "", + "PRIVY_WALLET_ID": "", + "PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY": "", + "PRIVY_WALLET_AUTHORIZATION_KEY_ID": "", + "NETWORK_ID": "solana-devnet", + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/getAgentKit.ts new file mode 100644 index 000000000..b6bd3381c --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/privy/getAgentKit.ts @@ -0,0 +1,57 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + jupiterActionProvider, + PrivyWalletConfig, + PrivyWalletProvider, + splActionProvider, + walletActionProvider, +} from "@coinbase/agentkit"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + const config: PrivyWalletConfig = { + appId: process.env.PRIVY_APP_ID as string, + appSecret: process.env.PRIVY_APP_SECRET as string, + walletId: process.env.PRIVY_WALLET_ID as string, + authorizationPrivateKey: process.env.PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY, + authorizationKeyId: process.env.PRIVY_WALLET_AUTHORIZATION_KEY_ID, + chainType: "solana", + networkId: process.env.NETWORK_ID, + }; + + const walletProvider = await PrivyWalletProvider.configureWithWallet(config); + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + walletActionProvider(), + splActionProvider(), + jupiterActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/claude_desktop_config.json b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/claude_desktop_config.json new file mode 100644 index 000000000..6337f5def --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/claude_desktop_config.json @@ -0,0 +1,15 @@ +{ + "mcpServers": { + "agentkit": { + "command": "node", + "args": ["{absolutePath}/build/index.js"], + "env": { + "SOLANA_PRIVATE_KEY": "", + "SOLANA_RPC_URL": "", + "NETWORK_ID": "solana-devnet", + "CDP_API_KEY_NAME": "", + "CDP_API_KEY_PRIVATE_KEY": "" + } + } + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/getAgentKit.ts new file mode 100644 index 000000000..cefb0b26e --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/agentkit/svm/solana-keypair/getAgentKit.ts @@ -0,0 +1,64 @@ +import { + ActionProvider, + AgentKit, + cdpApiActionProvider, + jupiterActionProvider, + SOLANA_NETWORK_ID, + SolanaKeypairWalletProvider, + splActionProvider, + walletActionProvider, +} from "@coinbase/agentkit"; +import { Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; + +/** + * Get the AgentKit instance. + * + * @returns {Promise} The AgentKit instance + */ +export async function getAgentKit(): Promise { + try { + // Setup Private Key + let privateKey = process.env.SOLANA_PRIVATE_KEY as string; + if (!privateKey) { + const keypair = Keypair.generate(); + privateKey = bs58.encode(keypair.secretKey); + } + + // Initialize WalletProvider: https://docs.cdp.coinbase.com/agentkit/docs/wallet-management + // Configure Solana Keypair Wallet Provider + const rpcUrl = process.env.SOLANA_RPC_URL; + let walletProvider: SolanaKeypairWalletProvider; + if (rpcUrl) { + walletProvider = await SolanaKeypairWalletProvider.fromRpcUrl(rpcUrl, privateKey); + } else { + const network = (process.env.NETWORK_ID ?? "solana-devnet") as SOLANA_NETWORK_ID; + walletProvider = await SolanaKeypairWalletProvider.fromNetwork(network, privateKey); + } + + // Initialize AgentKit: https://docs.cdp.coinbase.com/agentkit/docs/agent-actions + const actionProviders: ActionProvider[] = [ + walletActionProvider(), + splActionProvider(), + jupiterActionProvider(), + ]; + const canUseCdpApi = process.env.CDP_API_KEY_NAME && process.env.CDP_API_KEY_PRIVATE_KEY; + if (canUseCdpApi) { + actionProviders.push( + cdpApiActionProvider({ + apiKeyName: process.env.CDP_API_KEY_NAME, + apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY, + }), + ); + } + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders, + }); + + return agentkit; + } catch (error) { + console.error("Error initializing agent:", error); + throw new Error("Failed to initialize agent"); + } +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/getAgentKit.ts b/typescript/create-onchain-agent/templates/mcp/src/getAgentKit.ts new file mode 100644 index 000000000..fbffc826a --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/getAgentKit.ts @@ -0,0 +1,15 @@ +import { AgentKit } from "@coinbase/agentkit"; + +/** + * A stub function that throws a "Not implemented" error. + * + * This is a placeholder function for the implementation that is selected. + * The actual implementation is selected by the cli user that generated this framework. + * + * @returns {Promise} The initialized AI agent. + * + * @throws {Error} Throws a "Not implemented" error. + */ +export async function getAgentKit(): Promise { + throw new Error("Not implemented"); +} diff --git a/typescript/create-onchain-agent/templates/mcp/src/index.ts b/typescript/create-onchain-agent/templates/mcp/src/index.ts new file mode 100644 index 000000000..3dabffae1 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/src/index.ts @@ -0,0 +1,47 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; +import { getMcpTools } from "@coinbase/agentkit-model-context-protocol"; +import { getAgentKit } from "./getAgentKit.js"; + +/** + * This is the main entry point for the MCP agent. + * It creates a new agent and starts the server. + */ +async function main() { + const agentKit = await getAgentKit(); + + const { tools, toolHandler } = await getMcpTools(agentKit); + + const server = new Server( + { + name: "agentkit", + version: "0.1.0", + }, + { + capabilities: { + tools: {}, + }, + }, + ); + + server.setRequestHandler(ListToolsRequestSchema, async () => { + return { + tools, + }; + }); + + server.setRequestHandler(CallToolRequestSchema, async request => { + try { + return toolHandler(request.params.name, request.params.arguments); + } catch (error) { + throw new Error(`Tool ${request.params.name} failed: ${error}`); + } + }); + + const transport = new StdioServerTransport(); + + await server.connect(transport); +} + +main().catch(console.error); diff --git a/typescript/create-onchain-agent/templates/mcp/tsconfig.json b/typescript/create-onchain-agent/templates/mcp/tsconfig.json new file mode 100644 index 000000000..51c65a9a4 --- /dev/null +++ b/typescript/create-onchain-agent/templates/mcp/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./build", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/typescript/package-lock.json b/typescript/package-lock.json index ffefc61c7..7b26fe0c0 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -10,6 +10,7 @@ "agentkit", "create-onchain-agent", "create-onchain-agent/templates/next", + "create-onchain-agent/templates/mcp", "framework-extensions/langchain", "framework-extensions/vercel-ai-sdk", "framework-extensions/model-context-protocol", @@ -283,6 +284,56 @@ "base-x": "^5.0.0" } }, + "create-onchain-agent/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "create-onchain-agent/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "create-onchain-agent/templates/mcp": { + "version": "0.1.0", + "dependencies": { + "@coinbase/agentkit": "^0.3.0", + "@coinbase/agentkit-model-context-protocol": "^0.2.0", + "@modelcontextprotocol/sdk": "^1.6.1", + "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0", + "viem": "^2.23.7" + }, + "bin": { + "agentkit": "build/index.js" + }, + "devDependencies": { + "@types/node": "^22.13.9", + "tsx": "^4.19.3", + "typescript": "^5.8.2" + } + }, + "create-onchain-agent/templates/mcp/node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, "create-onchain-agent/templates/next": { "name": "next-template", "version": "0.1.0", @@ -12836,6 +12887,10 @@ "node": ">= 0.4" } }, + "node_modules/mcp": { + "resolved": "create-onchain-agent/templates/mcp", + "link": true + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -14592,9 +14647,9 @@ } }, "node_modules/ox": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", - "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", + "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", "funding": [ { "type": "github", @@ -18699,9 +18754,9 @@ } }, "node_modules/viem": { - "version": "2.23.5", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.5.tgz", - "integrity": "sha512-cUfBHdFQHmBlPW0loFXda0uZcoU+uJw3NRYQRwYgkrpH6PgovH8iuVqDn6t1jZk82zny4wQL54c9dCX2W9kLMg==", + "version": "2.23.10", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.10.tgz", + "integrity": "sha512-va6Wde+v96PdfzdPEspCML1MjAqe+88O8BD+R9Kun/4s5KMUNcqfHbXdZP0ZZ2Zms80styvH2pDRAqCho6TqkA==", "funding": [ { "type": "github", @@ -18716,8 +18771,8 @@ "@scure/bip39": "1.5.4", "abitype": "1.0.8", "isows": "1.0.6", - "ox": "0.6.7", - "ws": "8.18.0" + "ox": "0.6.9", + "ws": "8.18.1" }, "peerDependencies": { "typescript": ">=5.0.4" @@ -18728,27 +18783,6 @@ } } }, - "node_modules/viem/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/wagmi": { "version": "2.14.12", "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.14.12.tgz", @@ -19246,21 +19280,6 @@ "ts-node": "^10.9.2" } }, - "typescript/examples/langchain-cdp-chatbot/node_modules/@coinbase/agentkit-langchain/node_modules/@coinbase/agentkit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@coinbase/agentkit/-/agentkit-0.1.2.tgz", - "integrity": "sha512-vG7crTvtkG/dovng5vSuOgHam7KXeirTPi3yzPbVyV+Xi3toGxB51ZYOXPiqp31tKI9avm2TSNuv7nCnPQEAVg==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@coinbase/coinbase-sdk": "^0.17.0", - "md5": "^2.3.0", - "reflect-metadata": "^0.2.2", - "twitter-api-v2": "^1.18.2", - "viem": "^2.22.16", - "zod": "^3.23.8" - } - }, "typescript/examples/langchain-farcaster-chatbot": { "name": "@coinbase/farcaster-langchain-chatbot-example", "version": "1.0.0", @@ -19337,21 +19356,6 @@ "ts-node": "^10.9.2" } }, - "typescript/examples/langchain-twitter-chatbot/node_modules/@coinbase/agentkit-langchain/node_modules/@coinbase/agentkit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@coinbase/agentkit/-/agentkit-0.1.2.tgz", - "integrity": "sha512-vG7crTvtkG/dovng5vSuOgHam7KXeirTPi3yzPbVyV+Xi3toGxB51ZYOXPiqp31tKI9avm2TSNuv7nCnPQEAVg==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@coinbase/coinbase-sdk": "^0.17.0", - "md5": "^2.3.0", - "reflect-metadata": "^0.2.2", - "twitter-api-v2": "^1.18.2", - "viem": "^2.22.16", - "zod": "^3.23.8" - } - }, "typescript/framework-extensions/langchain": { "name": "@coinbase/agentkit-langchain", "version": "0.2.2", @@ -24468,6 +24472,18 @@ "requires": { "base-x": "^5.0.0" } + }, + "typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true + }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true } } }, @@ -28251,6 +28267,31 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, + "mcp": { + "version": "file:create-onchain-agent/templates/mcp", + "requires": { + "@coinbase/agentkit": "^0.3.0", + "@coinbase/agentkit-model-context-protocol": "^0.2.0", + "@modelcontextprotocol/sdk": "^1.6.1", + "@solana/web3.js": "^1.98.0", + "@types/node": "^22.13.9", + "bs58": "^6.0.0", + "tsx": "^4.19.3", + "typescript": "^5.8.2", + "viem": "^2.23.7" + }, + "dependencies": { + "@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "dev": true, + "requires": { + "undici-types": "~6.20.0" + } + } + } + }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -29371,9 +29412,9 @@ } }, "ox": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", - "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", + "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", "requires": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", @@ -32176,9 +32217,9 @@ } }, "viem": { - "version": "2.23.5", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.5.tgz", - "integrity": "sha512-cUfBHdFQHmBlPW0loFXda0uZcoU+uJw3NRYQRwYgkrpH6PgovH8iuVqDn6t1jZk82zny4wQL54c9dCX2W9kLMg==", + "version": "2.23.10", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.10.tgz", + "integrity": "sha512-va6Wde+v96PdfzdPEspCML1MjAqe+88O8BD+R9Kun/4s5KMUNcqfHbXdZP0ZZ2Zms80styvH2pDRAqCho6TqkA==", "requires": { "@noble/curves": "1.8.1", "@noble/hashes": "1.7.1", @@ -32186,16 +32227,8 @@ "@scure/bip39": "1.5.4", "abitype": "1.0.8", "isows": "1.0.6", - "ox": "0.6.7", - "ws": "8.18.0" - }, - "dependencies": { - "ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "requires": {} - } + "ox": "0.6.9", + "ws": "8.18.1" } }, "wagmi": { diff --git a/typescript/package.json b/typescript/package.json index 917658a1d..4b192e02f 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -5,6 +5,7 @@ "agentkit", "create-onchain-agent", "create-onchain-agent/templates/next", + "create-onchain-agent/templates/mcp", "framework-extensions/langchain", "framework-extensions/vercel-ai-sdk", "framework-extensions/model-context-protocol",