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:
@@ -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:
|
||||

|
||||

|
||||
|
||||
10
examples/mpc/inspector/example.sh
Normal file → Executable file
10
examples/mpc/inspector/example.sh
Normal file → Executable 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
BIN
examples/mpc/inspector/server
Executable file
Binary file not shown.
120
examples/mpc/inspector/server.vsh
Executable file
120
examples/mpc/inspector/server.vsh
Executable 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()!
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user