web3-tools-mcp 2.0.2 → 3.1.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 (180) hide show
  1. package/CHANGELOG.md +182 -1
  2. package/README.md +76 -47
  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/chain-meta.d.ts.map +1 -1
  8. package/dist/chain-meta.js +19 -8
  9. package/dist/chain-meta.js.map +1 -1
  10. package/dist/chains.d.ts +9 -1
  11. package/dist/chains.d.ts.map +1 -1
  12. package/dist/chains.js +11 -2
  13. package/dist/chains.js.map +1 -1
  14. package/dist/clear-signing.d.ts.map +1 -1
  15. package/dist/clear-signing.js +17 -6
  16. package/dist/clear-signing.js.map +1 -1
  17. package/dist/client.d.ts.map +1 -1
  18. package/dist/client.js.map +1 -1
  19. package/dist/context.d.ts +22 -0
  20. package/dist/context.d.ts.map +1 -0
  21. package/dist/context.js +24 -0
  22. package/dist/context.js.map +1 -0
  23. package/dist/hosted.d.ts +12 -0
  24. package/dist/hosted.d.ts.map +1 -0
  25. package/dist/hosted.js +12 -0
  26. package/dist/hosted.js.map +1 -0
  27. package/dist/http-server.d.ts +13 -0
  28. package/dist/http-server.d.ts.map +1 -1
  29. package/dist/http-server.js +22 -7
  30. package/dist/http-server.js.map +1 -1
  31. package/dist/index.js +20 -12
  32. package/dist/index.js.map +1 -1
  33. package/dist/kv-storage.d.ts +2 -1
  34. package/dist/kv-storage.d.ts.map +1 -1
  35. package/dist/kv-storage.js +5 -2
  36. package/dist/kv-storage.js.map +1 -1
  37. package/dist/lib.d.ts +24 -0
  38. package/dist/lib.d.ts.map +1 -0
  39. package/dist/lib.js +23 -0
  40. package/dist/lib.js.map +1 -0
  41. package/dist/mcp-server.d.ts +4 -0
  42. package/dist/mcp-server.d.ts.map +1 -0
  43. package/dist/mcp-server.js +43 -0
  44. package/dist/mcp-server.js.map +1 -0
  45. package/dist/oauth.d.ts +57 -8
  46. package/dist/oauth.d.ts.map +1 -1
  47. package/dist/oauth.js +133 -63
  48. package/dist/oauth.js.map +1 -1
  49. package/dist/preview.d.ts +7 -0
  50. package/dist/preview.d.ts.map +1 -1
  51. package/dist/preview.js +21 -7
  52. package/dist/preview.js.map +1 -1
  53. package/dist/signing-requests.d.ts +81 -0
  54. package/dist/signing-requests.d.ts.map +1 -0
  55. package/dist/signing-requests.js +138 -0
  56. package/dist/signing-requests.js.map +1 -0
  57. package/dist/tools/index.d.ts +27 -1
  58. package/dist/tools/index.d.ts.map +1 -1
  59. package/dist/tools/index.js +50 -15
  60. package/dist/tools/index.js.map +1 -1
  61. package/dist/tools/{advanced.d.ts → read/advanced.d.ts} +39 -30
  62. package/dist/tools/read/advanced.d.ts.map +1 -0
  63. package/dist/tools/{advanced.js → read/advanced.js} +53 -41
  64. package/dist/tools/read/advanced.js.map +1 -0
  65. package/dist/tools/{balance.d.ts → read/balance.d.ts} +8 -7
  66. package/dist/tools/read/balance.d.ts.map +1 -0
  67. package/dist/tools/{balance.js → read/balance.js} +18 -33
  68. package/dist/tools/read/balance.js.map +1 -0
  69. package/dist/tools/{contract-info.d.ts → read/contract-info.d.ts} +20 -15
  70. package/dist/tools/read/contract-info.d.ts.map +1 -0
  71. package/dist/tools/{contract-info.js → read/contract-info.js} +54 -123
  72. package/dist/tools/read/contract-info.js.map +1 -0
  73. package/dist/tools/{contract.d.ts → read/contract.d.ts} +14 -12
  74. package/dist/tools/read/contract.d.ts.map +1 -0
  75. package/dist/tools/{contract.js → read/contract.js} +3 -2
  76. package/dist/tools/read/contract.js.map +1 -0
  77. package/dist/tools/read/ens.d.ts +104 -0
  78. package/dist/tools/read/ens.d.ts.map +1 -0
  79. package/dist/tools/{ens.js → read/ens.js} +25 -33
  80. package/dist/tools/read/ens.js.map +1 -0
  81. package/dist/tools/read/gas.d.ts +171 -0
  82. package/dist/tools/read/gas.d.ts.map +1 -0
  83. package/dist/tools/read/gas.js +333 -0
  84. package/dist/tools/read/gas.js.map +1 -0
  85. package/dist/tools/read/logs.d.ts +101 -0
  86. package/dist/tools/read/logs.d.ts.map +1 -0
  87. package/dist/tools/read/logs.js +211 -0
  88. package/dist/tools/read/logs.js.map +1 -0
  89. package/dist/tools/{signatures.d.ts → read/signatures.d.ts} +8 -4
  90. package/dist/tools/read/signatures.d.ts.map +1 -0
  91. package/dist/tools/{signatures.js → read/signatures.js} +4 -3
  92. package/dist/tools/read/signatures.js.map +1 -0
  93. package/dist/tools/read/signing.d.ts +27 -0
  94. package/dist/tools/read/signing.d.ts.map +1 -0
  95. package/dist/tools/read/signing.js +81 -0
  96. package/dist/tools/read/signing.js.map +1 -0
  97. package/dist/tools/sign/errors.d.ts +9 -0
  98. package/dist/tools/sign/errors.d.ts.map +1 -0
  99. package/dist/tools/sign/errors.js +29 -0
  100. package/dist/tools/sign/errors.js.map +1 -0
  101. package/dist/tools/sign/transactions.d.ts +141 -0
  102. package/dist/tools/sign/transactions.d.ts.map +1 -0
  103. package/dist/tools/sign/transactions.js +361 -0
  104. package/dist/tools/sign/transactions.js.map +1 -0
  105. package/dist/tools/sign/wallet.d.ts +50 -0
  106. package/dist/tools/sign/wallet.d.ts.map +1 -0
  107. package/dist/tools/sign/wallet.js +180 -0
  108. package/dist/tools/sign/wallet.js.map +1 -0
  109. package/dist/utils.d.ts +23 -2
  110. package/dist/utils.d.ts.map +1 -1
  111. package/dist/utils.js +48 -3
  112. package/dist/utils.js.map +1 -1
  113. package/dist/wallet-client.d.ts +34 -4
  114. package/dist/wallet-client.d.ts.map +1 -1
  115. package/dist/wallet-client.js +106 -27
  116. package/dist/wallet-client.js.map +1 -1
  117. package/dist/walletconnect.d.ts +100 -13
  118. package/dist/walletconnect.d.ts.map +1 -1
  119. package/dist/walletconnect.js +215 -50
  120. package/dist/walletconnect.js.map +1 -1
  121. package/package.json +12 -9
  122. package/src/anvil.ts +150 -271
  123. package/src/chain-meta.ts +16 -7
  124. package/src/chains.ts +14 -2
  125. package/src/clear-signing.ts +15 -9
  126. package/src/client.ts +0 -1
  127. package/src/context.ts +27 -0
  128. package/src/hosted.ts +11 -0
  129. package/src/http-server.ts +38 -8
  130. package/src/index.ts +20 -13
  131. package/src/kv-storage.ts +6 -3
  132. package/src/lib.ts +24 -0
  133. package/src/mcp-server.ts +47 -0
  134. package/src/oauth.ts +181 -69
  135. package/src/preview.ts +28 -7
  136. package/src/signing-requests.ts +198 -0
  137. package/src/tools/index.ts +78 -16
  138. package/src/tools/{advanced.ts → read/advanced.ts} +74 -43
  139. package/src/tools/{balance.ts → read/balance.ts} +20 -35
  140. package/src/tools/{contract-info.ts → read/contract-info.ts} +66 -146
  141. package/src/tools/{contract.ts → read/contract.ts} +4 -3
  142. package/src/tools/{ens.ts → read/ens.ts} +27 -34
  143. package/src/tools/read/gas.ts +443 -0
  144. package/src/tools/read/logs.ts +281 -0
  145. package/src/tools/{signatures.ts → read/signatures.ts} +5 -5
  146. package/src/tools/read/signing.ts +92 -0
  147. package/src/tools/sign/errors.ts +28 -0
  148. package/src/tools/sign/transactions.ts +479 -0
  149. package/src/tools/sign/wallet.ts +216 -0
  150. package/src/utils.ts +52 -3
  151. package/src/wallet-client.ts +107 -24
  152. package/src/walletconnect.ts +250 -48
  153. package/dist/tools/advanced.d.ts.map +0 -1
  154. package/dist/tools/advanced.js.map +0 -1
  155. package/dist/tools/balance.d.ts.map +0 -1
  156. package/dist/tools/balance.js.map +0 -1
  157. package/dist/tools/contract-info.d.ts.map +0 -1
  158. package/dist/tools/contract-info.js.map +0 -1
  159. package/dist/tools/contract.d.ts.map +0 -1
  160. package/dist/tools/contract.js.map +0 -1
  161. package/dist/tools/ens.d.ts +0 -119
  162. package/dist/tools/ens.d.ts.map +0 -1
  163. package/dist/tools/ens.js.map +0 -1
  164. package/dist/tools/gas.d.ts +0 -99
  165. package/dist/tools/gas.d.ts.map +0 -1
  166. package/dist/tools/gas.js +0 -181
  167. package/dist/tools/gas.js.map +0 -1
  168. package/dist/tools/logs.d.ts +0 -39
  169. package/dist/tools/logs.d.ts.map +0 -1
  170. package/dist/tools/logs.js +0 -230
  171. package/dist/tools/logs.js.map +0 -1
  172. package/dist/tools/signatures.d.ts.map +0 -1
  173. package/dist/tools/signatures.js.map +0 -1
  174. package/dist/tools/transactions.d.ts +0 -155
  175. package/dist/tools/transactions.d.ts.map +0 -1
  176. package/dist/tools/transactions.js +0 -365
  177. package/dist/tools/transactions.js.map +0 -1
  178. package/src/tools/gas.ts +0 -219
  179. package/src/tools/logs.ts +0 -293
  180. package/src/tools/transactions.ts +0 -456
