web3-tools-mcp 2.0.1 → 3.0.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.
Files changed (167) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +5 -0
  3. package/dist/anvil.d.ts +17 -19
  4. package/dist/anvil.d.ts.map +1 -1
  5. package/dist/anvil.js +124 -228
  6. package/dist/anvil.js.map +1 -1
  7. package/dist/chains.d.ts +9 -1
  8. package/dist/chains.d.ts.map +1 -1
  9. package/dist/chains.js +11 -2
  10. package/dist/chains.js.map +1 -1
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js.map +1 -1
  13. package/dist/context.d.ts +22 -0
  14. package/dist/context.d.ts.map +1 -0
  15. package/dist/context.js +24 -0
  16. package/dist/context.js.map +1 -0
  17. package/dist/hosted.d.ts +12 -0
  18. package/dist/hosted.d.ts.map +1 -0
  19. package/dist/hosted.js +12 -0
  20. package/dist/hosted.js.map +1 -0
  21. package/dist/http-server.d.ts +13 -0
  22. package/dist/http-server.d.ts.map +1 -1
  23. package/dist/http-server.js +13 -4
  24. package/dist/http-server.js.map +1 -1
  25. package/dist/index.js +19 -15
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib.d.ts +24 -0
  28. package/dist/lib.d.ts.map +1 -0
  29. package/dist/lib.js +23 -0
  30. package/dist/lib.js.map +1 -0
  31. package/dist/log.d.ts +16 -0
  32. package/dist/log.d.ts.map +1 -0
  33. package/dist/log.js +20 -0
  34. package/dist/log.js.map +1 -0
  35. package/dist/mcp-server.d.ts +4 -0
  36. package/dist/mcp-server.d.ts.map +1 -0
  37. package/dist/mcp-server.js +43 -0
  38. package/dist/mcp-server.js.map +1 -0
  39. package/dist/oauth.d.ts +41 -6
  40. package/dist/oauth.d.ts.map +1 -1
  41. package/dist/oauth.js +75 -41
  42. package/dist/oauth.js.map +1 -1
  43. package/dist/preview.d.ts +7 -0
  44. package/dist/preview.d.ts.map +1 -1
  45. package/dist/preview.js +14 -2
  46. package/dist/preview.js.map +1 -1
  47. package/dist/signing-requests.d.ts +81 -0
  48. package/dist/signing-requests.d.ts.map +1 -0
  49. package/dist/signing-requests.js +138 -0
  50. package/dist/signing-requests.js.map +1 -0
  51. package/dist/tools/index.d.ts +27 -1
  52. package/dist/tools/index.d.ts.map +1 -1
  53. package/dist/tools/index.js +37 -15
  54. package/dist/tools/index.js.map +1 -1
  55. package/dist/tools/{advanced.d.ts → read/advanced.d.ts} +30 -26
  56. package/dist/tools/read/advanced.d.ts.map +1 -0
  57. package/dist/tools/{advanced.js → read/advanced.js} +10 -10
  58. package/dist/tools/read/advanced.js.map +1 -0
  59. package/dist/tools/{balance.d.ts → read/balance.d.ts} +8 -7
  60. package/dist/tools/read/balance.d.ts.map +1 -0
  61. package/dist/tools/{balance.js → read/balance.js} +2 -2
  62. package/dist/tools/read/balance.js.map +1 -0
  63. package/dist/tools/{contract-info.d.ts → read/contract-info.d.ts} +18 -15
  64. package/dist/tools/read/contract-info.d.ts.map +1 -0
  65. package/dist/tools/{contract-info.js → read/contract-info.js} +2 -2
  66. package/dist/tools/read/contract-info.js.map +1 -0
  67. package/dist/tools/{contract.d.ts → read/contract.d.ts} +14 -12
  68. package/dist/tools/read/contract.d.ts.map +1 -0
  69. package/dist/tools/{contract.js → read/contract.js} +2 -2
  70. package/dist/tools/read/contract.js.map +1 -0
  71. package/dist/tools/{ens.d.ts → read/ens.d.ts} +30 -25
  72. package/dist/tools/read/ens.d.ts.map +1 -0
  73. package/dist/tools/{ens.js → read/ens.js} +2 -2
  74. package/dist/tools/read/ens.js.map +1 -0
  75. package/dist/tools/read/gas.d.ts +171 -0
  76. package/dist/tools/read/gas.d.ts.map +1 -0
  77. package/dist/tools/read/gas.js +368 -0
  78. package/dist/tools/read/gas.js.map +1 -0
  79. package/dist/tools/{logs.d.ts → read/logs.d.ts} +6 -5
  80. package/dist/tools/read/logs.d.ts.map +1 -0
  81. package/dist/tools/{logs.js → read/logs.js} +2 -2
  82. package/dist/tools/read/logs.js.map +1 -0
  83. package/dist/tools/{signatures.d.ts → read/signatures.d.ts} +8 -4
  84. package/dist/tools/read/signatures.d.ts.map +1 -0
  85. package/dist/tools/{signatures.js → read/signatures.js} +1 -1
  86. package/dist/tools/read/signatures.js.map +1 -0
  87. package/dist/tools/read/signing.d.ts +27 -0
  88. package/dist/tools/read/signing.d.ts.map +1 -0
  89. package/dist/tools/read/signing.js +75 -0
  90. package/dist/tools/read/signing.js.map +1 -0
  91. package/dist/tools/sign/errors.d.ts +9 -0
  92. package/dist/tools/sign/errors.d.ts.map +1 -0
  93. package/dist/tools/sign/errors.js +29 -0
  94. package/dist/tools/sign/errors.js.map +1 -0
  95. package/dist/tools/{transactions.d.ts → sign/transactions.d.ts} +48 -62
  96. package/dist/tools/sign/transactions.d.ts.map +1 -0
  97. package/dist/tools/sign/transactions.js +316 -0
  98. package/dist/tools/sign/transactions.js.map +1 -0
  99. package/dist/tools/sign/wallet.d.ts +50 -0
  100. package/dist/tools/sign/wallet.d.ts.map +1 -0
  101. package/dist/tools/sign/wallet.js +180 -0
  102. package/dist/tools/sign/wallet.js.map +1 -0
  103. package/dist/utils.d.ts +5 -2
  104. package/dist/utils.d.ts.map +1 -1
  105. package/dist/utils.js +5 -2
  106. package/dist/utils.js.map +1 -1
  107. package/dist/wallet-client.d.ts +33 -4
  108. package/dist/wallet-client.d.ts.map +1 -1
  109. package/dist/wallet-client.js +90 -24
  110. package/dist/wallet-client.js.map +1 -1
  111. package/dist/walletconnect.d.ts +111 -13
  112. package/dist/walletconnect.d.ts.map +1 -1
  113. package/dist/walletconnect.js +248 -68
  114. package/dist/walletconnect.js.map +1 -1
  115. package/package.json +10 -4
  116. package/src/anvil.ts +150 -271
  117. package/src/chains.ts +14 -2
  118. package/src/client.ts +0 -1
  119. package/src/context.ts +27 -0
  120. package/src/hosted.ts +11 -0
  121. package/src/http-server.ts +30 -5
  122. package/src/index.ts +19 -16
  123. package/src/lib.ts +24 -0
  124. package/src/log.ts +25 -0
  125. package/src/mcp-server.ts +47 -0
  126. package/src/oauth.ts +98 -45
  127. package/src/preview.ts +21 -2
  128. package/src/signing-requests.ts +198 -0
  129. package/src/tools/index.ts +67 -16
  130. package/src/tools/{advanced.ts → read/advanced.ts} +13 -11
  131. package/src/tools/{balance.ts → read/balance.ts} +3 -3
  132. package/src/tools/{contract-info.ts → read/contract-info.ts} +3 -3
  133. package/src/tools/{contract.ts → read/contract.ts} +3 -3
  134. package/src/tools/{ens.ts → read/ens.ts} +3 -3
  135. package/src/tools/read/gas.ts +480 -0
  136. package/src/tools/{logs.ts → read/logs.ts} +3 -3
  137. package/src/tools/{signatures.ts → read/signatures.ts} +2 -2
  138. package/src/tools/read/signing.ts +86 -0
  139. package/src/tools/sign/errors.ts +28 -0
  140. package/src/tools/sign/transactions.ts +428 -0
  141. package/src/tools/sign/wallet.ts +216 -0
  142. package/src/utils.ts +5 -2
  143. package/src/wallet-client.ts +91 -22
  144. package/src/walletconnect.ts +286 -66
  145. package/dist/tools/advanced.d.ts.map +0 -1
  146. package/dist/tools/advanced.js.map +0 -1
  147. package/dist/tools/balance.d.ts.map +0 -1
  148. package/dist/tools/balance.js.map +0 -1
  149. package/dist/tools/contract-info.d.ts.map +0 -1
  150. package/dist/tools/contract-info.js.map +0 -1
  151. package/dist/tools/contract.d.ts.map +0 -1
  152. package/dist/tools/contract.js.map +0 -1
  153. package/dist/tools/ens.d.ts.map +0 -1
  154. package/dist/tools/ens.js.map +0 -1
  155. package/dist/tools/gas.d.ts +0 -99
  156. package/dist/tools/gas.d.ts.map +0 -1
  157. package/dist/tools/gas.js +0 -181
  158. package/dist/tools/gas.js.map +0 -1
  159. package/dist/tools/logs.d.ts.map +0 -1
  160. package/dist/tools/logs.js.map +0 -1
  161. package/dist/tools/signatures.d.ts.map +0 -1
  162. package/dist/tools/signatures.js.map +0 -1
  163. package/dist/tools/transactions.d.ts.map +0 -1
  164. package/dist/tools/transactions.js +0 -361
  165. package/dist/tools/transactions.js.map +0 -1
  166. package/src/tools/gas.ts +0 -219
  167. package/src/tools/transactions.ts +0 -452
