@@ -17,10 +17,12 @@ use ldk_server_client::ldk_server_grpc::api::GetNodeInfoRequest;
1717use serde_json:: Value ;
1818use tokio:: io:: { AsyncBufReadExt , AsyncWriteExt , BufReader } ;
1919
20- use crate :: mcp:: InitializeResult ;
20+ use crate :: mcp:: {
21+ InitializeResult , LEGACY_PROTOCOL_VERSION , PROTOCOL_VERSION , SERVER_NAME , SERVER_VERSION ,
22+ } ;
2123use crate :: protocol:: {
2224 JsonRpcErrorResponse , JsonRpcRequest , JsonRpcResponse , INVALID_PARAMS , METHOD_NOT_FOUND ,
23- PARSE_ERROR ,
25+ PARSE_ERROR , UNSUPPORTED_PROTOCOL_VERSION ,
2426} ;
2527use crate :: tools:: build_tool_registry;
2628
@@ -62,7 +64,7 @@ async fn main() {
6264
6365 // Probe the server so misconfiguration surfaces on startup rather than on
6466 // the first tool call. We warn instead of exiting so the MCP protocol loop
65- // still answers `initialize` and `tools/ list` even when the server is
67+ // still answers discovery and tool- list requests even when the server is
6668 // temporarily unreachable.
6769 if let Err ( e) = client. get_node_info ( GetNodeInfoRequest { } ) . await {
6870 eprintln ! ( "Warning: Failed to reach ldk-server on startup: {e}" ) ;
@@ -113,30 +115,136 @@ async fn main() {
113115
114116 let id = request. id . unwrap ( ) ;
115117
118+ let protocol_version = request
119+ . params
120+ . as_ref ( )
121+ . and_then ( |params| params. get ( "_meta" ) )
122+ . and_then ( |meta| meta. get ( "io.modelcontextprotocol/protocolVersion" ) )
123+ . and_then ( Value :: as_str)
124+ . map ( str:: to_owned) ;
125+
126+ if let Some ( requested) = protocol_version. as_deref ( ) {
127+ if requested != PROTOCOL_VERSION && requested != LEGACY_PROTOCOL_VERSION {
128+ let err = JsonRpcErrorResponse :: with_data (
129+ id,
130+ UNSUPPORTED_PROTOCOL_VERSION ,
131+ format ! ( "Unsupported protocol version: {requested}" ) ,
132+ serde_json:: json!( {
133+ "supported" : [ PROTOCOL_VERSION , LEGACY_PROTOCOL_VERSION ] ,
134+ "requested" : requested,
135+ } ) ,
136+ ) ;
137+ write_response ( & mut stdout, serde_json:: to_string ( & err) . unwrap ( ) ) . await ;
138+ continue ;
139+ }
140+
141+ let has_capabilities = request
142+ . params
143+ . as_ref ( )
144+ . and_then ( |params| params. get ( "_meta" ) )
145+ . and_then ( |meta| meta. get ( "io.modelcontextprotocol/clientCapabilities" ) )
146+ . is_some_and ( Value :: is_object) ;
147+ if requested == PROTOCOL_VERSION && !has_capabilities {
148+ let err = JsonRpcErrorResponse :: new (
149+ id,
150+ INVALID_PARAMS ,
151+ "Missing required request metadata: io.modelcontextprotocol/clientCapabilities"
152+ . to_string ( ) ,
153+ ) ;
154+ write_response ( & mut stdout, serde_json:: to_string ( & err) . unwrap ( ) ) . await ;
155+ continue ;
156+ }
157+ }
158+
159+ let latest_protocol = protocol_version. as_deref ( ) == Some ( PROTOCOL_VERSION ) ;
116160 let response_str = match request. method . as_str ( ) {
117161 "initialize" => {
118- let result = InitializeResult :: new ( ) ;
119- let resp = JsonRpcResponse :: new ( id, serde_json:: to_value ( result) . unwrap ( ) ) ;
120- serde_json:: to_string ( & resp) . unwrap ( )
162+ if request
163+ . params
164+ . as_ref ( )
165+ . and_then ( |params| params. get ( "protocolVersion" ) )
166+ . and_then ( Value :: as_str)
167+ == Some ( PROTOCOL_VERSION )
168+ {
169+ let err = JsonRpcErrorResponse :: new (
170+ id,
171+ METHOD_NOT_FOUND ,
172+ "Method not found: initialize" . to_string ( ) ,
173+ ) ;
174+ serde_json:: to_string ( & err) . unwrap ( )
175+ } else {
176+ let result = InitializeResult :: new ( ) ;
177+ let resp = JsonRpcResponse :: new ( id, serde_json:: to_value ( result) . unwrap ( ) ) ;
178+ serde_json:: to_string ( & resp) . unwrap ( )
179+ }
180+ } ,
181+ "server/discover" => {
182+ if !latest_protocol {
183+ let err = JsonRpcErrorResponse :: new (
184+ id,
185+ INVALID_PARAMS ,
186+ "server/discover requires 2026-07-28 request metadata" . to_string ( ) ,
187+ ) ;
188+ serde_json:: to_string ( & err) . unwrap ( )
189+ } else {
190+ let result = latest_result ( serde_json:: json!( {
191+ "supportedVersions" : [ PROTOCOL_VERSION , LEGACY_PROTOCOL_VERSION ] ,
192+ "capabilities" : { "tools" : { } } ,
193+ "instructions" : "Use the available tools to operate an LDK Server node." ,
194+ "ttlMs" : 300_000 ,
195+ "cacheScope" : "public" ,
196+ } ) ) ;
197+ let resp = JsonRpcResponse :: new ( id, result) ;
198+ serde_json:: to_string ( & resp) . unwrap ( )
199+ }
121200 } ,
122201 "tools/list" => {
123202 let tools = registry. list_tools ( ) ;
124- let resp = JsonRpcResponse :: new ( id, serde_json:: json!( { "tools" : tools } ) ) ;
203+ let result = if latest_protocol {
204+ latest_result ( serde_json:: json!( {
205+ "tools" : tools,
206+ "ttlMs" : 300_000 ,
207+ "cacheScope" : "public" ,
208+ } ) )
209+ } else {
210+ serde_json:: json!( { "tools" : tools } )
211+ } ;
212+ let resp = JsonRpcResponse :: new ( id, result) ;
125213 serde_json:: to_string ( & resp) . unwrap ( )
126214 } ,
127215 "ping" => {
128- // Per the MCP spec, a ping must be answered with an empty result object.
129- let resp = JsonRpcResponse :: new ( id, serde_json:: json!( { } ) ) ;
130- serde_json:: to_string ( & resp) . unwrap ( )
216+ if latest_protocol {
217+ let err = JsonRpcErrorResponse :: new (
218+ id,
219+ METHOD_NOT_FOUND ,
220+ "Method not found: ping" . to_string ( ) ,
221+ ) ;
222+ serde_json:: to_string ( & err) . unwrap ( )
223+ } else {
224+ let resp = JsonRpcResponse :: new ( id, serde_json:: json!( { } ) ) ;
225+ serde_json:: to_string ( & resp) . unwrap ( )
226+ }
131227 } ,
132228 "tools/call" => {
133229 let params = request. params . unwrap_or ( Value :: Null ) ;
134230 match params. get ( "name" ) . and_then ( |v| v. as_str ( ) ) {
231+ Some ( tool_name) if latest_protocol && !registry. has_tool ( tool_name) => {
232+ let err = JsonRpcErrorResponse :: new (
233+ id,
234+ INVALID_PARAMS ,
235+ format ! ( "Unknown tool: {tool_name}" ) ,
236+ ) ;
237+ serde_json:: to_string ( & err) . unwrap ( )
238+ } ,
135239 Some ( tool_name) => {
136240 let tool_args =
137241 params. get ( "arguments" ) . cloned ( ) . unwrap_or ( serde_json:: json!( { } ) ) ;
138242 let result = registry. call_tool ( & client, tool_name, tool_args) . await ;
139- let resp = JsonRpcResponse :: new ( id, serde_json:: to_value ( result) . unwrap ( ) ) ;
243+ let mut result = serde_json:: to_value ( result) . unwrap ( ) ;
244+ if latest_protocol {
245+ result = latest_result ( result) ;
246+ }
247+ let resp = JsonRpcResponse :: new ( id, result) ;
140248 serde_json:: to_string ( & resp) . unwrap ( )
141249 } ,
142250 None => {
@@ -159,8 +267,27 @@ async fn main() {
159267 } ,
160268 } ;
161269
162- let _ = stdout. write_all ( response_str. as_bytes ( ) ) . await ;
163- let _ = stdout. write_all ( b"\n " ) . await ;
164- let _ = stdout. flush ( ) . await ;
270+ write_response ( & mut stdout, response_str) . await ;
165271 }
166272}
273+
274+ fn latest_result ( mut result : Value ) -> Value {
275+ let object = result. as_object_mut ( ) . expect ( "MCP results must be JSON objects" ) ;
276+ object. insert ( "resultType" . to_string ( ) , Value :: String ( "complete" . to_string ( ) ) ) ;
277+ object. insert (
278+ "_meta" . to_string ( ) ,
279+ serde_json:: json!( {
280+ "io.modelcontextprotocol/serverInfo" : {
281+ "name" : SERVER_NAME ,
282+ "version" : SERVER_VERSION ,
283+ }
284+ } ) ,
285+ ) ;
286+ result
287+ }
288+
289+ async fn write_response ( stdout : & mut tokio:: io:: Stdout , response : String ) {
290+ let _ = stdout. write_all ( response. as_bytes ( ) ) . await ;
291+ let _ = stdout. write_all ( b"\n " ) . await ;
292+ let _ = stdout. flush ( ) . await ;
293+ }
0 commit comments