diff --git a/nostr_dvm/backends/mcp/connection_check.py b/nostr_dvm/backends/mcp/connection_check.py index bc543f3..57ade18 100644 --- a/nostr_dvm/backends/mcp/connection_check.py +++ b/nostr_dvm/backends/mcp/connection_check.py @@ -5,6 +5,7 @@ from config import load_config from messages.send_initialize_message import send_initialize from messages.send_ping import send_ping from messages.send_tools_list import send_tools_list +from messages.send_call_tool import send_call_tool from transport.stdio.stdio_client import stdio_client # Configure logging @@ -18,7 +19,7 @@ async def main(): """Stripped-down script to initialize the server and send a ping.""" # Configuration values config_path = "server_config.json" - server_name = "Echo" + server_name = "nostrdvmmcp" # Load server configuration server_params = await load_config(config_path, server_name) @@ -44,6 +45,10 @@ async def main(): result = await send_tools_list(read_stream, write_stream) print(result) + result = await send_call_tool("get-crypto-price", {"symbol":"BTC"}, read_stream, write_stream) + + print(result) + # Run the script if __name__ == "__main__": anyio.run(main) diff --git a/nostr_dvm/backends/mcp/server_config.json b/nostr_dvm/backends/mcp/server_config.json index e11cf48..3a86392 100644 --- a/nostr_dvm/backends/mcp/server_config.json +++ b/nostr_dvm/backends/mcp/server_config.json @@ -1,19 +1,8 @@ { "mcpServers": { - "sqlite": { - "command": "uvx", - "args": ["mcp-server-sqlite", "--db-path", "test.db"] - }, - "Echo": { - "command": "uv", - "args": [ - "run", - "--with", - "mcp[cli]", - "mcp", - "run", - "~/Documents/GitHub/nostrdvm/tests/mcp_server.py" - ] + "nostrdvmmcp": { + "command": "node", + "args": ["../../../tests/mcp/nostr_dvmcp_server.js"] } } } diff --git a/nostr_dvm/tasks/mcpbridge.py b/nostr_dvm/tasks/mcpbridge.py index d8aade9..504c66c 100644 --- a/nostr_dvm/tasks/mcpbridge.py +++ b/nostr_dvm/tasks/mcpbridge.py @@ -77,8 +77,7 @@ class MCPBridge(DVMTaskInterface): print(c) content = event.content() - print(c) - print(content) + options = { "command" : c, @@ -112,9 +111,8 @@ class MCPBridge(DVMTaskInterface): return json.dumps(final_tools) - else: + elif options["command"] == "execute-tool": - print(options["payload"]) ob = json.loads(options["payload"]) tool_name = ob["name"] @@ -162,7 +160,6 @@ class MCPBridge(DVMTaskInterface): except BaseException as e: pass - print("Ignore the error. We're good.") return alltools diff --git a/tests/mcp/mcp_server_config.json b/tests/mcp/mcp_server_config.json index defc76e..e540705 100644 --- a/tests/mcp/mcp_server_config.json +++ b/tests/mcp/mcp_server_config.json @@ -9,17 +9,6 @@ "args": [ "../../mcp-crypto-price/build/index.js" ] - }, - "Echo": { - "command": "uv", - "args": [ - "run", - "--with", - "mcp[cli]", - "mcp", - "run", - "tests/mcp/mcp_server.py" - ] } } } diff --git a/tests/mcp/mcp_test.py b/tests/mcp/mcp_test.py index 933ada5..053bef9 100644 --- a/tests/mcp/mcp_test.py +++ b/tests/mcp/mcp_test.py @@ -35,7 +35,7 @@ def playground(announce=False): # MCP CONFIG config_path = str(Path.absolute(Path(__file__).parent / "mcp_server_config.json")) - server_names = ["Echo", "mcp-crypto-price"] + server_names = ["mcp-crypto-price"] tools = asyncio.run(get_tools(config_path, server_names)) @@ -74,7 +74,8 @@ def playground(announce=False): capabilities_tag = Tag.parse(["capabilities", "mcp-1.0"]) t1_tag = Tag.parse(["t","mcp"]) t2_tag = Tag.parse(["t", "bitcoin price"]) - nip89config.EXTRA_TAGS =[capabilities_tag, t1_tag, t2_tag] + t3_tag = Tag.parse(["t", "bitcoin analysis"]) + nip89config.EXTRA_TAGS =[capabilities_tag, t1_tag, t2_tag, t3_tag] options = { diff --git a/tests/mcp/nostr_dvmcp_server.js b/tests/mcp/nostr_dvmcp_server.js index fa4b1d8..e61c000 100644 --- a/tests/mcp/nostr_dvmcp_server.js +++ b/tests/mcp/nostr_dvmcp_server.js @@ -62,6 +62,39 @@ const server = new McpServer(config); await getnip89s() +function convertSchemaDefinitionToZod(schemaDefinition) { + const zodSchema = {}; + + for (const key in schemaDefinition) { + const property = schemaDefinition[key]; + + let zodProperty; + switch (property.type) { + case 'string': + zodProperty = z.string(); + break; + case 'number': + zodProperty = z.number(); + if (property.min !== undefined) zodProperty = zodProperty.min(property.min); + if (property.max !== undefined) zodProperty = zodProperty.max(property.max); + break; + case 'boolean': + zodProperty = z.boolean(); + break; + default: + zodProperty = z.any(); // Fallback for unknown types + } + + if (property.description) { + zodProperty = zodProperty.describe(property.description); + } + + zodSchema[key] = zodProperty; + } + + return z.object(zodSchema); +} + //define getNip89s. Fetch from Nostr and add as tools to server. async function getnip89s() { await loadWasmAsync(); @@ -90,12 +123,9 @@ async function getnip89s() { if (tool.inputSchema === undefined || tool.inputSchema.properties === undefined) { continue } - - //TODO convert inputSchema.properties? to zod schema or to any other way so it works. - let inputSchema = {symbol: z.string()} - - server.tool(tool.name, tool.description, inputSchema, - async (args) => { + const zodSchema = convertSchemaDefinitionToZod(tool.inputSchema.properties); + server.tool(tool.name, tool.description, zodSchema.shape, + async (args) => { return await handle_dvm_request(args, tool.name, pubkey) }); } @@ -123,7 +153,9 @@ async function handle_dvm_request(args, name, pubkey) { await client.addRelay(relay); } await client.connect(); - var relays_list = merge(["relays"], relays) + + const relays_list = ["relays"].concat(relays) + let tags = [ Tag.parse(["c", "execute-tool"]), @@ -145,15 +177,25 @@ async function handle_dvm_request(args, name, pubkey) { var result = "" const handle = { - handleEvent: async (relayUrl, subscriptionId, event) => { - //TODO More logic / safety checks - result = JSON.parse(event.content).content[0].text - abortable.abort() - return true + handleEvent: async (relayUrl, subscriptionId, event) => + { + if(event.kind.asU16() === 6910){ + try { + result = JSON.parse(event.content).content[0].text + } + catch { + console.log("Error parsing JSON in event") + } + + abortable.abort() + return true + } + }, - handleMsg: async (relayUrl, message) => { - //console.log("Received message from", relayUrl, message.asJson()); + handleMsg: async (relayUrl, message) => + { + console.log("Received message from", relayUrl, message.asJson()); } };