Skip to content

Commit 1e1d879

Browse files
committed
feat: replace generic EVM prompts with new task-oriented workflows for transfers, diagnostics, wallet analysis, and approvals, and add an ABI service.
1 parent 25ab4d9 commit 1e1d879

5 files changed

Lines changed: 779 additions & 917 deletions

File tree

src/core/prompts.ts

Lines changed: 215 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,174 +2,313 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { z } from "zod";
33

44
/**
5-
* Register all EVM-related prompts with the MCP server
5+
* Register task-oriented prompts with the MCP server
6+
*
7+
* Prompts function like macros - they guide the model through complex multi-step workflows.
8+
* Instead of the AI agent having to discover and call multiple tools in the right sequence,
9+
* these prompts provide a structured approach to common blockchain tasks.
10+
*
611
* @param server The MCP server instance
712
*/
813
export function registerEVMPrompts(server: McpServer) {
9-
// Basic block explorer prompt
14+
// ============================================================================
15+
// TRANSACTION PREPARATION PROMPTS
16+
// ============================================================================
17+
1018
server.registerPrompt(
11-
"explore_block",
19+
"prepare_transfer",
1220
{
13-
description: "Explore information about a specific block",
21+
description: "Guide through preparing a token transfer with safety checks",
1422
argsSchema: {
15-
blockNumber: z.string().optional().describe("Block number to explore. If not provided, latest block will be used."),
16-
network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.")
23+
tokenType: z.enum(["native", "erc20"]).describe("Type of token: 'native' for ETH/MATIC, 'erc20' for contract tokens"),
24+
recipient: z.string().describe("Recipient address or ENS name"),
25+
amount: z.string().describe("Amount to transfer"),
26+
network: z.string().optional().describe("Network name (e.g., 'ethereum', 'polygon'). Defaults to Ethereum mainnet."),
27+
tokenAddress: z.string().optional().describe("Token contract address (required for ERC20 transfers)")
1728
}
1829
},
19-
({ blockNumber, network = "ethereum" }) => ({
30+
({ tokenType, recipient, amount, network = "ethereum", tokenAddress }) => ({
2031
messages: [{
2132
role: "user",
2233
content: {
2334
type: "text",
24-
text: blockNumber
25-
? `Please analyze block #${blockNumber} on the ${network} network and provide information about its key metrics, transactions, and significance.`
26-
: `Please analyze the latest block on the ${network} network and provide information about its key metrics, transactions, and significance.`
35+
text: tokenType === "native"
36+
? `I want to transfer ${amount} native tokens (${network === "ethereum" ? "ETH" : "MATIC"}) to ${recipient} on ${network}.
37+
38+
Before proceeding:
39+
1. Call get_wallet_address to confirm which wallet will send the transaction
40+
2. Call get_balance to verify I have enough balance
41+
3. Call get_gas_price to understand current gas costs
42+
4. Summarize the transaction details and ask for confirmation before executing
43+
5. Call transfer_native once approved
44+
6. Wait for confirmation with wait_for_transaction
45+
46+
Be clear about gas costs and the impact of this transaction.`
47+
: `I want to transfer ${amount} tokens (contract: ${tokenAddress}) to ${recipient} on ${network}.
48+
49+
Before proceeding:
50+
1. Call get_wallet_address to confirm which wallet will send the transaction
51+
2. Call get_token_balance to verify I have enough balance
52+
3. Call get_gas_price to understand current gas costs
53+
4. Check if an approval is needed (call get_allowance for your DEX/protocol)
54+
5. Summarize the transaction details and ask for confirmation before executing
55+
6. If approval needed, call approve_token_spending first
56+
7. Call transfer_erc20 once approved
57+
8. Wait for confirmation with wait_for_transaction
58+
59+
Be clear about gas costs, token decimals, and the impact of this transaction.`
2760
}
2861
}]
2962
})
3063
);
3164

32-
// Transaction analysis prompt
3365
server.registerPrompt(
34-
"analyze_transaction",
66+
"diagnose_transaction",
3567
{
36-
description: "Analyze a specific transaction",
68+
description: "Analyze a failed or pending transaction and suggest solutions",
3769
argsSchema: {
38-
txHash: z.string().describe("Transaction hash to analyze"),
39-
network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.")
70+
txHash: z.string().describe("Transaction hash (0x...)"),
71+
network: z.string().optional().describe("Network name. Defaults to Ethereum mainnet.")
4072
}
4173
},
4274
({ txHash, network = "ethereum" }) => ({
4375
messages: [{
4476
role: "user",
4577
content: {
4678
type: "text",
47-
text: `Please analyze transaction ${txHash} on the ${network} network and provide a detailed explanation of what this transaction does, who the parties involved are, the amount transferred (if applicable), gas used, and any other relevant information.`
79+
text: `Please diagnose this transaction: ${txHash} on ${network}
80+
81+
Follow these steps:
82+
1. Call get_transaction to get the transaction details
83+
2. Call get_transaction_receipt to check the status and gas used
84+
3. Analyze what went wrong or what the transaction is doing
85+
4. If it failed:
86+
- Check if it was out of gas
87+
- Check if it was a contract revert
88+
- Look at the gas limit vs gas used
89+
5. Provide:
90+
- Current status (pending/confirmed/failed)
91+
- Why it failed (if applicable)
92+
- Gas analysis
93+
- Recommended next steps
94+
95+
Be specific about the issues and provide actionable solutions.`
4896
}
4997
}]
5098
})
5199
);
52100

53-
// Address analysis prompt
101+
// ============================================================================
102+
// WALLET ANALYSIS PROMPTS
103+
// ============================================================================
104+
54105
server.registerPrompt(
55-
"analyze_address",
106+
"analyze_wallet",
56107
{
57-
description: "Analyze an EVM address",
108+
description: "Get a comprehensive overview of a wallet's assets and activity",
58109
argsSchema: {
59-
address: z.string().describe("Ethereum address to analyze"),
60-
network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.")
110+
address: z.string().describe("Wallet address or ENS name to analyze"),
111+
network: z.string().optional().describe("Network name. Defaults to Ethereum mainnet."),
112+
tokens: z.string().optional().describe("Comma-separated list of ERC20 token addresses to check balance for")
61113
}
62114
},
63-
({ address, network = "ethereum" }) => ({
115+
({ address, network = "ethereum", tokens }) => {
116+
const tokenList = tokens ? tokens.split(',').map(t => t.trim()) : [];
117+
return {
64118
messages: [{
65119
role: "user",
66120
content: {
67121
type: "text",
68-
text: `Please analyze the address ${address} on the ${network} network. Provide information about its balance, transaction count, and any other relevant information you can find.`
122+
text: `Please analyze the wallet ${address} on ${network}:
123+
124+
1. Call resolve_ens_name if the input looks like an ENS name, otherwise use it as an address
125+
2. Call get_balance to get the native token balance
126+
3. If tokens are specified, call get_token_balance for each token to show holdings
127+
4. Provide a summary with:
128+
- Wallet address and ENS name (if any)
129+
- Native token balance (in both wei and ether)
130+
- Token holdings (if checked)
131+
- Overall asset overview
132+
133+
Format the results in a clear, readable way. If the wallet has no balance, let the user know.`
134+
}
135+
}]
136+
};
137+
}
138+
);
139+
140+
server.registerPrompt(
141+
"audit_approvals",
142+
{
143+
description: "Review token approvals for a wallet and identify security risks",
144+
argsSchema: {
145+
address: z.string().optional().describe("Wallet address to audit (defaults to configured wallet)"),
146+
tokenAddress: z.string().describe("ERC20 token contract address to check approvals for"),
147+
network: z.string().optional().describe("Network name. Defaults to Ethereum mainnet.")
148+
}
149+
},
150+
({ address, tokenAddress, network = "ethereum" }) => ({
151+
messages: [{
152+
role: "user",
153+
content: {
154+
type: "text",
155+
text: `Please audit token approvals for ${tokenAddress} on ${network}${address ? ` for address ${address}` : " for the configured wallet"}:
156+
157+
1. If no address is provided, call get_wallet_address to get the configured wallet
158+
2. Call get_allowance to check if there are any existing approvals
159+
3. Analyze the approval amount and what it means:
160+
- If allowance is 0: No approval set
161+
- If allowance is a normal amount: Limited approval (safe)
162+
- If allowance is max uint256: Unlimited approval (security risk)
163+
4. Provide recommendations:
164+
- Is the current approval appropriate?
165+
- Should any dangerous approvals be revoked?
166+
- What approvals should be set up before interacting with protocols?
167+
168+
Be clear about the security implications of unlimited approvals.`
69169
}
70170
}]
71171
})
72172
);
73173

74-
// Smart contract interaction guidance
174+
// ============================================================================
175+
// SMART CONTRACT EXPLORATION PROMPTS
176+
// ============================================================================
177+
75178
server.registerPrompt(
76-
"interact_with_contract",
179+
"explore_contract",
77180
{
78-
description: "Get guidance on interacting with a smart contract",
181+
description: "Analyze a smart contract to understand its functions and state",
79182
argsSchema: {
80-
contractAddress: z.string().describe("The contract address"),
81-
abiJson: z.string().optional().describe("The contract ABI as a JSON string"),
82-
network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.")
183+
contractAddress: z.string().describe("The contract address to explore"),
184+
network: z.string().optional().describe("Network name. Defaults to Ethereum mainnet.")
83185
}
84186
},
85-
({ contractAddress, abiJson, network = "ethereum" }) => ({
187+
({ contractAddress, network = "ethereum" }) => ({
86188
messages: [{
87189
role: "user",
88190
content: {
89191
type: "text",
90-
text: abiJson
91-
? `I need to interact with the smart contract at address ${contractAddress} on the ${network} network. Here's the ABI:\n\n${abiJson}\n\nPlease analyze this contract's functions and provide guidance on how to interact with it safely.`
92-
: `I need to interact with the smart contract at address ${contractAddress} on the ${network} network. Please help me understand what this contract does and how I can interact with it safely.`
192+
text: `Please explore the smart contract at ${contractAddress} on ${network}:
193+
194+
1. This is a read-only exploration - no transactions will be executed
195+
2. Identify what type of contract this is (token, NFT, DEX, lending, etc.)
196+
3. Try to read common functions that might exist:
197+
- For tokens: name(), symbol(), decimals(), totalSupply()
198+
- For NFTs: name(), symbol(), totalSupply()
199+
- For other contracts: relevant state functions
200+
4. Use read_contract to call these functions
201+
5. Provide a summary with:
202+
- Contract type and purpose (best guess)
203+
- Key properties discovered
204+
- Security notes (if applicable)
205+
- Next steps for interacting with it
206+
207+
Be clear about what you were able to discover and what remains unknown.`
93208
}
94209
}]
95210
})
96211
);
97212

98-
// EVM concept explanation
99213
server.registerPrompt(
100214
"explain_evm_concept",
101215
{
102-
description: "Get an explanation of an EVM concept",
216+
description: "Get an explanation of an EVM or blockchain concept",
103217
argsSchema: {
104-
concept: z.string().describe("The EVM concept to explain (e.g., gas, nonce, etc.)")
218+
concept: z.string().describe("The concept to explain (e.g., 'gas', 'nonce', 'smart contracts', 'MEV')")
105219
}
106220
},
107221
({ concept }) => ({
108222
messages: [{
109223
role: "user",
110224
content: {
111225
type: "text",
112-
text: `Please explain the EVM Blockchain concept of "${concept}" in detail. Include how it works, why it's important, and provide examples if applicable.`
226+
text: `Please explain the blockchain/EVM concept: "${concept}"
227+
228+
In your explanation:
229+
1. Define what it is in simple terms
230+
2. Explain why it matters
231+
3. Give practical examples
232+
4. Explain how it affects using blockchain applications
233+
5. If relevant, mention how to check or monitor it on the blockchain
234+
235+
Make it accessible for someone new to blockchain but interested in using EVM networks.`
113236
}
114237
}]
115238
})
116239
);
117240

118-
// Network comparison
241+
// ============================================================================
242+
// NETWORK INFORMATION PROMPTS
243+
// ============================================================================
244+
119245
server.registerPrompt(
120246
"compare_networks",
121247
{
122-
description: "Compare different EVM-compatible networks",
248+
description: "Compare different EVM networks to understand their differences",
123249
argsSchema: {
124-
networkList: z.string().describe("Comma-separated list of networks to compare")
250+
networks: z.string().describe("Comma-separated list of network names to compare (e.g., 'ethereum,polygon,arbitrum')")
125251
}
126252
},
127-
({ networkList }) => {
128-
const networks = networkList.split(',').map(n => n.trim());
253+
({ networks }) => {
254+
const networkList = networks.split(',').map(n => n.trim());
129255
return {
130-
messages: [{
131-
role: "user",
132-
content: {
133-
type: "text",
134-
text: `Please compare the following EVM-compatible networks: ${networks.join(', ')}. Include information about their architecture, gas fees, transaction speed, security, and any other relevant differences.`
135-
}
136-
}]
256+
messages: [{
257+
role: "user",
258+
content: {
259+
type: "text",
260+
text: `Please compare these EVM networks: ${networkList.join(", ")}
261+
262+
For each network:
263+
1. Call get_chain_info to get current chain ID and block number
264+
2. Call get_gas_price to understand current gas costs
265+
3. Provide comparison across:
266+
- Chain characteristics (mainnet, testnet, etc.)
267+
- Current gas prices
268+
- Block time and finality
269+
- Typical use cases and advantages
270+
- Any known limitations or risks
271+
4. Provide a recommendation for which network to use based on:
272+
- Transaction speed needs
273+
- Cost considerations
274+
- Ecosystem size and liquidity
275+
- Security considerations
276+
277+
Make the comparison easy to understand for someone deciding which network to use.`
278+
}
279+
}]
137280
};
138281
}
139282
);
140283

141-
// Token analysis prompt
142284
server.registerPrompt(
143-
"analyze_token",
285+
"check_network_status",
144286
{
145-
description: "Analyze an ERC20 or NFT token",
287+
description: "Check the current status and health of an EVM network",
146288
argsSchema: {
147-
tokenAddress: z.string().describe("Token contract address to analyze"),
148-
tokenType: z.string().optional().describe("Type of token (erc20, erc721/nft, or auto-detect)"),
149-
tokenId: z.string().optional().describe("Token ID (required for NFT analysis)"),
150-
network: z.string().optional().describe("Network name or chain ID. Defaults to Ethereum mainnet.")
289+
network: z.string().optional().describe("Network name. Defaults to Ethereum mainnet.")
151290
}
152291
},
153-
({ tokenAddress, tokenType = "auto", tokenId, network = "ethereum" }) => {
154-
let promptText = "";
155-
156-
if (tokenType === "erc20" || tokenType === "auto") {
157-
promptText = `Please analyze the ERC20 token at address ${tokenAddress} on the ${network} network. Provide information about its name, symbol, total supply, and any other relevant details.`;
158-
} else if ((tokenType === "erc721" || tokenType === "nft") && tokenId) {
159-
promptText = `Please analyze the NFT with token ID ${tokenId} from the collection at address ${tokenAddress} on the ${network} network. Provide information about the collection name, token details, and any other relevant information.`;
160-
} else if (tokenType === "nft" || tokenType === "erc721") {
161-
promptText = `Please analyze the NFT collection at address ${tokenAddress} on the ${network} network. Provide information about the collection name, symbol, total supply if available, and any other relevant details.`;
162-
}
292+
({ network = "ethereum" }) => ({
293+
messages: [{
294+
role: "user",
295+
content: {
296+
type: "text",
297+
text: `Please check the current status of the ${network} network:
163298
164-
return {
165-
messages: [{
166-
role: "user",
167-
content: {
168-
type: "text",
169-
text: promptText
170-
}
171-
}]
172-
};
173-
}
299+
1. Call get_chain_info to get chain ID, current block number, and RPC info
300+
2. Call get_latest_block to see recent block information
301+
3. Call get_gas_price to check current gas prices
302+
4. Provide a status report with:
303+
- Network is healthy/operational
304+
- Current block number and recent block times
305+
- Current gas prices (base fee and priority fee)
306+
- Any observations about network congestion
307+
- Recommendations for transaction timing if applicable
308+
309+
Be clear about whether now is a good time to transact on this network.`
310+
}
311+
}]
312+
})
174313
);
175314
}

0 commit comments

Comments
 (0)