bsv-mcp 0.0.9 → 0.0.10
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/package.json +25 -2
- package/Dockerfile +0 -20
- package/biome.json +0 -30
- package/tests/integration/wallet-server.test.ts +0 -216
- package/tsconfig.json +0 -28
package/package.json
CHANGED
|
@@ -2,7 +2,31 @@
|
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.10",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "satchmo",
|
|
8
|
+
"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/b-open-io/bsv-mcp"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"bitcoin",
|
|
15
|
+
"bsv",
|
|
16
|
+
"bitcoin-sv",
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"wallet",
|
|
20
|
+
"ordinals",
|
|
21
|
+
"blockchain"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"index.ts",
|
|
25
|
+
"tools/**/*.ts",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md",
|
|
28
|
+
"smithery.yaml"
|
|
29
|
+
],
|
|
6
30
|
"bin": {
|
|
7
31
|
"bsv-mcp": "./index.ts"
|
|
8
32
|
},
|
|
@@ -20,7 +44,6 @@
|
|
|
20
44
|
"@types/node": "^22.14.1",
|
|
21
45
|
"js-1sat-ord": "^0.1.80",
|
|
22
46
|
"satoshi-token": "^0.0.4",
|
|
23
|
-
"tsx": "^4.19.3",
|
|
24
47
|
"zod": "^3.24.2"
|
|
25
48
|
},
|
|
26
49
|
"scripts": {
|
package/Dockerfile
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Use the official Bun image
|
|
2
|
-
FROM oven/bun:1
|
|
3
|
-
|
|
4
|
-
# Set working directory
|
|
5
|
-
WORKDIR /app
|
|
6
|
-
|
|
7
|
-
# Copy all application code first
|
|
8
|
-
COPY . .
|
|
9
|
-
|
|
10
|
-
# Install dependencies
|
|
11
|
-
RUN bun install --frozen-lockfile
|
|
12
|
-
|
|
13
|
-
# Set user for security
|
|
14
|
-
USER bun
|
|
15
|
-
|
|
16
|
-
# Expose port (if needed)
|
|
17
|
-
EXPOSE 3000
|
|
18
|
-
|
|
19
|
-
# Run the application
|
|
20
|
-
CMD ["bun", "run", "index.ts"]
|
package/biome.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
-
"vcs": {
|
|
4
|
-
"enabled": false,
|
|
5
|
-
"clientKind": "git",
|
|
6
|
-
"useIgnoreFile": false
|
|
7
|
-
},
|
|
8
|
-
"files": {
|
|
9
|
-
"ignoreUnknown": false,
|
|
10
|
-
"ignore": []
|
|
11
|
-
},
|
|
12
|
-
"formatter": {
|
|
13
|
-
"enabled": true,
|
|
14
|
-
"indentStyle": "tab"
|
|
15
|
-
},
|
|
16
|
-
"organizeImports": {
|
|
17
|
-
"enabled": true
|
|
18
|
-
},
|
|
19
|
-
"linter": {
|
|
20
|
-
"enabled": true,
|
|
21
|
-
"rules": {
|
|
22
|
-
"recommended": true
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"javascript": {
|
|
26
|
-
"formatter": {
|
|
27
|
-
"quoteStyle": "double"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import { PrivateKey } from "@bsv/sdk";
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { registerWalletTools } from "../../tools/wallet/tools";
|
|
6
|
-
import { Wallet } from "../../tools/wallet/wallet";
|
|
7
|
-
|
|
8
|
-
// List of expected wallet tool names
|
|
9
|
-
const EXPECTED_WALLET_TOOLS = [
|
|
10
|
-
"wallet_getPublicKey",
|
|
11
|
-
"wallet_createSignature",
|
|
12
|
-
"wallet_verifySignature",
|
|
13
|
-
"wallet_encrypt",
|
|
14
|
-
"wallet_decrypt",
|
|
15
|
-
"wallet_createAction",
|
|
16
|
-
"wallet_signAction",
|
|
17
|
-
"wallet_listActions",
|
|
18
|
-
"wallet_listOutputs",
|
|
19
|
-
"wallet_getNetwork",
|
|
20
|
-
"wallet_getVersion",
|
|
21
|
-
"wallet_revealCounterpartyKeyLinkage",
|
|
22
|
-
"wallet_revealSpecificKeyLinkage",
|
|
23
|
-
"wallet_createHmac",
|
|
24
|
-
"wallet_verifyHmac",
|
|
25
|
-
"wallet_abortAction",
|
|
26
|
-
"wallet_internalizeAction",
|
|
27
|
-
"wallet_relinquishOutput",
|
|
28
|
-
"wallet_acquireCertificate",
|
|
29
|
-
"wallet_listCertificates",
|
|
30
|
-
"wallet_proveCertificate",
|
|
31
|
-
"wallet_relinquishCertificate",
|
|
32
|
-
"wallet_discoverByIdentityKey",
|
|
33
|
-
"wallet_discoverByAttributes",
|
|
34
|
-
"wallet_isAuthenticated",
|
|
35
|
-
"wallet_waitForAuthentication",
|
|
36
|
-
"wallet_getHeaderForHeight",
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
// Mock wallet with test responses
|
|
40
|
-
class TestWallet extends Wallet {
|
|
41
|
-
constructor() {
|
|
42
|
-
super(PrivateKey.fromRandom());
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Override with test implementations that actually return something
|
|
46
|
-
async getPublicKey() {
|
|
47
|
-
return { publicKey: "mockPublicKey123" };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Note: other methods will still return 'not implemented' errors
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test("MCP server registers all expected wallet tools", async () => {
|
|
54
|
-
// Create an MCP server
|
|
55
|
-
const server = new McpServer({
|
|
56
|
-
name: "Wallet Server Test",
|
|
57
|
-
version: "0.0.1",
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Create test wallet and register tools
|
|
61
|
-
const wallet = new TestWallet();
|
|
62
|
-
const handlers = registerWalletTools(server, wallet);
|
|
63
|
-
|
|
64
|
-
// Check that we have all expected tool handlers
|
|
65
|
-
for (const toolName of EXPECTED_WALLET_TOOLS) {
|
|
66
|
-
expect(handlers[toolName as keyof typeof handlers]).toBeDefined();
|
|
67
|
-
expect(typeof handlers[toolName as keyof typeof handlers]).toBe("function");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Check that we don't have any unexpected handlers
|
|
71
|
-
const actualToolNames = Object.keys(handlers);
|
|
72
|
-
expect(actualToolNames.length).toBe(EXPECTED_WALLET_TOOLS.length);
|
|
73
|
-
|
|
74
|
-
for (const toolName of actualToolNames) {
|
|
75
|
-
expect(EXPECTED_WALLET_TOOLS).toContain(toolName);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("MCP server with wallet tools", async () => {
|
|
80
|
-
// Create an MCP server
|
|
81
|
-
const server = new McpServer({
|
|
82
|
-
name: "Wallet Server Test",
|
|
83
|
-
version: "0.0.1",
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Create test wallet and register tools
|
|
87
|
-
const wallet = new TestWallet();
|
|
88
|
-
const handlers = registerWalletTools(server, wallet);
|
|
89
|
-
|
|
90
|
-
// Mock the request handler extra
|
|
91
|
-
const mockExtra = {
|
|
92
|
-
signal: new AbortController().signal,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// Test the overridden getPublicKey handler which should return data
|
|
96
|
-
const getPublicKeyHandler = handlers.wallet_getPublicKey;
|
|
97
|
-
const getPublicKeyResult = await getPublicKeyHandler({ args: {} }, mockExtra);
|
|
98
|
-
|
|
99
|
-
// Check the success result based on the actual structure
|
|
100
|
-
expect(getPublicKeyResult.content).toBeDefined();
|
|
101
|
-
expect(getPublicKeyResult.content?.[0]?.type).toBe("text");
|
|
102
|
-
|
|
103
|
-
const publicKeyContent = getPublicKeyResult.content?.[0]?.text;
|
|
104
|
-
if (typeof publicKeyContent === "string") {
|
|
105
|
-
expect(JSON.parse(publicKeyContent)).toEqual({
|
|
106
|
-
publicKey: "mockPublicKey123",
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// For a more complete integration test, we could create a real HTTP server
|
|
112
|
-
// and use fetch to make requests to it
|
|
113
|
-
test.skip("MCP server with HTTP requests", async () => {
|
|
114
|
-
// Create MCP server
|
|
115
|
-
const server = new McpServer({
|
|
116
|
-
name: "Wallet Server Test",
|
|
117
|
-
version: "0.0.1",
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Register wallet tools
|
|
121
|
-
const wallet = new TestWallet();
|
|
122
|
-
const handlers = registerWalletTools(server, wallet);
|
|
123
|
-
|
|
124
|
-
// Define expected request type
|
|
125
|
-
type ToolCallRequest = {
|
|
126
|
-
type: string;
|
|
127
|
-
name: string;
|
|
128
|
-
args?: Record<string, unknown>;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Create a simple HTTP server
|
|
132
|
-
const httpServer = Bun.serve({
|
|
133
|
-
port: 0, // Use random available port
|
|
134
|
-
async fetch(req) {
|
|
135
|
-
if (req.method === "POST") {
|
|
136
|
-
const bodyText = await req.text();
|
|
137
|
-
let body: unknown;
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
body = JSON.parse(bodyText);
|
|
141
|
-
} catch (err) {
|
|
142
|
-
return new Response("Invalid JSON", { status: 400 });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Type guard for the request body
|
|
146
|
-
const isToolCallRequest = (obj: unknown): obj is ToolCallRequest => {
|
|
147
|
-
return (
|
|
148
|
-
typeof obj === "object" &&
|
|
149
|
-
obj !== null &&
|
|
150
|
-
"type" in obj &&
|
|
151
|
-
"name" in obj &&
|
|
152
|
-
typeof (obj as ToolCallRequest).type === "string" &&
|
|
153
|
-
typeof (obj as ToolCallRequest).name === "string"
|
|
154
|
-
);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Handle MCP requests by calling the appropriate tool
|
|
158
|
-
if (
|
|
159
|
-
isToolCallRequest(body) &&
|
|
160
|
-
body.type === "tool_call" &&
|
|
161
|
-
body.name.startsWith("wallet_")
|
|
162
|
-
) {
|
|
163
|
-
// Get handler from our handlers object
|
|
164
|
-
const toolName = body.name as keyof typeof handlers;
|
|
165
|
-
const handler = handlers[toolName];
|
|
166
|
-
if (handler) {
|
|
167
|
-
const result = await handler(
|
|
168
|
-
{ args: body.args || {} },
|
|
169
|
-
{
|
|
170
|
-
signal: new AbortController().signal,
|
|
171
|
-
},
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
return new Response(JSON.stringify(result), {
|
|
175
|
-
headers: { "Content-Type": "application/json" },
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return new Response("Not found", { status: 404 });
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const port = httpServer.port;
|
|
187
|
-
const baseUrl = `http://localhost:${port}`;
|
|
188
|
-
|
|
189
|
-
// Test calling the getPublicKey tool
|
|
190
|
-
const response = await fetch(`${baseUrl}`, {
|
|
191
|
-
method: "POST",
|
|
192
|
-
headers: { "Content-Type": "application/json" },
|
|
193
|
-
body: JSON.stringify({
|
|
194
|
-
type: "tool_call",
|
|
195
|
-
name: "wallet_getPublicKey",
|
|
196
|
-
args: {},
|
|
197
|
-
}),
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
const resultData = (await response.json()) as {
|
|
201
|
-
isError?: boolean;
|
|
202
|
-
content?: Array<{ type: string; text: string }>;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
expect(resultData.content).toBeDefined();
|
|
206
|
-
expect(resultData.content?.[0]?.type).toBe("text");
|
|
207
|
-
|
|
208
|
-
if (resultData.content?.[0]?.text) {
|
|
209
|
-
const parsedContent = JSON.parse(resultData.content[0].text);
|
|
210
|
-
expect(parsedContent).toEqual({ publicKey: "mockPublicKey123" });
|
|
211
|
-
}
|
|
212
|
-
} finally {
|
|
213
|
-
// Shutdown the server
|
|
214
|
-
httpServer.stop();
|
|
215
|
-
}
|
|
216
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
|
-
"lib": ["ESNext"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
|
|
11
|
-
// Bundler mode
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
// Best practices
|
|
18
|
-
"strict": true,
|
|
19
|
-
"skipLibCheck": true,
|
|
20
|
-
"noFallthroughCasesInSwitch": true,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
|
|
23
|
-
// Some stricter flags (disabled by default)
|
|
24
|
-
"noUnusedLocals": false,
|
|
25
|
-
"noUnusedParameters": false,
|
|
26
|
-
"noPropertyAccessFromIndexSignature": false
|
|
27
|
-
}
|
|
28
|
-
}
|