@@ -1,17 +1,31 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
2
  import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'
3
3
  import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js'
4
- import advancedTools from './advanced.js'
5
- import balanceTools from './balance.js'
6
- import contractTools from './contract.js'
7
- import contractInfoTools from './contract-info.js'
8
- import ensTools from './ens.js'
9
- import gasTools from './gas.js'
10
- import logTools from './logs.js'
11
- import signatureTools from './signatures.js'
12
- import transactionTools from './transactions.js'
13
-
14
- const allToolDefinitions = {
4
+ import { identityFrom } from '../context.js'
5
+ import type { ToolResult } from '../types.js'
6
+ import advancedTools from './read/advanced.js'
7
+ import balanceTools from './read/balance.js'
8
+ import contractTools from './read/contract.js'
9
+ import contractInfoTools from './read/contract-info.js'
10
+ import ensTools from './read/ens.js'
11
+ import gasTools from './read/gas.js'
12
+ import logTools from './read/logs.js'
13
+ import signatureTools from './read/signatures.js'
14
+ import signingRequestTools from './read/signing.js'
15
+ import transactionTools from './sign/transactions.js'
16
+ import walletTools from './sign/wallet.js'
17
+
18
+ /**
19
+ * What a tool does with a wallet, which is the line everything else divides along.
20
+ *
21
+ * `read` answers questions about a chain and costs an RPC call. `sign` puts a transaction
22
+ * in front of somebody's wallet, and is the reason identity and rooms exist. A host pricing
23
+ * or rate-limiting tool calls almost always wants this distinction, and taking it from the
24
+ * folder a tool lives in means it cannot drift from a hand-kept list.
25
+ */
26
+ export type ToolGroup = 'read' | 'sign'
27
+
28
+ const readTools = {
15
29
  ...signatureTools,
16
30
  ...contractTools,
17
31
  ...contractInfoTools,
@@ -20,12 +34,45 @@ const allToolDefinitions = {
20
34
  ...advancedTools,
21
35
  ...ensTools,
22
36
  ...gasTools,
23
- ...transactionTools
37
+ // A signature already asked for and paid for; collecting it is not a second request.
38
+ ...signingRequestTools
24
39
  } as const
25
40
 
26
- // Register all tools with the MCP server
27
- export function registerAllTools(server: McpServer) {
41
+ const signTools = {
42
+ ...transactionTools,
43
+ ...walletTools
44
+ } as const
45
+
46
+ const allToolDefinitions = { ...readTools, ...signTools } as const
47
+
48
+ const groups = new Map<string, ToolGroup>([
49
+ ...Object.keys(readTools).map((name) => [name, 'read'] as const),
50
+ ...Object.keys(signTools).map((name) => [name, 'sign'] as const)
51
+ ])
52
+
53
+ /** The group a tool belongs to, for a host deciding what it costs or who may call it. */
54
+ export function toolGroup(name: string): ToolGroup | undefined {
55
+ return groups.get(name)
56
+ }
57
+
58
+ /**
59
+ * Wraps every tool call, for a host that needs to say who may run what.
60
+ *
61
+ * Deliberately only the tool's name, its group and who is calling: enough to count calls,
62
+ * refuse them past a limit and record what was used, without this package taking a view on
63
+ * any of it. Throwing refuses the call, and the message reaches the agent — so it should
64
+ * say what to do about it. Whatever the returned promise settles as is what the caller
65
+ * sees, so a host can also time a call or meter it once it has succeeded.
66
+ */
67
+ export type ToolMiddleware = (
68
+ call: { tool: string; group: ToolGroup; identity: string },
69
+ run: () => Promise<ToolResult>
70
+ ) => Promise<ToolResult>
71
+
72
+ export function registerAllTools(server: McpServer, middleware?: ToolMiddleware) {
28
73
  Object.entries(allToolDefinitions).forEach(([name, tool]) => {
74
+ if (tool.available && !tool.available()) return
75
+
29
76
  server.registerTool(
30
77
  name,
31
78
  {
@@ -33,8 +80,12 @@ export function registerAllTools(server: McpServer) {
33
80
  description: tool.description,
34
81
  inputSchema: tool.schema.shape
35
82
  },
36
- async (args: any, _extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
37
- return await tool.handler(args)
83
+ async (args: any, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
84
+ // Passed to every handler; the ones that reach a signer must say whose it is.
85
+ const identity = identityFrom(extra.authInfo)
86
+ const run = () => tool.handler(args, identity)
87
+ const group = toolGroup(name) as ToolGroup
88
+ return await (middleware ? middleware({ tool: name, group, identity }, run) : run())
38
89
  }
39
90
  )
40
91
  })
@@ -1,9 +1,9 @@
1
1
  import { type Address, decodeAbiParameters, isAddress, parseAbiParameters } from 'viem'
2
2
  import { z } from 'zod'
3
- import { isAnvilInstalled, simulateCallWithTrace, traceTransactionWithAnvil } from '../anvil.js'
4
- import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
5
- import type { ChainName } from '../types.js'
6
- import { createTool, formatResponse, summarizeTrace } from '../utils.js'
3
+ import { isDebugNodeAvailable, simulateCallWithTrace, traceTransactionWithAnvil } from '../../anvil.js'
4
+ import { getClientManager, SUPPORTED_CHAINS } from '../../client.js'
5
+ import type { ChainName } from '../../types.js'
6
+ import { createTool, formatResponse, summarizeTrace } from '../../utils.js'
7
7
 
8
8
  export default {
9
9
  get_storage_at: createTool(
@@ -155,7 +155,9 @@ export default {
155
155
  useAnvil: z
156
156
  .boolean()
157
157
  .optional()
158
- .describe('Force using Anvil for tracing (requires Foundry installed). Auto-used as fallback when RPC tracing fails.')
158
+ .describe(
159
+ 'Force tracing through the debug node (ANVIL_RPC_URL). Used automatically as a fallback when the RPC cannot trace.'
160
+ )
159
161
  .default(false),
160
162
  summarize: z
161
163
  .boolean()
@@ -213,14 +215,14 @@ export default {
213
215
  (traceResult && typeof traceResult === 'object' && 'rpcError' in traceResult) ||
214
216
  args.useAnvil
215
217
  ) {
216
- const anvilAvailable = await isAnvilInstalled()
218
+ const anvilAvailable = await isDebugNodeAvailable()
217
219
  if (anvilAvailable) {
218
220
  usedAnvil = true // Mark as used before attempting (even if it fails)
219
221
  try {
220
222
  const forkUrl = clientManager.getRpcUrl(args.chain as ChainName)
221
223
  const blockNumber = transaction.blockNumber ?? 0n
222
224
 
223
- traceResult = await traceTransactionWithAnvil(forkUrl, args.transactionHash, blockNumber, tracer, args.chain)
225
+ traceResult = await traceTransactionWithAnvil(forkUrl, args.transactionHash, blockNumber, tracer)
224
226
  } catch (anvilError) {
225
227
  // Anvil tracing also failed
226
228
  traceResult = {
@@ -235,7 +237,7 @@ export default {
235
237
  // Anvil not available and RPC failed
236
238
  traceResult = {
237
239
  error:
238
- 'Tracing not available. Install Foundry (anvil) for local tracing: https://book.getfoundry.sh/getting-started/installation',
240
+ 'Tracing not available. Point ANVIL_RPC_URL at a node exposing debug_traceCall — a local Anvil on 127.0.0.1:8545 is picked up automatically.',
239
241
  rpcError:
240
242
  traceResult && typeof traceResult === 'object' && 'rpcError' in traceResult
241
243
  ? (traceResult as { rpcError: string }).rpcError
@@ -299,7 +301,7 @@ export default {
299
301
 
300
302
  debug_call: createTool(
301
303
  'Debug Contract Call',
302
- 'Simulate a contract call with full trace output. Requires Foundry (anvil) installed. Useful for debugging reverts and understanding call execution.',
304
+ 'Simulate a contract call with full trace output. Needs a node exposing debug_traceCall (a local Anvil, or ANVIL_RPC_URL). Useful for debugging reverts and understanding call execution.',
303
305
  z.object({
304
306
  chain: z.enum(SUPPORTED_CHAINS).describe('The blockchain network to fork'),
305
307
  to: z.string().describe('Contract address to call'),
@@ -328,9 +330,9 @@ export default {
328
330
  }),
329
331
  async (args) => {
330
332
  // Check if Anvil is installed
331
- const anvilAvailable = await isAnvilInstalled()
333
+ const anvilAvailable = await isDebugNodeAvailable()
332
334
  if (!anvilAvailable) {
333
- throw new Error('Anvil is not installed. Please install Foundry: https://book.getfoundry.sh/getting-started/installation')
335
+ throw new Error('No tracing node reachable. Run a local Anvil, or set ANVIL_RPC_URL to a node exposing debug_traceCall.')
334
336
  }
335
337
 
336
338
  const clientManager = getClientManager()
@@ -1,8 +1,8 @@
1
1
  import { type Address, formatEther, formatUnits, isAddress } from 'viem'
2
2
  import { z } from 'zod'
3
- import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
4
- import type { ChainName } from '../types.js'
5
- import { createTool, formatResponse } from '../utils.js'
3
+ import { getClientManager, SUPPORTED_CHAINS } from '../../client.js'
4
+ import type { ChainName } from '../../types.js'
5
+ import { createTool, formatResponse } from '../../utils.js'
6
6
 
7
7
  const BalanceQuerySchema = z.object({
8
8
  chain: z.enum(SUPPORTED_CHAINS).describe('The blockchain network to use'),
@@ -1,8 +1,8 @@
1
1
  import { isAddress } from 'viem'
2
2
  import { z } from 'zod'
3
- import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
4
- import type { ChainName } from '../types.js'
5
- import { createTool, formatResponse } from '../utils.js'
3
+ import { getClientManager, SUPPORTED_CHAINS } from '../../client.js'
4
+ import type { ChainName } from '../../types.js'
5
+ import { createTool, formatResponse } from '../../utils.js'
6
6
 
7
7
  // Type for raw Etherscan contract info
8
8
  interface EtherscanContractInfo {
@@ -1,8 +1,8 @@
1
1
  import { type AbiFunction, type Address, isAddress, parseAbiItem } from 'viem'
2
2
  import { z } from 'zod'
3
- import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
4
- import type { ChainName } from '../types.js'
5
- import { convertArgumentsToTypes, createTool, formatResponse } from '../utils.js'
3
+ import { getClientManager, SUPPORTED_CHAINS } from '../../client.js'
4
+ import type { ChainName } from '../../types.js'
5
+ import { convertArgumentsToTypes, createTool, formatResponse } from '../../utils.js'
6
6
 
7
7
  const ContractCallSchema = z.object({
8
8
  chain: z.enum(SUPPORTED_CHAINS).describe('Blockchain network (mainnet, base, arbitrum, polygon, optimism, celo, localhost)'),
@@ -1,9 +1,9 @@
1
1
  import { type Address, isAddress, namehash } from 'viem'
2
2
  import { normalize } from 'viem/ens'
3
3
  import { z } from 'zod'
4
- import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
5
- import type { ChainName } from '../types.js'
6
- import { createTool, formatResponse } from '../utils.js'
4
+ import { getClientManager, SUPPORTED_CHAINS } from '../../client.js'
5
+ import type { ChainName } from '../../types.js'
6
+ import { createTool, formatResponse } from '../../utils.js'
7
7
 
8
8
  // Base ENS Contract Addresses
9
9
  const BASE_ENS_CONTRACTS = {