bsv-mcp 0.2.9 → 0.2.11
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 +19 -0
- package/README.md +26 -2
- package/dist/index.js +6777 -126216
- package/index.ts +287 -265
- package/package.json +4 -3
- package/tools/bap/generate.test.ts +17 -16
- package/tools/index.ts +8 -3
- package/tools/ordinals/getInscription.ts +15 -65
- package/tools/ordinals/getTokenByIdOrTicker.ts +14 -80
- package/tools/ordinals/index.ts +8 -7
- package/tools/ordinals/marketListings.ts +21 -188
- package/tools/ordinals/marketSales.ts +21 -127
- package/tools/ordinals/searchInscriptions.ts +21 -108
- package/tools/wallet/brc100.ts +572 -0
- package/tools/wallet/cancelListing.ts +80 -0
- package/tools/wallet/createOrdinals.ts +62 -143
- package/tools/wallet/getAddress.ts +19 -13
- package/tools/wallet/getBalance.ts +29 -25
- package/tools/wallet/getBsv21Balances.ts +28 -0
- package/tools/wallet/getLockData.ts +25 -0
- package/tools/wallet/getOrdinals.ts +35 -0
- package/tools/wallet/listOrdinal.ts +86 -0
- package/tools/wallet/listTokens.ts +28 -0
- package/tools/wallet/lockBsv.ts +69 -0
- package/tools/wallet/opnsDeregister.ts +80 -0
- package/tools/wallet/opnsRegister.ts +80 -0
- package/tools/wallet/purchaseListing.ts +89 -290
- package/tools/wallet/refreshUtxos.ts +21 -32
- package/tools/wallet/schemas.ts +7 -11
- package/tools/wallet/sendAllBsv.ts +64 -0
- package/tools/wallet/sendToAddress.ts +46 -47
- package/tools/wallet/signBsm.ts +29 -0
- package/tools/wallet/sweepBsv.ts +70 -0
- package/tools/wallet/sweepBsv21.ts +71 -0
- package/tools/wallet/sweepOrdinals.ts +69 -0
- package/tools/wallet/tools.ts +50 -88
- package/tools/wallet/transferOrdToken.ts +137 -129
- package/tools/wallet/unlockBsv.ts +57 -0
- package/tools/wallet/a2bPublishAgent.ts +0 -307
- package/tools/wallet/sendOrdinals.ts +0 -160
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
3
|
"module": "dist/index.js",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.11",
|
|
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",
|
|
@@ -79,6 +79,8 @@
|
|
|
79
79
|
"typescript": "^5.9.3"
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
|
+
"@1sat/actions": "0.0.27",
|
|
83
|
+
"@1sat/wallet-remote": "0.0.7",
|
|
82
84
|
"@bsv/sdk": "^2.0.6",
|
|
83
85
|
"@modelcontextprotocol/ext-apps": "^1.2.0",
|
|
84
86
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -104,7 +106,6 @@
|
|
|
104
106
|
"start:next": "next start",
|
|
105
107
|
"lint": "biome check .",
|
|
106
108
|
"lint:fix": "biome check . --write",
|
|
107
|
-
"
|
|
108
|
-
"deploy:preview": "vercel"
|
|
109
|
+
"prepack": "bun run build:all"
|
|
109
110
|
}
|
|
110
111
|
}
|
|
@@ -23,7 +23,7 @@ import * as utxoUtils from "../wallet/fetchPaymentUtxos";
|
|
|
23
23
|
import { type BapGenerateArgs, registerBapGenerateTool } from "./generate";
|
|
24
24
|
|
|
25
25
|
type GenericToolHandler = (
|
|
26
|
-
params:
|
|
26
|
+
params: BapGenerateArgs,
|
|
27
27
|
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
28
28
|
) => CallToolResult | Promise<CallToolResult>;
|
|
29
29
|
|
|
@@ -115,12 +115,13 @@ describe("BAP Generate Tool", () => {
|
|
|
115
115
|
it("should register bap_generate tool with the MCP server", () => {
|
|
116
116
|
registerBapGenerateTool(mockMcpServerInstance as unknown as McpServer);
|
|
117
117
|
expect(mockServerToolSpy).toHaveBeenCalledTimes(1);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
);
|
|
118
|
+
const [name, description, schema, handler] =
|
|
119
|
+
mockServerToolSpy.mock.calls[0];
|
|
120
|
+
expect(name).toBe("bap_generate");
|
|
121
|
+
expect(typeof description).toBe("string");
|
|
122
|
+
expect(schema).toHaveProperty("alternateName");
|
|
123
|
+
expect(schema).toHaveProperty("description");
|
|
124
|
+
expect(typeof handler).toBe("function");
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
const getRegisteredHandler = (): GenericToolHandler => {
|
|
@@ -141,7 +142,7 @@ describe("BAP Generate Tool", () => {
|
|
|
141
142
|
ServerRequest,
|
|
142
143
|
ServerNotification
|
|
143
144
|
>;
|
|
144
|
-
const result = (await handler({
|
|
145
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
145
146
|
|
|
146
147
|
expect(result.isError).toBe(false);
|
|
147
148
|
expect(mkdirSyncSpy).toHaveBeenCalledWith(KEY_DIR, {
|
|
@@ -174,7 +175,7 @@ describe("BAP Generate Tool", () => {
|
|
|
174
175
|
ServerRequest,
|
|
175
176
|
ServerNotification
|
|
176
177
|
>;
|
|
177
|
-
const result = (await handler({
|
|
178
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
178
179
|
|
|
179
180
|
expect(result.isError).toBe(true);
|
|
180
181
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -192,7 +193,7 @@ describe("BAP Generate Tool", () => {
|
|
|
192
193
|
ServerRequest,
|
|
193
194
|
ServerNotification
|
|
194
195
|
>;
|
|
195
|
-
const result = (await handler({
|
|
196
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
196
197
|
|
|
197
198
|
expect(result.isError).toBe(true);
|
|
198
199
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -214,7 +215,7 @@ describe("BAP Generate Tool", () => {
|
|
|
214
215
|
ServerRequest,
|
|
215
216
|
ServerNotification
|
|
216
217
|
>;
|
|
217
|
-
const result = (await handler({
|
|
218
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
218
219
|
|
|
219
220
|
expect(result.isError).toBe(true);
|
|
220
221
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -236,7 +237,7 @@ describe("BAP Generate Tool", () => {
|
|
|
236
237
|
ServerRequest,
|
|
237
238
|
ServerNotification
|
|
238
239
|
>;
|
|
239
|
-
const result = (await handler({
|
|
240
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
240
241
|
|
|
241
242
|
expect(result.isError).toBe(true);
|
|
242
243
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -260,7 +261,7 @@ describe("BAP Generate Tool", () => {
|
|
|
260
261
|
try {
|
|
261
262
|
registerBapGenerateTool(mockMcpServerInstance as unknown as McpServer);
|
|
262
263
|
const handler = getRegisteredHandler();
|
|
263
|
-
const result = (await handler({
|
|
264
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
264
265
|
|
|
265
266
|
expect(result.isError).toBe(true);
|
|
266
267
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -283,7 +284,7 @@ describe("BAP Generate Tool", () => {
|
|
|
283
284
|
ServerRequest,
|
|
284
285
|
ServerNotification
|
|
285
286
|
>;
|
|
286
|
-
const result = (await handler({
|
|
287
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
287
288
|
|
|
288
289
|
expect(result.isError).toBe(true);
|
|
289
290
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -302,7 +303,7 @@ describe("BAP Generate Tool", () => {
|
|
|
302
303
|
ServerRequest,
|
|
303
304
|
ServerNotification
|
|
304
305
|
>;
|
|
305
|
-
const result = (await handler({
|
|
306
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
306
307
|
|
|
307
308
|
expect(result.isError).toBe(false);
|
|
308
309
|
expect(result.content?.[0]?.text).toContain(
|
|
@@ -333,7 +334,7 @@ describe("BAP Generate Tool", () => {
|
|
|
333
334
|
ServerRequest,
|
|
334
335
|
ServerNotification
|
|
335
336
|
>;
|
|
336
|
-
const result = (await handler({
|
|
337
|
+
const result = (await handler({}, mockExtra)) as CallToolResult;
|
|
337
338
|
|
|
338
339
|
expect(result.isError).toBe(false);
|
|
339
340
|
expect(result.content?.[0]?.text).toContain(
|
package/tools/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import type { OneSatServices } from "@1sat/wallet-remote";
|
|
1
3
|
import type { PrivateKey } from "@bsv/sdk";
|
|
2
4
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
5
|
import { registerA2bDiscoverTool } from "./a2b/discover";
|
|
@@ -41,6 +43,8 @@ export interface ToolsConfig {
|
|
|
41
43
|
wallet?: Wallet;
|
|
42
44
|
integratedWallet?: IntegratedWallet;
|
|
43
45
|
disableBroadcasting?: boolean; // For wallet tools
|
|
46
|
+
ctx?: OneSatContext;
|
|
47
|
+
services?: OneSatServices;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -81,7 +85,7 @@ export function registerAllTools(
|
|
|
81
85
|
|
|
82
86
|
// Register Ordinals-related tools
|
|
83
87
|
if (enableOrdinalsTools) {
|
|
84
|
-
registerOrdinalsTools(server);
|
|
88
|
+
registerOrdinalsTools(server, config.ctx);
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
// Register utility tools
|
|
@@ -126,9 +130,10 @@ export function registerAllTools(
|
|
|
126
130
|
} else if (config.wallet) {
|
|
127
131
|
// Register normal wallet tools
|
|
128
132
|
const walletToolOptions = {
|
|
129
|
-
disableBroadcasting: config.disableBroadcasting === true,
|
|
130
|
-
enableA2bTools: enableA2bTools,
|
|
133
|
+
disableBroadcasting: config.disableBroadcasting === true,
|
|
134
|
+
enableA2bTools: enableA2bTools,
|
|
131
135
|
identityPk: config.identityPk,
|
|
136
|
+
ctx: config.ctx,
|
|
132
137
|
};
|
|
133
138
|
registerWalletTools(server, config.wallet, walletToolOptions);
|
|
134
139
|
}
|
|
@@ -1,83 +1,33 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
// Schema for get inscription arguments
|
|
6
|
-
export const getInscriptionArgsSchema = z.object({
|
|
7
|
-
outpoint: z.string().describe("Outpoint in format 'txid_vout'"),
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export type GetInscriptionArgs = z.infer<typeof getInscriptionArgsSchema>;
|
|
11
|
-
|
|
12
|
-
// Inscription API response type
|
|
13
|
-
interface InscriptionResponse {
|
|
14
|
-
outpoint: string;
|
|
15
|
-
origin: {
|
|
16
|
-
outpoint: string;
|
|
17
|
-
data?: {
|
|
18
|
-
insc?: {
|
|
19
|
-
text?: string;
|
|
20
|
-
json?: unknown;
|
|
21
|
-
file?: {
|
|
22
|
-
hash?: string;
|
|
23
|
-
size?: number;
|
|
24
|
-
type?: string;
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
height?: number;
|
|
30
|
-
idx?: number;
|
|
31
|
-
satoshis?: number;
|
|
32
|
-
script?: string;
|
|
33
|
-
spend?: string;
|
|
34
|
-
[key: string]: unknown;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
5
|
/**
|
|
38
|
-
* Register the
|
|
6
|
+
* Register the inscription metadata lookup tool
|
|
39
7
|
*/
|
|
40
|
-
export function registerGetInscriptionTool(
|
|
8
|
+
export function registerGetInscriptionTool(
|
|
9
|
+
server: McpServer,
|
|
10
|
+
ctx?: OneSatContext,
|
|
11
|
+
): void {
|
|
41
12
|
server.tool(
|
|
42
13
|
"ordinals_getInscription",
|
|
43
|
-
"Retrieves
|
|
14
|
+
"Retrieves metadata for an inscription by its outpoint. Returns content type, file info, origin, MAP data, and sequence info.",
|
|
44
15
|
{
|
|
45
|
-
|
|
16
|
+
outpoint: z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("Outpoint in format 'txid_vout'"),
|
|
46
19
|
},
|
|
47
|
-
async (
|
|
48
|
-
{ outpoint },
|
|
49
|
-
extra: RequestHandlerExtra,
|
|
50
|
-
) => {
|
|
20
|
+
async ({ outpoint }) => {
|
|
51
21
|
try {
|
|
22
|
+
if (!ctx?.services) {
|
|
23
|
+
throw new Error("OneSat services not available");
|
|
24
|
+
}
|
|
52
25
|
|
|
53
|
-
// Validate outpoint format
|
|
54
26
|
if (!/^[0-9a-f]{64}_\d+$/i.test(outpoint)) {
|
|
55
27
|
throw new Error("Invalid outpoint format. Expected 'txid_vout'");
|
|
56
28
|
}
|
|
57
29
|
|
|
58
|
-
|
|
59
|
-
const response = await fetch(
|
|
60
|
-
`https://ordinals.gorillapool.io/api/inscriptions/${outpoint}`,
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
if (response.status === 404) {
|
|
64
|
-
return {
|
|
65
|
-
content: [
|
|
66
|
-
{
|
|
67
|
-
type: "text",
|
|
68
|
-
text: JSON.stringify({ error: "Inscription not found" }),
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!response.ok) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`API error: ${response.status} ${response.statusText}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const data = (await response.json()) as InscriptionResponse;
|
|
30
|
+
const data = await ctx.services.ordfs.getMetadata(outpoint);
|
|
81
31
|
|
|
82
32
|
return {
|
|
83
33
|
content: [
|
|
@@ -1,99 +1,33 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
// Schema for get token by ID or ticker arguments
|
|
6
|
-
export const getTokenByIdOrTickerArgsSchema = z
|
|
7
|
-
.object({
|
|
8
|
-
id: z
|
|
9
|
-
.string()
|
|
10
|
-
.optional()
|
|
11
|
-
.describe("BSV20 token ID in outpoint format (txid_vout)"),
|
|
12
|
-
tick: z.string().optional().describe("BSV20 token ticker symbol"),
|
|
13
|
-
})
|
|
14
|
-
.refine((data) => data.id || data.tick, {
|
|
15
|
-
message: "Either id or tick must be provided",
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export type GetTokenByIdOrTickerArgs = z.infer<
|
|
19
|
-
typeof getTokenByIdOrTickerArgsSchema
|
|
20
|
-
>;
|
|
21
|
-
|
|
22
|
-
// BSV20 token response type
|
|
23
|
-
interface TokenResponse {
|
|
24
|
-
id: string;
|
|
25
|
-
tick?: string;
|
|
26
|
-
sym?: string;
|
|
27
|
-
max?: string;
|
|
28
|
-
lim?: string;
|
|
29
|
-
dec?: number;
|
|
30
|
-
supply?: string;
|
|
31
|
-
amt?: string;
|
|
32
|
-
status?: number;
|
|
33
|
-
icon?: string;
|
|
34
|
-
height?: number;
|
|
35
|
-
[key: string]: unknown;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
5
|
/**
|
|
39
|
-
* Register the
|
|
6
|
+
* Register the BSV21 token lookup tool
|
|
40
7
|
*/
|
|
41
|
-
export function registerGetTokenByIdOrTickerTool(
|
|
8
|
+
export function registerGetTokenByIdOrTickerTool(
|
|
9
|
+
server: McpServer,
|
|
10
|
+
ctx?: OneSatContext,
|
|
11
|
+
): void {
|
|
42
12
|
server.tool(
|
|
43
13
|
"ordinals_getTokenByIdOrTicker",
|
|
44
|
-
"Retrieves detailed information about a
|
|
14
|
+
"Retrieves detailed information about a BSV21 token by its ID (txid_vout format). Returns token data including symbol, supply, decimals, funding status, and current state.",
|
|
45
15
|
{
|
|
46
16
|
id: z
|
|
47
17
|
.string()
|
|
48
|
-
.
|
|
49
|
-
.describe("BSV20 token ID in outpoint format (txid_vout)"),
|
|
50
|
-
tick: z.string().optional().describe("BSV20 token ticker symbol"),
|
|
18
|
+
.describe("BSV21 token ID in outpoint format (txid_vout)"),
|
|
51
19
|
},
|
|
52
|
-
async (
|
|
53
|
-
{ id, tick },
|
|
54
|
-
extra: RequestHandlerExtra,
|
|
55
|
-
) => {
|
|
20
|
+
async ({ id }) => {
|
|
56
21
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (!id && !tick) {
|
|
60
|
-
throw new Error("Either token ID or ticker symbol must be provided");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Validate ID format if provided
|
|
64
|
-
if (id && !/^[0-9a-f]{64}_\d+$/i.test(id)) {
|
|
65
|
-
throw new Error("Invalid BSV20 ID format. Expected 'txid_vout'");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Determine which endpoint to use based on provided parameters
|
|
69
|
-
let endpoint: string;
|
|
70
|
-
if (id) {
|
|
71
|
-
endpoint = `https://ordinals.gorillapool.io/api/bsv20/id/${id}`;
|
|
72
|
-
} else {
|
|
73
|
-
endpoint = `https://ordinals.gorillapool.io/api/bsv20/tick/${tick}`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Fetch BSV20 token data from GorillaPool API
|
|
77
|
-
const response = await fetch(endpoint);
|
|
78
|
-
|
|
79
|
-
if (response.status === 404) {
|
|
80
|
-
return {
|
|
81
|
-
content: [
|
|
82
|
-
{
|
|
83
|
-
type: "text",
|
|
84
|
-
text: JSON.stringify({ error: "BSV20 token not found" }),
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
};
|
|
22
|
+
if (!ctx?.services) {
|
|
23
|
+
throw new Error("OneSat services not available");
|
|
88
24
|
}
|
|
89
25
|
|
|
90
|
-
if (
|
|
91
|
-
throw new Error(
|
|
92
|
-
`API error: ${response.status} ${response.statusText}`,
|
|
93
|
-
);
|
|
26
|
+
if (!/^[0-9a-f]{64}_\d+$/i.test(id)) {
|
|
27
|
+
throw new Error("Invalid token ID format. Expected 'txid_vout'");
|
|
94
28
|
}
|
|
95
29
|
|
|
96
|
-
const data =
|
|
30
|
+
const data = await ctx.services.bsv21.getTokenDetails(id);
|
|
97
31
|
|
|
98
32
|
return {
|
|
99
33
|
content: [
|
package/tools/ordinals/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import { registerGetInscriptionTool } from "./getInscription";
|
|
3
4
|
import { registerGetTokenByIdOrTickerTool } from "./getTokenByIdOrTicker";
|
|
@@ -8,12 +9,12 @@ import { registerSearchInscriptionsTool } from "./searchInscriptions";
|
|
|
8
9
|
/**
|
|
9
10
|
* Register all Ordinals tools with the MCP server
|
|
10
11
|
* @param server The MCP server instance
|
|
12
|
+
* @param ctx OneSat context for SDK service access
|
|
11
13
|
*/
|
|
12
|
-
export function registerOrdinalsTools(server: McpServer): void {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
registerGetTokenByIdOrTickerTool(server);
|
|
14
|
+
export function registerOrdinalsTools(server: McpServer, ctx?: OneSatContext): void {
|
|
15
|
+
registerGetInscriptionTool(server, ctx);
|
|
16
|
+
registerSearchInscriptionsTool(server, ctx);
|
|
17
|
+
registerMarketListingsTool(server, ctx);
|
|
18
|
+
registerMarketSalesTool(server, ctx);
|
|
19
|
+
registerGetTokenByIdOrTickerTool(server, ctx);
|
|
19
20
|
}
|
|
@@ -1,197 +1,30 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
// Enhanced schema for unified market listings arguments
|
|
6
|
-
export const marketListingsArgsSchema = z.object({
|
|
7
|
-
// Common parameters
|
|
8
|
-
limit: z
|
|
9
|
-
.number()
|
|
10
|
-
.int()
|
|
11
|
-
.min(1)
|
|
12
|
-
.max(100)
|
|
13
|
-
.default(20)
|
|
14
|
-
.describe("Number of results (1-100, default 20)"),
|
|
15
|
-
offset: z.number().int().min(0).default(0).describe("Pagination offset"),
|
|
16
|
-
dir: z
|
|
17
|
-
.enum(["asc", "desc"])
|
|
18
|
-
.default("desc")
|
|
19
|
-
.describe("Sort direction (asc or desc)"),
|
|
20
|
-
address: z.string().optional().describe("Bitcoin address"),
|
|
21
|
-
|
|
22
|
-
// NFT-specific parameters
|
|
23
|
-
origin: z.string().optional().describe("Origin outpoint"),
|
|
24
|
-
mime: z.string().optional().describe("MIME type filter"),
|
|
25
|
-
num: z.string().optional().describe("Inscription number"),
|
|
26
|
-
|
|
27
|
-
// General market parameters
|
|
28
|
-
minPrice: z.number().optional().describe("Minimum price in satoshis"),
|
|
29
|
-
maxPrice: z.number().optional().describe("Maximum price in satoshis"),
|
|
30
|
-
|
|
31
|
-
// Token-specific parameters
|
|
32
|
-
tokenType: z
|
|
33
|
-
.enum(["nft", "bsv20", "bsv21", "all"])
|
|
34
|
-
.default("all")
|
|
35
|
-
.describe("Type of token to search for (nft, bsv20, bsv21, or all)"),
|
|
36
|
-
sort: z
|
|
37
|
-
.enum(["recent", "price", "num", "height", "price_per_token"])
|
|
38
|
-
.default("recent")
|
|
39
|
-
.describe("Sort method (recent, price, num, height, price_per_token)"),
|
|
40
|
-
id: z.string().optional().describe("Token ID in outpoint format"),
|
|
41
|
-
tick: z.string().optional().describe("Token ticker symbol"),
|
|
42
|
-
pending: z
|
|
43
|
-
.boolean()
|
|
44
|
-
.default(false)
|
|
45
|
-
.optional()
|
|
46
|
-
.describe("Include pending sales"),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export type MarketListingsArgs = z.infer<typeof marketListingsArgsSchema>;
|
|
50
|
-
|
|
51
|
-
// Unified response type that covers all token types
|
|
52
|
-
interface MarketListingResponse {
|
|
53
|
-
results: Array<{
|
|
54
|
-
outpoint: string;
|
|
55
|
-
origin?: {
|
|
56
|
-
outpoint: string;
|
|
57
|
-
data?: {
|
|
58
|
-
insc?: {
|
|
59
|
-
text?: string;
|
|
60
|
-
file?: {
|
|
61
|
-
type?: string;
|
|
62
|
-
size?: number;
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
data?: {
|
|
68
|
-
list?: {
|
|
69
|
-
price?: number;
|
|
70
|
-
payout?: string;
|
|
71
|
-
sale?: boolean;
|
|
72
|
-
price_per_token?: number;
|
|
73
|
-
};
|
|
74
|
-
bsv20?: {
|
|
75
|
-
id?: string;
|
|
76
|
-
tick?: string;
|
|
77
|
-
sym?: string;
|
|
78
|
-
amt?: string;
|
|
79
|
-
op?: string;
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
satoshis?: number;
|
|
83
|
-
height?: number;
|
|
84
|
-
[key: string]: unknown;
|
|
85
|
-
}>;
|
|
86
|
-
total: number;
|
|
87
|
-
}
|
|
88
3
|
|
|
89
4
|
/**
|
|
90
|
-
* Register the
|
|
91
|
-
* Handles NFTs, BSV20, and BSV21 tokens
|
|
5
|
+
* Register the market listings tool (stub — awaiting 1sat-stack implementation)
|
|
92
6
|
*/
|
|
93
|
-
export function registerMarketListingsTool(
|
|
7
|
+
export function registerMarketListingsTool(
|
|
8
|
+
server: McpServer,
|
|
9
|
+
ctx?: OneSatContext,
|
|
10
|
+
): void {
|
|
94
11
|
server.tool(
|
|
95
12
|
"ordinals_marketListings",
|
|
96
|
-
"
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
baseUrl += "/bsv20/market";
|
|
112
|
-
useTokenParams = true;
|
|
113
|
-
} else if (tokenType === "nft" || tokenType === "all") {
|
|
114
|
-
baseUrl += "/market";
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Build the URL with query parameters
|
|
118
|
-
const url = new URL(baseUrl);
|
|
119
|
-
url.searchParams.append("limit", limit.toString());
|
|
120
|
-
url.searchParams.append("offset", offset.toString());
|
|
121
|
-
url.searchParams.append("dir", dir);
|
|
122
|
-
|
|
123
|
-
// Add sort parameter based on token type
|
|
124
|
-
if (useTokenParams) {
|
|
125
|
-
// BSV20/BSV21 specific sort options
|
|
126
|
-
if (
|
|
127
|
-
sort === "height" ||
|
|
128
|
-
sort === "price" ||
|
|
129
|
-
sort === "price_per_token"
|
|
130
|
-
) {
|
|
131
|
-
url.searchParams.append("sort", sort);
|
|
132
|
-
}
|
|
133
|
-
// Add token-specific parameters
|
|
134
|
-
if (tokenType === "bsv21") {
|
|
135
|
-
url.searchParams.append("type", "v2");
|
|
136
|
-
}
|
|
137
|
-
if (id) url.searchParams.append("id", id);
|
|
138
|
-
if (tick) url.searchParams.append("tick", tick);
|
|
139
|
-
if (pending !== undefined)
|
|
140
|
-
url.searchParams.append("pending", pending.toString());
|
|
141
|
-
// For BSV20/21, min/max price parameters have slightly different names
|
|
142
|
-
if (minPrice !== undefined)
|
|
143
|
-
url.searchParams.append("min_price", minPrice.toString());
|
|
144
|
-
if (maxPrice !== undefined)
|
|
145
|
-
url.searchParams.append("max_price", maxPrice.toString());
|
|
146
|
-
} else {
|
|
147
|
-
// NFT specific sort options
|
|
148
|
-
if (sort === "recent" || sort === "price" || sort === "num") {
|
|
149
|
-
url.searchParams.append("sort", sort);
|
|
150
|
-
}
|
|
151
|
-
// Add NFT-specific parameters
|
|
152
|
-
if (origin) url.searchParams.append("origin", origin);
|
|
153
|
-
if (mime) url.searchParams.append("mime", mime);
|
|
154
|
-
if (num) url.searchParams.append("num", num);
|
|
155
|
-
// For NFTs, min/max price parameters use short names
|
|
156
|
-
if (minPrice !== undefined)
|
|
157
|
-
url.searchParams.append("min", minPrice.toString());
|
|
158
|
-
if (maxPrice !== undefined)
|
|
159
|
-
url.searchParams.append("max", maxPrice.toString());
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Add common parameters
|
|
163
|
-
if (address) url.searchParams.append("address", address);
|
|
164
|
-
|
|
165
|
-
// Make the fetch request
|
|
166
|
-
const response = await fetch(url.toString());
|
|
167
|
-
|
|
168
|
-
if (!response.ok) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`API error: ${response.status} ${response.statusText}`,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const data = (await response.json()) as MarketListingResponse;
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
content: [
|
|
178
|
-
{
|
|
179
|
-
type: "text",
|
|
180
|
-
text: JSON.stringify(data, null, 2),
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
};
|
|
184
|
-
} catch (error) {
|
|
185
|
-
return {
|
|
186
|
-
content: [
|
|
187
|
-
{
|
|
188
|
-
type: "text",
|
|
189
|
-
text: error instanceof Error ? error.message : String(error),
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
isError: true,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
13
|
+
"Query marketplace listings for ordinals and tokens. Currently awaiting 1sat-stack marketplace API implementation.",
|
|
14
|
+
{},
|
|
15
|
+
async () => {
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: JSON.stringify({
|
|
21
|
+
error: "Marketplace listings API not yet available on 1sat-stack",
|
|
22
|
+
status: "not_implemented",
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
195
28
|
},
|
|
196
29
|
);
|
|
197
30
|
}
|