this repo has no description
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

resource listing

+104 -11
+5
bin/dune
··· 14 14 (modules ocaml_eval_sdk) 15 15 (flags (:standard -w -32 -w -33)) 16 16 (libraries mcp mcp_sdk mcp_server yojson eio_main eio compiler-libs.toplevel)) 17 + 18 + (executable 19 + (name markdown_book_sdk) 20 + (modules markdown_book_sdk) 21 + (libraries mcp mcp_sdk mcp_server yojson eio_main eio))
+99 -11
lib/mcp_server.ml
··· 2 2 open Jsonrpc 3 3 open Mcp_sdk 4 4 5 + (* Create a proper JSONRPC error with code and data *) 6 + let create_jsonrpc_error id code message ?data () = 7 + let error_code = ErrorCode.to_int code in 8 + let error_data = match data with 9 + | Some d -> d 10 + | None -> `Null 11 + in 12 + create_error ~id ~code:error_code ~message ~data:(Some error_data) () 13 + 5 14 (* Process initialize request *) 6 15 let handle_initialize server req = 7 16 Log.debug "Processing initialize request"; ··· 51 60 let response = Mcp_rpc.ResourcesList.create_response ~id:req.id ~resources:resources_list () in 52 61 Some response 53 62 63 + (* Process resources/read request *) 64 + let handle_resources_read server (req:JSONRPCMessage.request) = 65 + Log.debug "Processing resources/read request"; 66 + match req.JSONRPCMessage.params with 67 + | None -> 68 + Log.error "Missing params for resources/read request"; 69 + Some (create_jsonrpc_error req.id ErrorCode.InvalidParams "Missing params for resources/read request" ()) 70 + | Some params -> 71 + let req_data = Mcp_rpc.ResourcesRead.Request.t_of_yojson params in 72 + let uri = req_data.uri in 73 + Log.debugf "Resource URI: %s" uri; 74 + 75 + (* Find matching resource handler by URI template *) 76 + let find_resource_handler uri = 77 + let parse_uri_template template uri = 78 + (* Simple URI template parsing: extract parameters from a URI based on a template *) 79 + let template_parts = String.split_on_char '/' template in 80 + let uri_parts = String.split_on_char '/' uri in 81 + 82 + (* If parts don't match in length, this template doesn't match *) 83 + if List.length template_parts <> List.length uri_parts then 84 + None 85 + else 86 + (* Extract parameters where template has {param} format *) 87 + let params, matches = 88 + List.fold_left2 (fun (params, matches) template_part uri_part -> 89 + if String.length template_part > 2 && 90 + template_part.[0] = '{' && 91 + template_part.[String.length template_part - 1] = '}' then 92 + (* This is a parameter, extract its value *) 93 + let _param_name = String.sub template_part 1 (String.length template_part - 2) in 94 + (uri_part :: params, matches) 95 + else if template_part = uri_part then 96 + (* Constant part matches *) 97 + (params, true && matches) 98 + else 99 + (* Constant part doesn't match *) 100 + (params, false && matches) 101 + ) ([], true) template_parts uri_parts 102 + in 103 + 104 + if matches then Some (List.rev params) else None 105 + in 106 + 107 + let rec find_handler = function 108 + | [] -> None 109 + | resource :: rest -> 110 + match parse_uri_template resource.Resource.uri_template uri with 111 + | Some params -> Some (resource, params) 112 + | None -> find_handler rest 113 + in 114 + 115 + find_handler (resources server) 116 + in 117 + 118 + match find_resource_handler uri with 119 + | Some (resource, params) -> 120 + (* Create context for this request *) 121 + let ctx = Context.create 122 + ?request_id:(Some req.id) 123 + ?progress_token:req.progress_token 124 + ~lifespan_context:[("resources/read", `Assoc [("uri", `String uri)])] 125 + () 126 + in 127 + 128 + (* Call the resource handler *) 129 + (match resource.handler ctx params with 130 + | Ok content -> 131 + (* Create text resource content *) 132 + let mime_type = match resource.mime_type with 133 + | Some mime -> mime 134 + | None -> "text/plain" 135 + in 136 + let text_resource = { 137 + TextResourceContents.uri; 138 + text = content; 139 + mime_type = Some mime_type 140 + } in 141 + let resource_content = Mcp_rpc.ResourcesRead.ResourceContent.TextResource text_resource in 142 + let response = Mcp_rpc.ResourcesRead.create_response ~id:req.id ~contents:[resource_content] () in 143 + Some response 144 + | Error err -> 145 + Log.errorf "Error reading resource: %s" err; 146 + Some (create_jsonrpc_error req.id ErrorCode.InternalError ("Error reading resource: " ^ err) ())) 147 + | None -> 148 + Log.errorf "Resource not found: %s" uri; 149 + Some (create_jsonrpc_error req.id ErrorCode.InvalidParams ("Resource not found: " ^ uri) ()) 150 + 54 151 (* Extract the tool name from params *) 55 152 let extract_tool_name params = 56 153 match List.assoc_opt "name" params with ··· 70 167 | _ -> 71 168 Log.debug "No arguments provided for tool call, using empty object"; 72 169 `Assoc [] (* Empty arguments is valid *) 73 - 74 - (* Create a proper JSONRPC error with code and data *) 75 - let create_jsonrpc_error id code message ?data () = 76 - let error_code = ErrorCode.to_int code in 77 - let error_data = match data with 78 - | Some d -> d 79 - | None -> `Null 80 - in 81 - create_error ~id ~code:error_code ~message ~data:(Some error_data) () 82 - 83 170 84 171 (* Execute a tool *) 85 172 let execute_tool server ctx name args = ··· 182 269 | Method.ToolsCall -> handle_tools_call server req 183 270 | Method.PromptsList -> handle_prompts_list server req 184 271 | Method.ResourcesList -> handle_resources_list server req 272 + | Method.ResourcesRead -> handle_resources_read server req 185 273 | _ -> 186 274 Log.errorf "Unknown method received: %s" (Method.to_string req.meth); 187 - Some (create_jsonrpc_error req.id MethodNotFound ("Method not found: " ^ (Method.to_string req.meth)) ())) 275 + Some (create_jsonrpc_error req.id ErrorCode.MethodNotFound ("Method not found: " ^ (Method.to_string req.meth)) ())) 188 276 | JSONRPCMessage.Notification notif -> 189 277 Log.debugf "Received notification with method: %s" (Method.to_string notif.meth); 190 278 (match notif.meth with