bsv-mcp 0.0.34 → 0.0.36
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.
- package/CHANGELOG.md +20 -0
- package/dist/index.js +62893 -0
- package/index.ts +33 -36
- package/package.json +11 -7
- package/resources/junglebus.ts +242 -0
- package/resources/resources.ts +4 -0
- package/tools/a2b/discover.ts +184 -44
- package/tools/wallet/a2bPublishMcp.ts +163 -15
- package/tools/wallet/fetchPaymentUtxos.ts +130 -0
- package/tools/wallet/refreshUtxos.ts +76 -0
- package/tools/wallet/tools.ts +5 -0
- package/tools/wallet/wallet.ts +22 -17
package/index.ts
CHANGED
|
@@ -39,18 +39,19 @@ function initializePrivateKey(): PrivateKey | undefined {
|
|
|
39
39
|
|
|
40
40
|
// Check if private key is set
|
|
41
41
|
if (!privateKeyWif) {
|
|
42
|
-
console
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
// stdio protocol you cant send anything to console!!
|
|
43
|
+
// console.warn(
|
|
44
|
+
// "\x1b[33mWarning: PRIVATE_KEY_WIF environment variable is not set\x1b[0m",
|
|
45
|
+
// );
|
|
46
|
+
// console.warn(
|
|
47
|
+
// "The server will run, but wallet operations requiring a private key will return errors.",
|
|
48
|
+
// );
|
|
49
|
+
// console.warn(
|
|
50
|
+
// "Set this variable with a valid Bitcoin SV private key in WIF format to enable all features:",
|
|
51
|
+
// );
|
|
52
|
+
// console.warn(
|
|
53
|
+
// "Example: PRIVATE_KEY_WIF=your_private_key_wif bun run index.ts",
|
|
54
|
+
// );
|
|
54
55
|
return undefined;
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -58,13 +59,13 @@ function initializePrivateKey(): PrivateKey | undefined {
|
|
|
58
59
|
try {
|
|
59
60
|
return PrivateKey.fromWif(privateKeyWif);
|
|
60
61
|
} catch (error) {
|
|
61
|
-
console.warn("\x1b[33mWarning: Invalid private key format\x1b[0m");
|
|
62
|
-
console.warn(
|
|
63
|
-
|
|
64
|
-
);
|
|
65
|
-
console.warn(
|
|
66
|
-
|
|
67
|
-
);
|
|
62
|
+
// console.warn("\x1b[33mWarning: Invalid private key format\x1b[0m");
|
|
63
|
+
// console.warn(
|
|
64
|
+
// "The PRIVATE_KEY_WIF provided is not a valid Bitcoin SV private key in WIF format.",
|
|
65
|
+
// );
|
|
66
|
+
// console.warn(
|
|
67
|
+
// "The server will run, but wallet operations requiring a private key will return errors.",
|
|
68
|
+
// );
|
|
68
69
|
return undefined;
|
|
69
70
|
}
|
|
70
71
|
}
|
|
@@ -73,23 +74,18 @@ function initializePrivateKey(): PrivateKey | undefined {
|
|
|
73
74
|
const privKey = initializePrivateKey();
|
|
74
75
|
|
|
75
76
|
const server = new McpServer(
|
|
76
|
-
{ name: "Bitcoin SV", version: "0.0.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// instructions: `
|
|
89
|
-
// This server exposes Bitcoin SV helpers.
|
|
90
|
-
// Tools are idempotent unless marked destructive.
|
|
91
|
-
// `,
|
|
92
|
-
// },
|
|
77
|
+
{ name: "Bitcoin SV", version: "0.0.36" },
|
|
78
|
+
{
|
|
79
|
+
capabilities: {
|
|
80
|
+
prompts: {},
|
|
81
|
+
resources: {},
|
|
82
|
+
tools: {},
|
|
83
|
+
},
|
|
84
|
+
instructions: `
|
|
85
|
+
This server exposes Bitcoin SV helpers.
|
|
86
|
+
Tools are idempotent unless marked destructive.
|
|
87
|
+
`,
|
|
88
|
+
},
|
|
93
89
|
);
|
|
94
90
|
|
|
95
91
|
// Initialize wallet and register tools based on configuration
|
|
@@ -134,3 +130,4 @@ if (CONFIG.loadPrompts) {
|
|
|
134
130
|
// Connect to the transport
|
|
135
131
|
const transport = new StdioServerTransport();
|
|
136
132
|
await server.connect(transport);
|
|
133
|
+
console.error("BSV MCP Server running on stdio");
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
|
-
"module": "index.
|
|
3
|
+
"module": "dist/index.js",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.36",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "satchmo",
|
|
8
8
|
"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
"bitcoin",
|
|
15
15
|
"bsv",
|
|
16
16
|
"bitcoin-sv",
|
|
17
|
-
"mcp",
|
|
18
|
-
"model-context-protocol",
|
|
19
17
|
"wallet",
|
|
20
18
|
"ordinals",
|
|
21
|
-
"blockchain"
|
|
19
|
+
"blockchain",
|
|
20
|
+
"1sat-ordinals",
|
|
21
|
+
"explorer",
|
|
22
|
+
"block explorer"
|
|
22
23
|
],
|
|
23
24
|
"files": [
|
|
25
|
+
"dist/**/*.js",
|
|
24
26
|
"package.json",
|
|
25
27
|
"*.ts",
|
|
26
28
|
"tools/*.ts",
|
|
@@ -35,7 +37,7 @@
|
|
|
35
37
|
"smithery.yaml"
|
|
36
38
|
],
|
|
37
39
|
"bin": {
|
|
38
|
-
"bsv-mcp": "./index.
|
|
40
|
+
"bsv-mcp": "./dist/index.js"
|
|
39
41
|
},
|
|
40
42
|
"private": false,
|
|
41
43
|
"devDependencies": {
|
|
@@ -48,7 +50,8 @@
|
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"@bsv/sdk": "^1.4.20",
|
|
50
52
|
"@modelcontextprotocol/sdk": "^1.10.2",
|
|
51
|
-
"@types/node": "^22.
|
|
53
|
+
"@types/node": "^22.15.2",
|
|
54
|
+
"bun": "^1.2.10",
|
|
52
55
|
"js-1sat-ord": "^0.1.81",
|
|
53
56
|
"mnee": "^2.0.0",
|
|
54
57
|
"satoshi-token": "^0.0.4",
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
"zod": "^3.24.3"
|
|
57
60
|
},
|
|
58
61
|
"scripts": {
|
|
62
|
+
"build": "bun build ./index.ts --outdir ./dist --target node",
|
|
59
63
|
"lint": "biome check .",
|
|
60
64
|
"lint:fix": "biome check . --write"
|
|
61
65
|
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
|
|
3
|
+
// Main JungleBus API documentation URL
|
|
4
|
+
const JUNGLEBUS_DOCS_URL = "https://junglebus.gorillapool.io/docs/";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Manually structured JungleBus API documentation
|
|
8
|
+
* Provides a clean summary of key JungleBus API endpoints and functionality
|
|
9
|
+
*/
|
|
10
|
+
export function getJungleBusDocumentation(): string {
|
|
11
|
+
return `# JungleBus API Documentation
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
JungleBus is a transaction monitoring service for Bitcoin SV that allows applications to subscribe to transaction events and filter them based on specific criteria. The API provides both subscription and querying capabilities.
|
|
16
|
+
|
|
17
|
+
## API Endpoints
|
|
18
|
+
|
|
19
|
+
### Base URL
|
|
20
|
+
\`\`\`
|
|
21
|
+
https://junglebus.gorillapool.io/v1
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
### Transaction API
|
|
25
|
+
|
|
26
|
+
#### Get Transaction by ID
|
|
27
|
+
\`\`\`
|
|
28
|
+
GET /transaction/get/{txid}
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
Returns detailed information about a specific transaction, including:
|
|
32
|
+
- Transaction data (hex format)
|
|
33
|
+
- Block information (hash, height, time)
|
|
34
|
+
- Input and output details
|
|
35
|
+
- Address information
|
|
36
|
+
|
|
37
|
+
#### Get Transactions by Block Hash
|
|
38
|
+
\`\`\`
|
|
39
|
+
GET /block/{blockhash}/transactions
|
|
40
|
+
\`\`\`
|
|
41
|
+
|
|
42
|
+
Returns all transactions within a specific block.
|
|
43
|
+
|
|
44
|
+
### Subscription API
|
|
45
|
+
|
|
46
|
+
#### Create Subscription
|
|
47
|
+
\`\`\`
|
|
48
|
+
POST /subscribe
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
Create a new subscription to monitor transactions based on filtering criteria.
|
|
52
|
+
|
|
53
|
+
Example request body:
|
|
54
|
+
\`\`\`json
|
|
55
|
+
{
|
|
56
|
+
"callback": "https://your-callback-url.com",
|
|
57
|
+
"fromBlock": 0,
|
|
58
|
+
"query": {
|
|
59
|
+
"find": {
|
|
60
|
+
"out.tape.cell.s": "BEEF"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
#### Delete Subscription
|
|
67
|
+
\`\`\`
|
|
68
|
+
DELETE /subscribe/{id}
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
Deletes an existing subscription.
|
|
72
|
+
|
|
73
|
+
### Network API
|
|
74
|
+
|
|
75
|
+
#### Get Network Info
|
|
76
|
+
\`\`\`
|
|
77
|
+
GET /network/info
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
Returns current blockchain network information, including block height and other statistics.
|
|
81
|
+
|
|
82
|
+
## Client Implementations
|
|
83
|
+
|
|
84
|
+
### TypeScript Client
|
|
85
|
+
|
|
86
|
+
Installation:
|
|
87
|
+
\`\`\`bash
|
|
88
|
+
$ npm install @gorillapool/js-junglebus
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
Usage:
|
|
92
|
+
\`\`\`javascript
|
|
93
|
+
import { JungleBusClient } from '@gorillapool/js-junglebus';
|
|
94
|
+
|
|
95
|
+
const server = "junglebus.gorillapool.io";
|
|
96
|
+
const jungleBusClient = new JungleBusClient(server, {
|
|
97
|
+
onConnected(ctx) {
|
|
98
|
+
// add your own code here
|
|
99
|
+
console.log(ctx);
|
|
100
|
+
},
|
|
101
|
+
onConnecting(ctx) {
|
|
102
|
+
// add your own code here
|
|
103
|
+
console.log(ctx);
|
|
104
|
+
},
|
|
105
|
+
onDisconnected(ctx) {
|
|
106
|
+
// add your own code here
|
|
107
|
+
console.log(ctx);
|
|
108
|
+
},
|
|
109
|
+
onError(ctx) {
|
|
110
|
+
// add your own code here
|
|
111
|
+
console.error(ctx);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// create subscriptions in the dashboard of the JungleBus website
|
|
116
|
+
const subId = "...."; // fill in the ID for the subscription
|
|
117
|
+
const fromBlock = 750000;
|
|
118
|
+
|
|
119
|
+
const subscription = jungleBusClient.Subscribe(
|
|
120
|
+
subId,
|
|
121
|
+
fromBlock,
|
|
122
|
+
onPublish(tx) => {
|
|
123
|
+
// add your own code here
|
|
124
|
+
console.log(tx);
|
|
125
|
+
},
|
|
126
|
+
onStatus(ctx) => {
|
|
127
|
+
// add your own code here
|
|
128
|
+
console.log(ctx);
|
|
129
|
+
},
|
|
130
|
+
onError(ctx) => {
|
|
131
|
+
// add your own code here
|
|
132
|
+
console.log(ctx);
|
|
133
|
+
},
|
|
134
|
+
onMempool(tx) => {
|
|
135
|
+
// add your own code here
|
|
136
|
+
console.log(tx);
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// For lite mode (transaction hash and block height only)
|
|
141
|
+
await client.Subscribe("a5e2fa655c41753331539a2a86546bf9335ff6d9b7a512dc9acddb00ab9985c0", 1550000, onPublish, onStatus, onError, onMempool, true);
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
### Go Client
|
|
145
|
+
|
|
146
|
+
Installation:
|
|
147
|
+
\`\`\`bash
|
|
148
|
+
go get github.com/GorillaPool/go-junglebus
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
Usage:
|
|
152
|
+
\`\`\`go
|
|
153
|
+
package main
|
|
154
|
+
|
|
155
|
+
import (
|
|
156
|
+
"context"
|
|
157
|
+
"log"
|
|
158
|
+
"sync"
|
|
159
|
+
"github.com/GorillaPool/go-junglebus"
|
|
160
|
+
"github.com/GorillaPool/go-junglebus/models"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
func main() {
|
|
164
|
+
wg := &sync.WaitGroup{}
|
|
165
|
+
|
|
166
|
+
junglebusClient, err := junglebus.New(
|
|
167
|
+
junglebus.WithHTTP("https://junglebus.gorillapool.io"),
|
|
168
|
+
)
|
|
169
|
+
if err != nil {
|
|
170
|
+
log.Fatalln(err.Error())
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
subscriptionID := "..." // fill in the ID for the subscription
|
|
174
|
+
fromBlock := uint64(750000)
|
|
175
|
+
|
|
176
|
+
eventHandler := junglebus.EventHandler{
|
|
177
|
+
// do not set this function to leave out mined transactions
|
|
178
|
+
OnTransaction: func(tx *models.TransactionResponse) {
|
|
179
|
+
log.Printf("[TX]: %d: %v", tx.BlockHeight, tx.Id)
|
|
180
|
+
},
|
|
181
|
+
// do not set this function to leave out mempool transactions
|
|
182
|
+
OnMempool: func(tx *models.TransactionResponse) {
|
|
183
|
+
log.Printf("[MEMPOOL TX]: %v", tx.Id)
|
|
184
|
+
},
|
|
185
|
+
OnStatus: func(status *models.ControlResponse) {
|
|
186
|
+
log.Printf("[STATUS]: %v", status)
|
|
187
|
+
},
|
|
188
|
+
OnError: func(err error) {
|
|
189
|
+
log.Printf("[ERROR]: %v", err)
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
var subscription *junglebus.Subscription
|
|
194
|
+
if subscription, err = junglebusClient.Subscribe(context.Background(), subscriptionID, fromBlock, eventHandler); err != nil {
|
|
195
|
+
log.Printf("ERROR: failed getting subscription %s", err.Error())
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// For lite mode
|
|
199
|
+
if subscription, err := junglebusClient.SubscribeWithQueue(context.Background(), subscriptionID, fromBlock, 0, eventHandler, &junglebus.SubscribeOptions{
|
|
200
|
+
QueueSize: 100000,
|
|
201
|
+
LiteMode: true,
|
|
202
|
+
}); err != nil {
|
|
203
|
+
log.Printf("ERROR: failed getting subscription %s", err.Error())
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
wg.Add(1)
|
|
207
|
+
wg.Wait()
|
|
208
|
+
}
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
## Further Reading
|
|
212
|
+
|
|
213
|
+
For complete API documentation, visit [JungleBus Docs](${JUNGLEBUS_DOCS_URL})
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Register the JungleBus API documentation resource with the MCP server
|
|
219
|
+
* @param server The MCP server instance
|
|
220
|
+
*/
|
|
221
|
+
export function registerJungleBusResource(server: McpServer): void {
|
|
222
|
+
server.resource(
|
|
223
|
+
"junglebus-api-docs",
|
|
224
|
+
JUNGLEBUS_DOCS_URL,
|
|
225
|
+
{
|
|
226
|
+
title: "JungleBus API Documentation",
|
|
227
|
+
description:
|
|
228
|
+
"API documentation for JungleBus, a transaction monitoring service for Bitcoin SV",
|
|
229
|
+
},
|
|
230
|
+
async (uri) => {
|
|
231
|
+
const documentationContent = getJungleBusDocumentation();
|
|
232
|
+
return {
|
|
233
|
+
contents: [
|
|
234
|
+
{
|
|
235
|
+
uri: uri.href,
|
|
236
|
+
text: documentationContent,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
}
|
package/resources/resources.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { registerBRCsResources } from "./brcs";
|
|
3
3
|
import { registerChangelogResource } from "./changelog";
|
|
4
|
+
import { registerJungleBusResource } from "./junglebus";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Register all resources with the MCP server
|
|
@@ -13,5 +14,8 @@ export function registerResources(server: McpServer): void {
|
|
|
13
14
|
// Register changelog resource
|
|
14
15
|
registerChangelogResource(server);
|
|
15
16
|
|
|
17
|
+
// Register JungleBus API documentation resource
|
|
18
|
+
registerJungleBusResource(server);
|
|
19
|
+
|
|
16
20
|
// Add more resource categories here as needed
|
|
17
21
|
}
|
package/tools/a2b/discover.ts
CHANGED
|
@@ -6,25 +6,34 @@ import type {
|
|
|
6
6
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// API endpoint for the A2B Overlay service
|
|
10
|
+
const OVERLAY_API_URL = "https://a2b-overlay-production.up.railway.app/v1";
|
|
11
|
+
|
|
12
|
+
type A2BDiscoveryItem = {
|
|
13
|
+
txid: string;
|
|
14
|
+
outpoint: string;
|
|
10
15
|
type: "agent" | "tool";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
app: string;
|
|
17
|
+
serverName: string;
|
|
18
|
+
command: string;
|
|
19
|
+
description: string;
|
|
20
|
+
keywords: string[];
|
|
21
|
+
args: Record<string, string>;
|
|
22
|
+
env: Record<string, string>;
|
|
23
|
+
blockHeight: number;
|
|
24
|
+
timestamp: string;
|
|
25
|
+
tools?: string[];
|
|
26
|
+
prompts?: string[];
|
|
27
|
+
resources?: string[];
|
|
16
28
|
};
|
|
17
29
|
|
|
18
|
-
type
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
name: string;
|
|
26
|
-
description: string;
|
|
27
|
-
}[];
|
|
30
|
+
type OverlaySearchResponse = {
|
|
31
|
+
items: A2BDiscoveryItem[];
|
|
32
|
+
total: number;
|
|
33
|
+
limit: number;
|
|
34
|
+
offset: number;
|
|
35
|
+
queryType: "agent" | "tool" | "all";
|
|
36
|
+
query: string;
|
|
28
37
|
};
|
|
29
38
|
|
|
30
39
|
// Schema for agent discovery parameters
|
|
@@ -38,6 +47,73 @@ export const a2bDiscoverArgsSchema = z.object({
|
|
|
38
47
|
});
|
|
39
48
|
export type A2bDiscoverArgs = z.infer<typeof a2bDiscoverArgsSchema>;
|
|
40
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Format the response in a user-friendly way
|
|
52
|
+
*/
|
|
53
|
+
function formatSearchResults(data: unknown, queryType: string): string {
|
|
54
|
+
// console.log(`Received data: ${JSON.stringify(data).substring(0, 200)}...`); // Debug log
|
|
55
|
+
|
|
56
|
+
// Check if data is an object with items property
|
|
57
|
+
if (!data) {
|
|
58
|
+
return `No ${queryType} results found.`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle the new API format where data is an object with 'items' array
|
|
62
|
+
const items = Array.isArray(data)
|
|
63
|
+
? data
|
|
64
|
+
: (data as { items?: A2BDiscoveryItem[] }).items;
|
|
65
|
+
|
|
66
|
+
// Ensure items is an array
|
|
67
|
+
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
68
|
+
return `No ${queryType} results found.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let result = `Found ${items.length} ${queryType === "all" ? "items" : `${queryType}s`}:\n\n`;
|
|
72
|
+
|
|
73
|
+
items.forEach((item, index) => {
|
|
74
|
+
if (!item) return;
|
|
75
|
+
|
|
76
|
+
// Agent or MCP server name with description
|
|
77
|
+
result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`;
|
|
78
|
+
|
|
79
|
+
// Display command to run
|
|
80
|
+
if (item.command) {
|
|
81
|
+
const args = item.args ? Object.values(item.args).join(" ") : "";
|
|
82
|
+
result += ` Command: \`${item.command} ${args}\`\n`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add tools count if available
|
|
86
|
+
if (item.tools && Array.isArray(item.tools)) {
|
|
87
|
+
result += ` Tools: ${item.tools.length} available\n`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add keywords if available
|
|
91
|
+
if (
|
|
92
|
+
item.keywords &&
|
|
93
|
+
Array.isArray(item.keywords) &&
|
|
94
|
+
item.keywords.length > 0
|
|
95
|
+
) {
|
|
96
|
+
result += ` Keywords: ${item.keywords.join(", ")}\n`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add blockchain details
|
|
100
|
+
if (item.outpoint) {
|
|
101
|
+
result += ` Outpoint: ${item.outpoint}\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (item.blockHeight !== undefined) {
|
|
105
|
+
const date = item.timestamp
|
|
106
|
+
? new Date(item.timestamp).toLocaleDateString()
|
|
107
|
+
: "Unknown date";
|
|
108
|
+
result += ` Block: ${item.blockHeight}, ${date}\n`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
result += "\n";
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
41
117
|
/**
|
|
42
118
|
* Registers the a2b_discover tool for on-chain agent discovery
|
|
43
119
|
*/
|
|
@@ -50,49 +126,113 @@ export function registerA2bDiscoverTool(server: McpServer) {
|
|
|
50
126
|
{ args }: { args: A2bDiscoverArgs },
|
|
51
127
|
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
52
128
|
) => {
|
|
53
|
-
if (args.queryType === "agent") {
|
|
54
|
-
return {
|
|
55
|
-
content: [
|
|
56
|
-
{ type: "text", text: "Agent discovery is not supported yet" },
|
|
57
|
-
],
|
|
58
|
-
isError: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (args.queryType !== "tool") {
|
|
63
|
-
return {
|
|
64
|
-
content: [
|
|
65
|
-
{
|
|
66
|
-
type: "text",
|
|
67
|
-
text: "Only tool discovery is supported currently",
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
isError: true,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
129
|
try {
|
|
75
130
|
const params = new URLSearchParams();
|
|
131
|
+
|
|
132
|
+
// Set query type (agent, tool, or all)
|
|
76
133
|
params.set("type", args.queryType);
|
|
77
|
-
|
|
78
|
-
|
|
134
|
+
|
|
135
|
+
// Use enhanced search for better relevance scoring
|
|
136
|
+
let searchEndpoint = "/search/enhanced";
|
|
137
|
+
|
|
138
|
+
// For empty queries, use the regular search endpoint
|
|
139
|
+
if (!args.query || !args.query.trim()) {
|
|
140
|
+
searchEndpoint = "/search";
|
|
141
|
+
} else {
|
|
142
|
+
params.set("q", args.query); // enhanced search uses 'q' parameter
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add pagination parameters
|
|
146
|
+
params.set("limit", args.limit?.toString() ?? "10");
|
|
79
147
|
params.set("offset", args.offset?.toString() ?? "0");
|
|
148
|
+
|
|
149
|
+
// Add block range if specified
|
|
80
150
|
if (args.fromBlock) {
|
|
81
151
|
params.set("fromBlock", args.fromBlock.toString());
|
|
82
152
|
}
|
|
83
153
|
if (args.toBlock) {
|
|
84
154
|
params.set("toBlock", args.toBlock.toString());
|
|
85
155
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
156
|
+
|
|
157
|
+
// Construct the full URL
|
|
158
|
+
const searchUrl = `${OVERLAY_API_URL}${searchEndpoint}?${params.toString()}`;
|
|
159
|
+
//console.log(`Searching URL: ${searchUrl}`);
|
|
160
|
+
|
|
161
|
+
// Make the request to the overlay API
|
|
162
|
+
const response = await fetch(searchUrl, {
|
|
163
|
+
method: "GET",
|
|
164
|
+
headers: {
|
|
165
|
+
Accept: "application/json",
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`API returned status ${response.status}: ${response.statusText}`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const data = (await response.json()) as OverlaySearchResponse;
|
|
176
|
+
|
|
177
|
+
// Format the results for better readability
|
|
178
|
+
let result = "";
|
|
179
|
+
|
|
180
|
+
if (data?.items?.length > 0) {
|
|
181
|
+
result = `Found ${data.items.length} ${args.queryType}(s):\n\n`;
|
|
182
|
+
|
|
183
|
+
data.items.forEach((item: A2BDiscoveryItem, index: number) => {
|
|
184
|
+
// Server name and description
|
|
185
|
+
result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`;
|
|
186
|
+
|
|
187
|
+
// Command to run
|
|
188
|
+
if (item.command) {
|
|
189
|
+
const cmdArgs = item.args
|
|
190
|
+
? Object.values(item.args).join(" ")
|
|
191
|
+
: "";
|
|
192
|
+
result += ` Command: \`${item.command} ${cmdArgs}\`\n`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Tools available
|
|
196
|
+
if (item.tools?.length) {
|
|
197
|
+
result += ` Tools: ${item.tools.length} available\n`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Keywords
|
|
201
|
+
if (item.keywords?.length) {
|
|
202
|
+
result += ` Keywords: ${item.keywords.join(", ")}\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Blockchain details
|
|
206
|
+
if (item.outpoint) {
|
|
207
|
+
result += ` Outpoint: ${item.outpoint}\n`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (item.blockHeight !== undefined) {
|
|
211
|
+
const date = item.timestamp
|
|
212
|
+
? new Date(item.timestamp).toLocaleDateString()
|
|
213
|
+
: "Unknown date";
|
|
214
|
+
result += ` Block: ${item.blockHeight}, ${date}\n`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
result += "\n";
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
result = `No ${args.queryType} results found.`;
|
|
221
|
+
}
|
|
222
|
+
|
|
89
223
|
return {
|
|
90
|
-
content: [{ type: "text", text:
|
|
224
|
+
content: [{ type: "text", text: result }],
|
|
91
225
|
isError: false,
|
|
92
226
|
};
|
|
93
227
|
} catch (error) {
|
|
228
|
+
console.error("Search error:", error);
|
|
94
229
|
return {
|
|
95
|
-
content: [
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: `Error querying A2B Overlay: ${error instanceof Error ? error.message : String(error)}`,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
96
236
|
isError: true,
|
|
97
237
|
};
|
|
98
238
|
}
|