this repo has no description
6
fork

Configure Feed

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

Add example servers to demonstrate MCP features

- audio_example.ml: Demonstrates audio content and tool results with multiple content types
- resource_template_example.ml: Shows resource templates and embedded resources
- completion_example.ml: Implements completion support with a custom tool

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+514
+145
bin/audio_example.ml
··· 1 + open Mcp 2 + open Mcp_sdk 3 + 4 + (* Helper for extracting string value from JSON *) 5 + let get_string_param json name = 6 + match json with 7 + | `Assoc fields -> 8 + (match List.assoc_opt name fields with 9 + | Some (`String value) -> value 10 + | _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name))) 11 + | _ -> raise (Failure "Expected JSON object") 12 + 13 + (* Create a server *) 14 + let server = create_server 15 + ~name:"OCaml MCP Audio Example" 16 + ~version:"0.1.0" 17 + ~protocol_version:"2024-11-05" 18 + () 19 + 20 + (* Define startup and shutdown hooks *) 21 + let startup () = 22 + Printf.printf "AudioExampleServer is starting up!\n"; 23 + flush stdout; 24 + Log.info "AudioExampleServer is starting up!" 25 + 26 + let shutdown () = 27 + Printf.printf "AudioExampleServer is shutting down. Goodbye!\n"; 28 + flush stdout; 29 + Log.info "AudioExampleServer is shutting down. Goodbye!" 30 + 31 + (* Register the hooks *) 32 + let () = 33 + set_startup_hook server startup; 34 + set_shutdown_hook server shutdown 35 + 36 + (* Helper to create audio content *) 37 + let make_audio_content data mime_type = 38 + let audio_content = AudioContent.{ 39 + data; 40 + mime_type; 41 + annotations = None; 42 + } in 43 + Audio audio_content 44 + 45 + (* Define and register an audio tool *) 46 + let _ = add_tool server 47 + ~name:"generate_audio_description" 48 + ~description:"Generates a description with an audio sample" 49 + ~schema_properties:[ 50 + ("text", "string", "The text to describe with audio"); 51 + ("frequency", "number", "The frequency in Hz for the tone (optional)"); 52 + ] 53 + ~schema_required:["text"] 54 + (fun args -> 55 + try 56 + let text = get_string_param args "text" in 57 + let frequency = 58 + try 59 + match List.assoc_opt "frequency" (match args with `Assoc l -> l | _ -> []) with 60 + | Some (`Int f) -> f 61 + | Some (`Float f) -> int_of_float f 62 + | _ -> 440 (* Default to A440 *) 63 + with _ -> 440 64 + in 65 + 66 + (* This is just a placeholder for actual audio data *) 67 + (* In a real implementation, you would generate a WAV or MP3 file and base64 encode it *) 68 + let audio_data = Printf.sprintf "BASE64_ENCODED_AUDIO_DATA_FOR_%d_HZ_TONE" frequency in 69 + 70 + (* Create a response with both text and audio content *) 71 + CallToolResult.yojson_of_t CallToolResult.{ 72 + content = [ 73 + Text TextContent.{ text = Printf.sprintf "Description: %s (with %d Hz tone)" text frequency; annotations = None }; 74 + Audio AudioContent.{ data = audio_data; mime_type = "audio/wav"; annotations = None } 75 + ]; 76 + is_error = false; 77 + meta = None 78 + } 79 + with 80 + | Failure msg -> 81 + Log.error (Printf.sprintf "Error in audio tool: %s" msg); 82 + CallToolResult.yojson_of_t CallToolResult.{ 83 + content = [ 84 + Text TextContent.{ 85 + text = Printf.sprintf "Error: %s" msg; 86 + annotations = None 87 + } 88 + ]; 89 + is_error = true; 90 + meta = None 91 + } 92 + ) 93 + 94 + (* Define and register a prompt example with audio *) 95 + let _ = add_prompt server 96 + ~name:"audio-description-prompt" 97 + ~description:"A prompt with audio and text content" 98 + ~arguments:[ 99 + ("description", Some "Text description to accompany the audio", true); 100 + ("frequency", Some "Frequency in Hz for the audio tone", false); 101 + ] 102 + (fun args -> 103 + let description = 104 + try List.assoc "description" args 105 + with Not_found -> "No description provided" 106 + in 107 + let frequency = 108 + try int_of_string (List.assoc "frequency" args) 109 + with _ -> 440 (* Default to A440 *) 110 + in 111 + 112 + (* Placeholder for audio data *) 113 + let audio_data = Printf.sprintf "BASE64_ENCODED_AUDIO_DATA_FOR_%d_HZ_TONE" frequency in 114 + 115 + [ 116 + Prompt.{ 117 + role = `User; 118 + content = make_text_content "Here's a sound sample with description:" 119 + }; 120 + Prompt.{ 121 + role = `User; 122 + content = make_audio_content audio_data "audio/wav" 123 + }; 124 + Prompt.{ 125 + role = `User; 126 + content = make_text_content description 127 + }; 128 + Prompt.{ 129 + role = `Assistant; 130 + content = make_text_content "I've received your audio file and description." 131 + } 132 + ] 133 + ) 134 + 135 + (* Main function *) 136 + let () = 137 + (* Print directly to ensure we see output *) 138 + Printf.printf "Starting AudioExampleServer...\n"; 139 + flush stdout; 140 + 141 + (* Configure the server with appropriate capabilities *) 142 + ignore (configure_server server ()); 143 + 144 + (* Run the server *) 145 + run_server server
+180
bin/completion_example.ml
··· 1 + open Mcp 2 + open Mcp_sdk 3 + 4 + (* Helper for extracting string value from JSON *) 5 + let get_string_param json name = 6 + match json with 7 + | `Assoc fields -> 8 + (match List.assoc_opt name fields with 9 + | Some (`String value) -> value 10 + | _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name))) 11 + | _ -> raise (Failure "Expected JSON object") 12 + 13 + (* Create a server *) 14 + let server = create_server 15 + ~name:"OCaml MCP Completion Example" 16 + ~version:"0.1.0" 17 + ~protocol_version:"2024-11-05" 18 + () 19 + 20 + (* Define startup and shutdown hooks *) 21 + let startup () = 22 + Printf.printf "CompletionServer is starting up!\n"; 23 + flush stdout; 24 + Log.info "CompletionServer is starting up!" 25 + 26 + let shutdown () = 27 + Printf.printf "CompletionServer is shutting down. Goodbye!\n"; 28 + flush stdout; 29 + Log.info "CompletionServer is shutting down. Goodbye!" 30 + 31 + (* Register the hooks *) 32 + let () = 33 + set_startup_hook server startup; 34 + set_shutdown_hook server shutdown 35 + 36 + (* Database of programming languages and their features *) 37 + let languages = [ 38 + ("ocaml", ["functional"; "static typing"; "pattern matching"; "modules"; "type inference"]); 39 + ("python", ["dynamic typing"; "interpreted"; "object-oriented"; "high-level"; "scripting"]); 40 + ("rust", ["memory safety"; "performance"; "static typing"; "ownership"; "zero-cost abstractions"]); 41 + ("javascript", ["dynamic typing"; "interpreted"; "prototypes"; "single-threaded"; "event-driven"]); 42 + ("go", ["concurrency"; "garbage collection"; "simplicity"; "static typing"; "compiled"]); 43 + ] 44 + 45 + (* Helper function to create a completion response *) 46 + let create_completion values ?(has_more=false) ?(total=None) () = 47 + Completion.Result.{ 48 + completion = { 49 + values; 50 + has_more = Some has_more; 51 + total; 52 + }; 53 + meta = None; 54 + } 55 + 56 + (* Define and register a tool that handles completions *) 57 + let _ = add_tool server 58 + ~name:"complete" 59 + ~description:"Handles completion requests for programming languages and features" 60 + ~schema_properties:[ 61 + ("argument_name", "string", "The name of the argument to complete"); 62 + ("argument_value", "string", "The partial value to complete"); 63 + ] 64 + ~schema_required:["argument_name"; "argument_value"] 65 + (fun args -> 66 + try 67 + let argument_name = get_string_param args "argument_name" in 68 + let argument_value = get_string_param args "argument_value" in 69 + 70 + Log.info (Printf.sprintf "Completion request for %s = %s" argument_name argument_value); 71 + 72 + (* Handle different completion requests *) 73 + let result = 74 + match argument_name with 75 + | "language" -> 76 + (* Complete programming language names *) 77 + let matches = 78 + List.filter 79 + (fun (lang, _) -> 80 + let lang_lower = String.lowercase_ascii lang in 81 + let arg_lower = String.lowercase_ascii argument_value in 82 + String.length lang_lower >= String.length arg_lower && 83 + String.sub lang_lower 0 (String.length arg_lower) = arg_lower) 84 + languages 85 + in 86 + let values = List.map fst matches in 87 + create_completion values ~has_more:false ~total:(Some (List.length values)) () 88 + 89 + | "feature" -> 90 + (* Complete programming language features *) 91 + let all_features = 92 + List.flatten (List.map snd languages) |> 93 + List.sort_uniq String.compare 94 + in 95 + let matches = 96 + List.filter 97 + (fun feature -> 98 + let feature_lower = String.lowercase_ascii feature in 99 + let arg_lower = String.lowercase_ascii argument_value in 100 + String.length feature_lower >= String.length arg_lower && 101 + String.sub feature_lower 0 (String.length arg_lower) = arg_lower) 102 + all_features 103 + in 104 + create_completion matches ~has_more:false ~total:(Some (List.length matches)) () 105 + 106 + | _ -> 107 + (* Default completions for unknown arguments *) 108 + create_completion ["unknown argument"] () 109 + in 110 + 111 + (* Convert to JSON and return *) 112 + TextContent.yojson_of_t TextContent.{ 113 + text = Yojson.Safe.to_string (Completion.Result.to_result result); 114 + annotations = None 115 + } 116 + with 117 + | Failure msg -> 118 + Log.error (Printf.sprintf "Error handling completion request: %s" msg); 119 + TextContent.yojson_of_t TextContent.{ 120 + text = Printf.sprintf "Error: %s" msg; 121 + annotations = None 122 + } 123 + ) 124 + 125 + (* Define and register a prompt that provides programming language info *) 126 + let _ = add_prompt server 127 + ~name:"language-info-prompt" 128 + ~description:"A prompt that provides information about programming languages" 129 + ~arguments:[ 130 + ("language", Some "Name of the programming language", true); 131 + ] 132 + (fun args -> 133 + let language = 134 + try List.assoc "language" args 135 + with Not_found -> "ocaml" (* Default to OCaml *) 136 + in 137 + 138 + let features = 139 + try 140 + let features = List.assoc (String.lowercase_ascii language) languages in 141 + String.concat ", " features 142 + with Not_found -> "unknown language" 143 + in 144 + 145 + [ 146 + Prompt.{ 147 + role = `User; 148 + content = make_text_content (Printf.sprintf "Tell me about the %s programming language" language) 149 + }; 150 + Prompt.{ 151 + role = `Assistant; 152 + content = make_text_content (Printf.sprintf "%s is a programming language with the following features: %s" language features) 153 + } 154 + ] 155 + ) 156 + 157 + (* Main function *) 158 + let () = 159 + (* Print directly to ensure we see output *) 160 + Printf.printf "Starting CompletionServer...\n"; 161 + flush stdout; 162 + 163 + (* Set custom capabilities to indicate support for completions *) 164 + let capabilities = `Assoc [ 165 + ("completions", `Assoc []); (* Indicate support for completions *) 166 + ("prompts", `Assoc [ 167 + ("listChanged", `Bool true) 168 + ]); 169 + ("resources", `Assoc [ 170 + ("listChanged", `Bool true); 171 + ("subscribe", `Bool true) 172 + ]); 173 + ("tools", `Assoc [ 174 + ("listChanged", `Bool true) 175 + ]) 176 + ] in 177 + set_capabilities server capabilities; 178 + 179 + (* Run the server *) 180 + run_server server
+15
bin/dune
··· 5 5 (executable 6 6 (name capitalize_sdk) 7 7 (modules capitalize_sdk) 8 + (libraries mcp mcp_sdk yojson unix)) 9 + 10 + (executable 11 + (name audio_example) 12 + (modules audio_example) 13 + (libraries mcp mcp_sdk yojson unix)) 14 + 15 + (executable 16 + (name resource_template_example) 17 + (modules resource_template_example) 18 + (libraries mcp mcp_sdk yojson unix)) 19 + 20 + (executable 21 + (name completion_example) 22 + (modules completion_example) 8 23 (libraries mcp mcp_sdk yojson unix))
+174
bin/resource_template_example.ml
··· 1 + open Mcp 2 + open Mcp_sdk 3 + 4 + (* Helper for extracting string value from JSON *) 5 + let get_string_param json name = 6 + match json with 7 + | `Assoc fields -> 8 + (match List.assoc_opt name fields with 9 + | Some (`String value) -> value 10 + | _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name))) 11 + | _ -> raise (Failure "Expected JSON object") 12 + 13 + (* Create a server *) 14 + let server = create_server 15 + ~name:"OCaml MCP Resource Template Example" 16 + ~version:"0.1.0" 17 + ~protocol_version:"2024-11-05" 18 + () 19 + 20 + (* Define startup and shutdown hooks *) 21 + let startup () = 22 + Printf.printf "ResourceTemplateServer is starting up!\n"; 23 + flush stdout; 24 + Log.info "ResourceTemplateServer is starting up!" 25 + 26 + let shutdown () = 27 + Printf.printf "ResourceTemplateServer is shutting down. Goodbye!\n"; 28 + flush stdout; 29 + Log.info "ResourceTemplateServer is shutting down. Goodbye!" 30 + 31 + (* Register the hooks *) 32 + let () = 33 + set_startup_hook server startup; 34 + set_shutdown_hook server shutdown 35 + 36 + (* Database of "documents" *) 37 + let documents = [ 38 + ("doc1", "This is the first document content"); 39 + ("doc2", "This document contains information about OCaml"); 40 + ("doc3", "MCP protocol is a standard for LLM-based applications"); 41 + ("doc4", "Resource templates allow for parameterized resources"); 42 + ] 43 + 44 + (* Define and register a resource template for documents *) 45 + let _ = add_resource server 46 + ~uri_template:"document://{id}" 47 + ~description:"Get a document by ID" 48 + ~mime_type:"text/plain" 49 + (fun params -> 50 + match params with 51 + | [id] -> 52 + begin 53 + try 54 + let content = List.assoc id documents in 55 + content 56 + with Not_found -> 57 + Printf.sprintf "Error: Document '%s' not found" id 58 + end 59 + | _ -> "Error: Invalid document ID" 60 + ) 61 + 62 + (* Define and register a list documents resource *) 63 + let _ = add_resource server 64 + ~uri_template:"documents://list" 65 + ~description:"List all available documents" 66 + ~mime_type:"text/plain" 67 + (fun _ -> 68 + let doc_list = 69 + String.concat "\n" 70 + (List.map (fun (id, _) -> Printf.sprintf "- %s" id) documents) 71 + in 72 + Printf.sprintf "Available Documents:\n%s" doc_list 73 + ) 74 + 75 + (* Define and register a tool that uses resource references *) 76 + let _ = add_tool server 77 + ~name:"get_document" 78 + ~description:"Gets a document by ID using resource references" 79 + ~schema_properties:[ 80 + ("document_id", "string", "The ID of the document to retrieve"); 81 + ] 82 + ~schema_required:["document_id"] 83 + (fun args -> 84 + try 85 + let doc_id = get_string_param args "document_id" in 86 + 87 + (* Create a resource reference *) 88 + let ref = ResourceReference.{ uri = Printf.sprintf "document://%s" doc_id } in 89 + (* Convert to JSON for logging purposes *) 90 + let _ = ResourceReference.yojson_of_t ref in 91 + 92 + (* Return the reference *) 93 + CallToolResult.yojson_of_t CallToolResult.{ 94 + content = [ 95 + Text TextContent.{ text = Printf.sprintf "Resource reference for document %s:" doc_id; annotations = None }; 96 + Resource EmbeddedResource.{ 97 + resource = `Text TextResourceContents.{ 98 + uri = Printf.sprintf "document://%s" doc_id; 99 + text = (try List.assoc doc_id documents with Not_found -> "Not found"); 100 + mime_type = Some "text/plain" 101 + }; 102 + annotations = None 103 + } 104 + ]; 105 + is_error = false; 106 + meta = None 107 + } 108 + with 109 + | Failure msg -> 110 + Log.error (Printf.sprintf "Error in get_document tool: %s" msg); 111 + CallToolResult.yojson_of_t CallToolResult.{ 112 + content = [ 113 + Text TextContent.{ 114 + text = Printf.sprintf "Error: %s" msg; 115 + annotations = None 116 + } 117 + ]; 118 + is_error = true; 119 + meta = None 120 + } 121 + ) 122 + 123 + (* Define and register a prompt that uses resource templates *) 124 + let _ = add_prompt server 125 + ~name:"document-prompt" 126 + ~description:"A prompt that references document resources" 127 + ~arguments:[ 128 + ("document_id", Some "ID of the document to include in the prompt", true); 129 + ] 130 + (fun args -> 131 + let doc_id = 132 + try List.assoc "document_id" args 133 + with Not_found -> "doc1" (* Default to doc1 *) 134 + in 135 + 136 + let doc_text = 137 + try List.assoc doc_id documents 138 + with Not_found -> Printf.sprintf "Document '%s' not found" doc_id 139 + in 140 + 141 + [ 142 + Prompt.{ 143 + role = `User; 144 + content = make_text_content (Printf.sprintf "Please summarize the following document (ID: %s):" doc_id) 145 + }; 146 + Prompt.{ 147 + role = `User; 148 + content = Resource EmbeddedResource.{ 149 + resource = `Text TextResourceContents.{ 150 + uri = Printf.sprintf "document://%s" doc_id; 151 + text = doc_text; 152 + mime_type = Some "text/plain" 153 + }; 154 + annotations = None 155 + } 156 + }; 157 + Prompt.{ 158 + role = `Assistant; 159 + content = make_text_content "I'll help summarize this document for you." 160 + } 161 + ] 162 + ) 163 + 164 + (* Main function *) 165 + let () = 166 + (* Print directly to ensure we see output *) 167 + Printf.printf "Starting ResourceTemplateServer...\n"; 168 + flush stdout; 169 + 170 + (* Configure the server with appropriate capabilities *) 171 + ignore (configure_server server ()); 172 + 173 + (* Run the server *) 174 + run_server server