feat: add complete V server example and improve runner

- Create new `server.vsh` example with custom tools
- Update `example.sh` to use the new V server script
- Improve README with new, clearer running instructions
- Fix server to not send responses for notifications
- Remove debug logging statements from server and handler
This commit is contained in:
Mahmoud-Emad
2025-07-27 16:01:28 +03:00
parent 5cee9a4d5a
commit 6357ae43db
7 changed files with 159 additions and 14 deletions

View File

@@ -24,15 +24,33 @@ server.start()!
## Running Example
### Option 1: Using the example script (Recommended)
1. `bash example.sh`
2. `open localhost:5173`
2. The MCP inspector will open automatically in your browser
3. The inspector should connect automatically to the V server
### Option 2: Manual configuration
1. Start the MCP inspector: `npx @modelcontextprotocol/inspector`
2. Open `localhost:5173` in your browser
3. Configure inspector:
- Transport type: `STDIO`
- Command: `<path-to-server.vsh>`
- Arguments: (leave empty)
4. Click "Connect"
### Option 3: Using V directly
1. Start the MCP inspector: `npx @modelcontextprotocol/inspector`
2. Open `localhost:5173` in your browser
3. Configure inspector:
- Transport type: `STDIO`
- Command: `v`
- Arguments: `-w -n run /Users/timurgordon/code/github/freeflowuniverse/herolib/lib/mcp/v_do/vdo.v`
4. Connect
- Arguments: `run <path-to-server.vsh>`
4. Click "Connect"
## Output
Expected output:
![Inspector Screenshot](inspector_screenshot.png)
![Inspector Screenshot](inspector_screenshot.png)

10
examples/mpc/inspector/example.sh Normal file → Executable file
View File

@@ -1,3 +1,11 @@
#!/bin/bash
npx @modelcontextprotocol/inspector node build/index.js
# Get the absolute path to the server.vsh file
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVER_PATH="$SCRIPT_DIR/server.vsh"
# Make sure the server script is executable
chmod +x "$SERVER_PATH"
# Start the MCP inspector with the V server
npx @modelcontextprotocol/inspector "$SERVER_PATH"

BIN
examples/mpc/inspector/server Executable file

Binary file not shown.

120
examples/mpc/inspector/server.vsh Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env -S v -n -w -cg -d use_openssl -enable-globals run
import freeflowuniverse.herolib.mcp
import freeflowuniverse.herolib.schemas.jsonrpc
import freeflowuniverse.herolib.schemas.jsonschema
import x.json2
// Example custom tool handler function
fn my_custom_handler(arguments map[string]json2.Any) !mcp.ToolCallResult {
return mcp.ToolCallResult{
is_error: false
content: [
mcp.ToolContent{
typ: 'text'
text: 'Hello from custom handler! Arguments: ${arguments}'
},
]
}
}
// Example of calculating 2 numbers
fn calculate(arguments map[string]json2.Any) !mcp.ToolCallResult {
// Check if num1 exists and can be converted to a number
if 'num1' !in arguments {
return mcp.ToolCallResult{
is_error: true
content: [
mcp.ToolContent{
typ: 'text'
text: 'Missing num1 parameter'
},
]
}
}
// Try to get num1 as a number (JSON numbers can be int, i64, or f64)
num1 := arguments['num1'].f64()
// Check if num2 exists and can be converted to a number
if 'num2' !in arguments {
return mcp.ToolCallResult{
is_error: true
content: [
mcp.ToolContent{
typ: 'text'
text: 'Missing num2 parameter'
},
]
}
}
// Try to get num2 as a number
num2 := arguments['num2'].f64()
// Calculate the result
result := num1 + num2
// Return the result
return mcp.ToolCallResult{
is_error: false
content: [
mcp.ToolContent{
typ: 'text'
text: 'Result: ${result} (${num1} + ${num2})'
},
]
}
}
// Create a backend with custom tools and handlers
backend := mcp.MemoryBackend{
tools: {
'custom_method': mcp.Tool{
name: 'custom_method'
description: 'A custom example tool'
input_schema: jsonschema.Schema{
typ: 'object'
properties: {
'message': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'string'
description: 'A message to process'
})
}
required: ['message']
}
}
'calculate': mcp.Tool{
name: 'calculate'
description: 'Calculates the sum of two numbers'
input_schema: jsonschema.Schema{
typ: 'object'
properties: {
'num1': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'number'
description: 'The first number'
})
'num2': jsonschema.SchemaRef(jsonschema.Schema{
typ: 'number'
description: 'The second number'
})
}
required: ['num1', 'num2']
}
}
}
tool_handlers: {
'custom_method': my_custom_handler
'calculate': calculate
}
}
// Create and start the server
mut server := mcp.new_server(backend, mcp.ServerParams{
config: mcp.ServerConfiguration{
server_info: mcp.ServerInfo{
name: 'inspector_example'
version: '1.0.0'
}
}
})!
server.start()!

View File

@@ -41,15 +41,16 @@ pub fn (mut s Server) start() ! {
continue
}
// Send the response
s.send(response)
// Send the response only if it's not empty (notifications return empty responses)
if response.len > 0 {
s.send(response)
}
}
}
// send sends a response to the client
pub fn (mut s Server) send(response string) {
// Send the response
log.error('Sending response: ${response}')
println(response)
flush_stdout()
}

View File

@@ -41,15 +41,16 @@ pub fn (mut s Server) start() ! {
continue
}
// Send the response
s.send(response)
// Send the response only if it's not empty (notifications return empty responses)
if response.len > 0 {
s.send(response)
}
}
}
// send sends a response to the client
pub fn (mut s Server) send(response string) {
// Send the response
log.error('Sending response: ${response}')
println(response)
flush_stdout()
}

View File

@@ -68,7 +68,6 @@ pub fn (handler Handler) handler(client &websocket.Client, message string) strin
// - The JSON-RPC response as a string, or an error if processing fails
pub fn (handler Handler) handle(message string) !string {
// Extract the method name from the request
log.error('debugzo1')
method := decode_request_method(message)!
// log.info('Handling remote procedure call to method: ${method}')
// Look up the procedure handler for the requested method
@@ -77,8 +76,6 @@ pub fn (handler Handler) handle(message string) !string {
return method_not_found
}
log.error('debugzo3')
// Execute the procedure handler with the request payload
response := procedure_func(message) or { panic(err) }
return response