From 4520cef6708b7ac5400b122f8ed4dcbdc2ba80dc Mon Sep 17 00:00:00 2001 From: dbth <1097224+believethehype@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:00:40 +0100 Subject: [PATCH] bump --- nostr_dvm/utils/nip89_utils.py | 7 ++++++ setup.py | 4 +-- tests/mcp_dvm_client.py | 32 ++++++++++++++++++------ tests/mcp_server_nostr.py | 45 ++++++++++++++++++++++++++++++++++ tests/mcp_test.py | 5 +++- 5 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 tests/mcp_server_nostr.py diff --git a/nostr_dvm/utils/nip89_utils.py b/nostr_dvm/utils/nip89_utils.py index ec7ffea..5413707 100644 --- a/nostr_dvm/utils/nip89_utils.py +++ b/nostr_dvm/utils/nip89_utils.py @@ -103,6 +103,13 @@ async def nip89_fetch_all_dvms(client): for event in events.to_vec(): print(event.as_json()) +async def nip89_fetch_all_dvms_by_kind(client, kind): + ktags = [str(kind)] + filter = Filter().kind(EventDefinitions.KIND_ANNOUNCEMENT).custom_tags(SingleLetterTag.lowercase(Alphabet.K), ktags) + events = await client.fetch_events(filter, relay_timeout) + return events.to_vec() + + async def nip89_fetch_events_pubkey(client, pubkey, kind): ktags = [str(kind.as_u16())] diff --git a/setup.py b/setup.py index d59396a..632ba23 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -VERSION = '1.1.0' +VERSION = '1.1.1' DESCRIPTION = 'A framework to build and run Nostr NIP90 Data Vending Machines' LONG_DESCRIPTION = ('A framework to build and run Nostr NIP90 Data Vending Machines. See the github repository for more information') @@ -25,7 +25,7 @@ setup( "PyUpload==0.1.4", "pandas==2.2.2", "requests==2.32.3", - "moviepy==2.0.0.dev2", + "moviepy==2.0.0", "zipp==3.19.1", "urllib3==2.2.2", "networkx==3.3", diff --git a/tests/mcp_dvm_client.py b/tests/mcp_dvm_client.py index d4357b1..83ea1fb 100644 --- a/tests/mcp_dvm_client.py +++ b/tests/mcp_dvm_client.py @@ -7,21 +7,23 @@ from nostr_sdk import Keys, Client, Tag, EventBuilder, Filter, HandleNotificatio nip44_encrypt, Nip44Version, NostrSigner, Event, Kind, init_logger, LogLevel from nostr_dvm.utils.definitions import EventDefinitions +from nostr_dvm.utils.nip89_utils import nip89_fetch_all_dvms_by_kind from nostr_dvm.utils.nostr_utils import send_event, check_and_set_private_key relay_list = ["wss://nostr.oxtr.dev", "wss://relay.nostrdvm.com"] -async def nostr_client_test_mcp_get_tools(): +async def nostr_client_test_mcp_get_tools(dvm_pubkey): keys = Keys.parse(check_and_set_private_key("test_client")) outTag = Tag.parse(["output", "application/json"]) cTag = Tag.parse(["c", "list-tools"]) alttag = Tag.parse(["alt", "This is a NIP90 Request to contact MCP server"]) relaysTag = Tag.parse(['relays'] + relay_list) + ptag = Tag.parse(["p", dvm_pubkey]) event = EventBuilder(EventDefinitions.KIND_NIP90_MCP, str("MCP request")).tags( - [outTag, alttag, cTag, relaysTag]).sign_with_keys(keys) + [ptag, outTag, alttag, cTag, relaysTag]).sign_with_keys(keys) client = Client(NostrSigner.keys(keys)) @@ -36,20 +38,21 @@ async def nostr_client_test_mcp_get_tools(): return result -async def nostr_client_test_mcp_execute_tool(tool_name, tool_parameters): +async def nostr_client_test_mcp_execute_tool(tool_name, tool_parameters, dvm_pubkey): keys = Keys.parse(check_and_set_private_key("test_client")) outTag = Tag.parse(["output", "application/json"]) cTag = Tag.parse(["c", "execute-tool"]) alttag = Tag.parse(["alt", "This is a NIP90 Request to contact MCP server"]) relaysTag = Tag.parse(['relays'] + relay_list) + ptag = Tag.parse(["p", dvm_pubkey]) payload = {"name": tool_name, "parameters": tool_parameters } event = EventBuilder(EventDefinitions.KIND_NIP90_MCP, json.dumps(payload)).tags( - [outTag, alttag, cTag, relaysTag]).sign_with_keys(keys) + [ptag, outTag, alttag, cTag, relaysTag]).sign_with_keys(keys) client = Client(NostrSigner.keys(keys)) @@ -89,9 +92,22 @@ async def nostr_client(): mcp_filter = Filter().pubkey(pk).kind(Kind(6910)).limit(0) await client.subscribe(mcp_filter, None) - #await nostr_client_test_mcp_get_tools() - await nostr_client_test_mcp_execute_tool(tool_name="get-crypto-price", tool_parameters={"symbol": "BTC"}) - #await nostr_client_test_mcp_execute_tool(tool_name="echo_tool", tool_parameters={"message": "Hello"}) + print("Existing MCP DVMS:") + nip89s = await nip89_fetch_all_dvms_by_kind(client, 5910) + for announcement in nip89s: + print(announcement.as_json()) + + + + dvm_pubkey = "12e76a4504c09f0b4b02d8c7497525136c18520b23b9a035c998d23c817f381d" + + #await nostr_client_test_mcp_get_tools(dvm_pubkey=dvm_pubkey) + + #await nostr_client_test_mcp_execute_tool(tool_name="get-crypto-price", tool_parameters={"symbol": "BTC"}, dvm_pubkey=dvm_pubkey) + #await nostr_client_test_mcp_execute_tool(tool_name="echo_tool", tool_parameters={"message": "Hello"}, dvm_pubkey=dvm_pubkey) + + #await nostr_client_test_mcp_get_tools(dvm_pubkey=dvm_pubkey) + await nostr_client_test_mcp_execute_tool(tool_name="extract", tool_parameters={"url": "https://en.wikipedia.org/wiki/Nostr"}, dvm_pubkey=dvm_pubkey) class NotificationHandler(HandleNotification): @@ -99,7 +115,7 @@ async def nostr_client(): print(f"Received new event from {relay_url}: {event.as_json()}") if event.kind().as_u16() == 6910: - print(event.content()) + print(event.content() + "\n") diff --git a/tests/mcp_server_nostr.py b/tests/mcp_server_nostr.py new file mode 100644 index 0000000..4c1bde9 --- /dev/null +++ b/tests/mcp_server_nostr.py @@ -0,0 +1,45 @@ +import json + +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("Nostr", dependencies=["nostr_dvm==1.1.0"]) + +@mcp.resource("echo://{message}") +def echo_resource(message: str) -> str: + """Echo a message as a resource""" + return f"Resource echo: {message}" + + + +@mcp.tool() +async def get_mcp_dvm_tool() -> str: + """Fetch Tools from Data Vending Machines""" + + from nostr_sdk import Keys, NostrSigner, Client + from nostr_dvm.utils.nip89_utils import nip89_fetch_all_dvms_by_kind + from nostr_dvm.utils.nostr_utils import check_and_set_private_key + + keys = Keys.parse(check_and_set_private_key("test_client")) + + signer = NostrSigner.keys(keys) + client = Client(signer) + + await client.add_relay("wss://relay.damus.io") + await client.add_relay("wss://nostr.mom") + await client.add_relay("wss://nostr.oxtr.dev") + await client.add_relay("wss://relay.nostrdvm.com") + await client.connect() + nip89s = await nip89_fetch_all_dvms_by_kind(client, 5910) + tools = [] + for announcement in nip89s: + print(announcement.as_json()["tools"]) + tools.append(announcement.as_json()["tools"]) + + return str(tools) + + + +@mcp.prompt() +def echo_prompt(message: str) -> str: + """Create an echo prompt""" + return f"Please process this message: {message}" \ No newline at end of file diff --git a/tests/mcp_test.py b/tests/mcp_test.py index 942ea63..e8cdb1d 100644 --- a/tests/mcp_test.py +++ b/tests/mcp_test.py @@ -16,6 +16,9 @@ async def get_tools(config_path, server_names): tools = await MCPBridge.list_tools(config_path, server_names) return tools + + + def playground(announce=False): framework = DVMFramework() @@ -28,7 +31,7 @@ def playground(announce=False): name = "MCP Test DVM" identifier = "mcp_test" # Chose a unique identifier in order to get a lnaddress dvm_config = build_default_config(identifier) - + dvm_config.DELETE_ANNOUNCEMENT_ON_SHUTDOWN = True # MCP CONFIG config_path = str(Path.absolute(Path(__file__).parent / "mcp_server_config.json"))