@@ -1,9 +1,11 @@
1
1
  import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js'
2
+ import type { OAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/provider.js'
2
3
  import { getOAuthProtectedResourceMetadataUrl, mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js'
3
4
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
5
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
5
- import express from 'express'
6
+ import express, { type Express } from 'express'
6
7
  import type { WalletRelay } from 'web3-wallet-relay'
8
+ import { HOSTED } from './hosted.js'
7
9
  import { SingleUserOAuthProvider } from './oauth.js'
8
10
 
9
11
  /**
@@ -31,24 +33,50 @@ export interface HttpServerOptions {
31
33
  /** Serve the signing page on this same server, so browser signing works when hosted. */
32
34
  walletRelay?: WalletRelay
33
35
  createMcpServer: () => McpServer
36
+ /**
37
+ * Who may connect, and as whom.
38
+ *
39
+ * The default authenticates the deployment rather than a person: one token, shared. A
40
+ * host serving several people supplies its own — logging them in however it likes, and
41
+ * putting the identity in the issued token's `extra`, which is where the tools read it
42
+ * from. `MCP_TOKEN` is then no longer required.
43
+ */
44
+ provider?: OAuthServerProvider
45
+ /** A host's own routes, mounted before the signing page claims whatever is left at the root. */
46
+ routes?: (app: Express) => void
34
47
  }
35
48
 
36
49
  export async function startHttpServer(options: HttpServerOptions): Promise<{ url: string; port: number; walletUrl?: string }> {
37
- const port = options.port ?? Number(process.env.MCP_HTTP_PORT) ?? DEFAULT_PORT
50
+ // The guards a shared host needs — no `localhost` chain, no loopback tracing node, no
51
+ // browser opened on the server — were decided when the package loaded, and cannot be
52
+ // switched on from here.
53
+ if (!HOSTED) {
54
+ throw new Error(
55
+ 'Serving over HTTP needs MCP_HOSTED=1 (or MCP_HTTP_PORT) in the environment before web3-tools-mcp is imported'
56
+ )
57
+ }
58
+
59
+ const port = options.port ?? (Number(process.env.MCP_HTTP_PORT) || DEFAULT_PORT)
38
60
  const host = options.host ?? process.env.MCP_HTTP_HOST ?? '0.0.0.0'
39
61
  const token = options.token ?? process.env.MCP_TOKEN
40
62
 
41
63
  // A reachable MCP server with a paired phone can push signing prompts at that phone. It
42
- // never runs without a token.
43
- if (!token) {
64
+ // never runs unauthenticated — either a host brought its own way to log people in, or
65
+ // there is a token.
66
+ if (!options.provider && !token) {
44
67
  throw new Error('MCP_TOKEN is required to serve MCP over HTTP — anyone reaching the URL could request signatures')
45
68
  }
46
69
 
47
70
  const publicUrl = options.publicUrl ?? process.env.MCP_PUBLIC_URL ?? `http://localhost:${port}`
48
71
  const issuer = new URL(publicUrl)
49
- const provider = new SingleUserOAuthProvider(token)
72
+ const provider = options.provider ?? new SingleUserOAuthProvider(token as string)
50
73
 
51
74
  const app = express()
75
+ // The auth routes rate-limit per req.ip, which behind a platform proxy is the proxy's own
76
+ // address — every caller would share one bucket. Only trust as many hops as are really
77
+ // in front, or anyone could pick their own bucket with a forged X-Forwarded-For.
78
+ const proxyHops = Number(process.env.MCP_TRUST_PROXY)
79
+ if (proxyHops) app.set('trust proxy', proxyHops)
52
80
  app.use(express.json())
53
81
  // The login form posts a token, so the authorize route needs form bodies too.
54
82
  app.use(express.urlencoded({ extended: false }))
@@ -78,6 +106,8 @@ export async function startHttpServer(options: HttpServerOptions): Promise<{ url
78
106
  resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(new URL('/mcp', issuer))
79
107
  })
80
108
 
109
+ options.routes?.(app)
110
+
81
111
  app.all('/mcp', authenticate, async (req, res) => {
82
112
  const mcp = options.createMcpServer()
83
113
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true })
@@ -99,8 +129,8 @@ export async function startHttpServer(options: HttpServerOptions): Promise<{ url
99
129
  })
100
130
 
101
131
  const server = await new Promise<ReturnType<typeof app.listen>>((resolve, reject) => {
102
- const listening = app.listen(port, host, () => resolve(listening))
103
- listening.once('error', reject)
132
+ // Express 5 hands a listen error to this callback rather than leaving it to 'error'.
133
+ const listening = app.listen(port, host, (error?: Error) => (error ? reject(error) : resolve(listening)))
104
134
  })
105
135
 
106
136
  // The signing page rides on this same server, so a hosted deployment can offer browser
@@ -110,7 +140,7 @@ export async function startHttpServer(options: HttpServerOptions): Promise<{ url
110
140
  let walletUrl: string | undefined
111
141
  if (options.walletRelay) {
112
142
  options.walletRelay.attach(server, (relayApp) => app.use(relayApp))
113
- walletUrl = `${publicUrl.replace(/\/$/, '')}/#t=${options.walletRelay.token}`
143
+ walletUrl = `${publicUrl.replace(/\/$/, '')}/#t=${options.walletRelay.signerToken}`
114
144
  }
115
145
 
116
146
  return { url: `${publicUrl.replace(/\/$/, '')}/mcp`, port: (server.address() as { port: number }).port, walletUrl }
package/src/index.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
5
4
  import { WalletRelay } from 'web3-wallet-relay'
6
5
  import packageJson from '../package.json' with { type: 'json' }
7
6
  import { initializeClientManager, SUPPORTED_CHAINS } from './client.js'
7
+ import { DEFAULT_IDENTITY } from './context.js'
8
8
  import { startHttpServer } from './http-server.js'
9
9
  import { attachLogServer } from './log.js'
10
- import { registerAllTools } from './tools/index.js'
10
+ import { createMcpServer } from './mcp-server.js'
11
11
  import { parseCommandLineArgs } from './utils.js'
12
- import { getWalletClient } from './wallet-client.js'
12
+ import { allWalletClients, configureWalletRelay, getWalletClient } from './wallet-client.js'
13
13
 
14
14
  // Parse configuration
15
15
  const config = parseCommandLineArgs()
@@ -42,12 +42,17 @@ ENVIRONMENT VARIABLES:
42
42
  WALLETCONNECT_PROJECT_ID Alternative to --walletconnect-project-id
43
43
  CUSTOM_RPC Alternative to --custom-rpc
44
44
  MCP_HTTP_PORT Serve MCP over HTTP on this port instead of stdio
45
+ MCP_HTTP_HOST Interface the HTTP server binds to (default: 0.0.0.0)
46
+ MCP_TRUST_PROXY Proxy hops in front of the server, e.g. 1 on Fly/Render
47
+ MCP_HOSTED Apply the hosted guards without the CLI (library use)
45
48
  MCP_TOKEN Bearer token required by the HTTP transport
46
49
  MCP_PUBLIC_URL Public URL of this server, advertised in OAuth metadata
47
50
  UPSTASH_REDIS_REST_URL Keep WalletConnect sessions in Redis, not on disk
48
51
  UPSTASH_REDIS_REST_TOKEN
49
52
  WALLET_SERVER_URL URL of a hosted wallet relay (omit to run one locally)
50
- WALLET_TOKEN Shared pairing token for the wallet relay
53
+ WALLET_TOKEN Secret the wallet relay signs its room tokens with
54
+ ANVIL_RPC_URL Node exposing debug_traceCall (default: 127.0.0.1:8545)
55
+ ANVIL_ALLOW_RESET Let tracing re-fork that node, clearing its state
51
56
 
52
57
  SUPPORTED CHAINS:
53
58
  ${SUPPORTED_CHAINS.join(', ')}
@@ -92,12 +97,6 @@ if (config.customRpcUrls) {
92
97
  // Initialize client manager
93
98
  initializeClientManager(config)
94
99
 
95
- function createMcpServer() {
96
- const server = new McpServer({ name: 'web3-tools-mcp', version: packageJson.version }, { capabilities: { logging: {} } })
97
- registerAllTools(server)
98
- return server
99
- }
100
-
101
100
  const httpMode = Boolean(process.env.MCP_HTTP_PORT)
102
101
  const httpPort = Number(process.env.MCP_HTTP_PORT)
103
102
 
@@ -114,7 +113,13 @@ const hostedRelay = httpMode
114
113
  ? new WalletRelay({ token: process.env.WALLET_TOKEN, publicUrl: process.env.MCP_PUBLIC_URL })
115
114
  : undefined
116
115
 
117
- const wallet = getWalletClient(hostedRelay ? { port: httpPort, token: hostedRelay.token } : undefined)
116
+ // Hosted, the relay is this process's own, on the platform's port. Set before any client
117
+ // is made, so every identity's client reaches that one rather than starting its own.
118
+ if (hostedRelay) configureWalletRelay({ port: httpPort, token: hostedRelay.token })
119
+
120
+ // Outside any tool call, so this is the shared identity — the only one a self-hosted
121
+ // server ever has, and the one a hosted server uses before anybody has authenticated.
122
+ const wallet = getWalletClient(DEFAULT_IDENTITY)
118
123
 
119
124
  // Hosted, the relay is attached to the HTTP server, so connect only once that is listening.
120
125
  const walletReady = httpMode
@@ -131,7 +136,7 @@ async function shutdown(reason: string) {
131
136
  if (shuttingDown) return
132
137
  shuttingDown = true
133
138
  console.error(`[MCP] Shutting down (${reason})`)
134
- await wallet.stop().catch(() => {})
139
+ await Promise.all(allWalletClients().map((client) => client.stop().catch(() => {})))
135
140
  process.exit(0)
136
141
  }
137
142
 
@@ -150,7 +155,9 @@ async function main() {
150
155
  await wallet.connect().catch((error) => {
151
156
  console.error('[MCP] Could not join the wallet relay:', error.message)
152
157
  })
153
- if (walletUrl) console.error(`Wallet interface available at ${walletUrl}`)
158
+ // Without its #t= token: platform logs are read by more people than may sign. The full
159
+ // link comes from wallet_status, which already requires MCP_TOKEN.
160
+ if (walletUrl) console.error(`Wallet interface available at ${walletUrl.split('#')[0]} (link with token via wallet_status)`)
154
161
 
155
162
  if (!config.walletConnectProjectId) {
156
163
  console.error('[MCP] No WalletConnect project id — phone signing is unavailable on this server')
package/src/kv-storage.ts CHANGED
@@ -15,7 +15,8 @@ export interface KeyValueStorage {
15
15
  getKeys(): Promise<string[]>
16
16
  getEntries<T = unknown>(): Promise<[string, T][]>
17
17
  getItem<T = unknown>(key: string): Promise<T | undefined>
18
- setItem<T = unknown>(key: string, value: T): Promise<void>
18
+ /** `ttl` (seconds) expires just this field, where the backend can. */
19
+ setItem<T = unknown>(key: string, value: T, ttl?: number): Promise<void>
19
20
  removeItem(key: string): Promise<void>
20
21
  }
21
22
 
@@ -30,7 +31,8 @@ class RedisKeyValueStorage implements KeyValueStorage {
30
31
  const response = await fetch(this.url, {
31
32
  method: 'POST',
32
33
  headers: { authorization: `Bearer ${this.token}`, 'content-type': 'application/json' },
33
- body: JSON.stringify(parts)
34
+ body: JSON.stringify(parts),
35
+ signal: AbortSignal.timeout(5000)
34
36
  })
35
37
 
36
38
  if (!response.ok) {
@@ -62,8 +64,9 @@ class RedisKeyValueStorage implements KeyValueStorage {
62
64
  return value == null ? undefined : (JSON.parse(value) as T)
63
65
  }
64
66
 
65
- async setItem<T = unknown>(key: string, value: T): Promise<void> {
67
+ async setItem<T = unknown>(key: string, value: T, ttl?: number): Promise<void> {
66
68
  await this.command('HSET', HASH_KEY, key, JSON.stringify(value))
69
+ if (ttl) await this.command('HEXPIRE', HASH_KEY, Math.ceil(ttl), 'FIELDS', 1, key)
67
70
  }
68
71
 
69
72
  async removeItem(key: string): Promise<void> {
package/src/lib.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * What this package offers a host that serves it to other people.
3
+ *
4
+ * The CLI at `index.ts` is a program: importing it parses argv, starts a relay and runs a
5
+ * server. This is the library half — nothing here has an effect until it is called, so a
6
+ * deployment can assemble its own server with its own way of deciding who may do what.
7
+ *
8
+ * The split in what each side owns is deliberate. This package knows how to talk to chains
9
+ * and wallets and how to keep one person's signing away from another's; it holds no opinion
10
+ * about who anybody is, what they are allowed, or what it costs. Those are policy, and they
11
+ * belong to whatever is doing the serving.
12
+ */
13
+
14
+ export { initializeClientManager, SUPPORTED_CHAINS } from './client.js'
15
+ export { DEFAULT_IDENTITY, identityFrom } from './context.js'
16
+ export { type HttpServerOptions, startHttpServer } from './http-server.js'
17
+ export { attachLogServer, log } from './log.js'
18
+ export { createMcpServer } from './mcp-server.js'
19
+ export { type Login, OAuthProvider, SingleUserOAuthProvider, tokenLogin } from './oauth.js'
20
+ export { registerAllTools, type ToolGroup, type ToolMiddleware, toolGroup } from './tools/index.js'
21
+ export type { ChainName, Config, ToolResult } from './types.js'
22
+ export { parseCommandLineArgs } from './utils.js'
23
+ export { allWalletClients, configureWalletRelay, getWalletClient, WalletClient } from './wallet-client.js'
24
+ export { getPhoneSigner, PhoneSigner, qrSvg } from './walletconnect.js'
@@ -0,0 +1,47 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import packageJson from '../package.json' with { type: 'json' }
3
+ import { registerAllTools, type ToolMiddleware } from './tools/index.js'
4
+
5
+ /**
6
+ * A server with every tool registered, ready to be connected to a transport.
7
+ *
8
+ * Exists so a host does not have to construct one itself. Reaching for the MCP SDK from
9
+ * outside would mean a second copy of it, and two copies of the same class are two
10
+ * unrelated types as far as a compiler is concerned — the error arrives at the seam rather
11
+ * than where the mistake was made.
12
+ */
13
+ /**
14
+ * What a client shows for this server, when it has somewhere to fetch it from.
15
+ *
16
+ * Only when a public URL is known: the icon has to be same-origin with the MCP endpoint and
17
+ * fetchable without credentials, which is true of the signing page's own static files and
18
+ * not true of anything a stdio server could offer. A client that cannot resolve it falls
19
+ * back to a letter, which is what it already does.
20
+ */
21
+ function identity() {
22
+ const publicUrl = process.env.MCP_PUBLIC_URL?.replace(/\/$/, '')
23
+ if (!publicUrl) return {}
24
+
25
+ return {
26
+ websiteUrl: publicUrl,
27
+ icons: [
28
+ { src: `${publicUrl}/icon.png`, mimeType: 'image/png', sizes: ['512x512'] },
29
+ { src: `${publicUrl}/icon.svg`, mimeType: 'image/svg+xml', sizes: ['any'] }
30
+ ]
31
+ }
32
+ }
33
+
34
+ export function createMcpServer(middleware?: ToolMiddleware): McpServer {
35
+ const server = new McpServer(
36
+ {
37
+ name: 'web3-tools-mcp',
38
+ version: packageJson.version,
39
+ title: 'Web3 Tools',
40
+ description: packageJson.description,
41
+ ...identity()
42
+ },
43
+ { capabilities: { logging: {} } }
44
+ )
45
+ registerAllTools(server, middleware)
46
+ return server
47
+ }