|
| 1 | +// This file is Copyright its original authors, visible in version control |
| 2 | +// history. |
| 3 | +// |
| 4 | +// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE |
| 5 | +// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 6 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. |
| 7 | +// You may not use this file except in accordance with one or both of these |
| 8 | +// licenses. |
| 9 | + |
| 10 | +use e2e_tests::{LdkServerHandle, McpHandle, TestBitcoind}; |
| 11 | +use ldk_server_client::ldk_server_grpc::api::Bolt11ReceiveRequest; |
| 12 | +use ldk_server_client::ldk_server_grpc::types::{ |
| 13 | + bolt11_invoice_description, Bolt11InvoiceDescription, |
| 14 | +}; |
| 15 | +use serde_json::json; |
| 16 | + |
| 17 | +#[tokio::test] |
| 18 | +async fn test_mcp_initialize_and_list_tools() { |
| 19 | + let bitcoind = TestBitcoind::new(); |
| 20 | + let server = LdkServerHandle::start(&bitcoind).await; |
| 21 | + let mut mcp = McpHandle::start(&server); |
| 22 | + |
| 23 | + let initialize = mcp.call( |
| 24 | + 1, |
| 25 | + "initialize", |
| 26 | + json!({ |
| 27 | + "protocolVersion": "2025-11-25", |
| 28 | + "capabilities": {}, |
| 29 | + "clientInfo": {"name": "e2e-test", "version": "0.1"} |
| 30 | + }), |
| 31 | + ); |
| 32 | + assert_eq!(initialize["result"]["protocolVersion"], "2025-11-25"); |
| 33 | + assert!(initialize["result"]["capabilities"]["tools"].is_object()); |
| 34 | + |
| 35 | + let tools = mcp.call(2, "tools/list", json!({})); |
| 36 | + let tool_names = tools["result"]["tools"].as_array().unwrap(); |
| 37 | + assert!(tool_names.iter().any(|tool| tool["name"] == "get_node_info")); |
| 38 | + assert!(tool_names.iter().any(|tool| tool["name"] == "onchain_receive")); |
| 39 | + assert!(tool_names.iter().any(|tool| tool["name"] == "decode_invoice")); |
| 40 | +} |
| 41 | + |
| 42 | +#[tokio::test] |
| 43 | +async fn test_mcp_live_tool_calls() { |
| 44 | + let bitcoind = TestBitcoind::new(); |
| 45 | + let server = LdkServerHandle::start(&bitcoind).await; |
| 46 | + let mut mcp = McpHandle::start(&server); |
| 47 | + |
| 48 | + let node_info = mcp.call(1, "tools/call", json!({ |
| 49 | + "name": "get_node_info", |
| 50 | + "arguments": {} |
| 51 | + })); |
| 52 | + let node_info_text = node_info["result"]["content"][0]["text"].as_str().unwrap(); |
| 53 | + let node_info_json: serde_json::Value = serde_json::from_str(node_info_text).unwrap(); |
| 54 | + assert_eq!(node_info_json["node_id"], server.node_id()); |
| 55 | + |
| 56 | + let onchain_receive = mcp.call(2, "tools/call", json!({ |
| 57 | + "name": "onchain_receive", |
| 58 | + "arguments": {} |
| 59 | + })); |
| 60 | + let onchain_receive_text = onchain_receive["result"]["content"][0]["text"].as_str().unwrap(); |
| 61 | + let onchain_receive_json: serde_json::Value = |
| 62 | + serde_json::from_str(onchain_receive_text).unwrap(); |
| 63 | + assert!(onchain_receive_json["address"].as_str().unwrap().starts_with("bcrt1")); |
| 64 | + |
| 65 | + let invoice = server |
| 66 | + .client() |
| 67 | + .bolt11_receive(Bolt11ReceiveRequest { |
| 68 | + amount_msat: Some(50_000_000), |
| 69 | + description: Some(Bolt11InvoiceDescription { |
| 70 | + kind: Some(bolt11_invoice_description::Kind::Direct("mcp decode".to_string())), |
| 71 | + }), |
| 72 | + expiry_secs: 3600, |
| 73 | + }) |
| 74 | + .await |
| 75 | + .unwrap(); |
| 76 | + |
| 77 | + let decode_invoice = mcp.call(3, "tools/call", json!({ |
| 78 | + "name": "decode_invoice", |
| 79 | + "arguments": { "invoice": invoice.invoice } |
| 80 | + })); |
| 81 | + let decode_invoice_text = decode_invoice["result"]["content"][0]["text"].as_str().unwrap(); |
| 82 | + let decode_invoice_json: serde_json::Value = |
| 83 | + serde_json::from_str(decode_invoice_text).unwrap(); |
| 84 | + assert_eq!(decode_invoice_json["destination"], server.node_id()); |
| 85 | + assert_eq!(decode_invoice_json["description"], "mcp decode"); |
| 86 | + assert_eq!(decode_invoice_json["amount_msat"], 50_000_000u64); |
| 87 | +} |
0 commit comments