mcp refactor wip
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
module escalayer
|
||||
|
||||
import log
|
||||
|
||||
// Task represents a complete AI task composed of multiple sequential unit tasks
|
||||
pub struct Task {
|
||||
pub mut:
|
||||
@@ -41,7 +43,7 @@ pub fn (mut t Task) initiate(input string)! string {
|
||||
mut current_input := input
|
||||
|
||||
for i, mut unit_task in t.unit_tasks {
|
||||
println('Executing unit task ${i+1}/${t.unit_tasks.len}: ${unit_task.name}')
|
||||
log.error('Executing unit task ${i+1}/${t.unit_tasks.len}: ${unit_task.name}')
|
||||
|
||||
// Execute the unit task with the current input
|
||||
result := unit_task.execute(current_input)!
|
||||
@@ -1,5 +1,6 @@
|
||||
module escalayer
|
||||
|
||||
import log
|
||||
import freeflowuniverse.herolib.clients.openai
|
||||
|
||||
// UnitTask represents a single step in the task
|
||||
@@ -30,7 +31,7 @@ pub fn (mut ut UnitTask) execute(input string)! string {
|
||||
|
||||
// If we've exhausted retries with the base model, switch to the retry model
|
||||
if attempts > ut.retry_count {
|
||||
println('Escalating to more powerful model: ${ut.retry_model.name}')
|
||||
log.error('Escalating to more powerful model: ${ut.retry_model.name}')
|
||||
current_model = ut.retry_model
|
||||
// Calculate remaining attempts but don't exceed absolute max
|
||||
max_attempts = attempts + ut.retry_count
|
||||
@@ -39,7 +40,7 @@ pub fn (mut ut UnitTask) execute(input string)! string {
|
||||
}
|
||||
}
|
||||
|
||||
println('Attempt ${attempts} with model ${current_model.name}')
|
||||
log.error('Attempt ${attempts} with model ${current_model.name}')
|
||||
|
||||
// Prepare the prompt with error feedback if this is a retry
|
||||
mut current_prompt := prompt
|
||||
@@ -49,7 +50,7 @@ pub fn (mut ut UnitTask) execute(input string)! string {
|
||||
|
||||
// Call the AI model
|
||||
response := call_ai_model(current_prompt, current_model) or {
|
||||
println('AI call failed: ${err}')
|
||||
log.error('AI call failed: ${err}')
|
||||
last_error = err.str()
|
||||
continue // Try again
|
||||
}
|
||||
@@ -57,7 +58,7 @@ pub fn (mut ut UnitTask) execute(input string)! string {
|
||||
// Process the response with the callback function
|
||||
result := ut.callback_function(response) or {
|
||||
// If callback returns an error, retry with the error message
|
||||
println('Callback returned error: ${err}')
|
||||
log.error('Callback returned error: ${err}')
|
||||
last_error = err.str()
|
||||
continue // Try again
|
||||
}
|
||||
34
lib/ai/utils/utils.v
Normal file
34
lib/ai/utils/utils.v
Normal file
@@ -0,0 +1,34 @@
|
||||
module utils
|
||||
|
||||
// Helper function to extract code blocks from the response
|
||||
pub fn extract_code_block(response string, identifier string, language string) string {
|
||||
// Find the start marker for the code block
|
||||
mut start_marker := '```${language}\n// ${identifier}'
|
||||
if language == '' {
|
||||
start_marker = '```\n// ${identifier}'
|
||||
}
|
||||
|
||||
start_index := response.index(start_marker) or {
|
||||
// Try alternative format
|
||||
mut alt_marker := '```${language}\n${identifier}'
|
||||
if language == '' {
|
||||
alt_marker = '```\n${identifier}'
|
||||
}
|
||||
|
||||
response.index(alt_marker) or {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// Find the end marker
|
||||
end_marker := '```'
|
||||
end_index := response.index_after(end_marker, start_index + start_marker.len) or {
|
||||
return ''
|
||||
}
|
||||
|
||||
// Extract the content between the markers
|
||||
content_start := start_index + start_marker.len
|
||||
content := response[content_start..end_index].trim_space()
|
||||
|
||||
return content
|
||||
}
|
||||
103
lib/lang/rust/rust.v
Normal file
103
lib/lang/rust/rust.v
Normal file
@@ -0,0 +1,103 @@
|
||||
module rust
|
||||
|
||||
import os
|
||||
|
||||
// Reads and combines all Rust files in the given directory
|
||||
pub fn read_source_code(source_code_path string) !string {
|
||||
// Get all files in the directory
|
||||
files := os.ls(source_code_path) or {
|
||||
return error('Failed to list files in directory: ${err}')
|
||||
}
|
||||
|
||||
// Combine all Rust files into a single source code string
|
||||
mut source_code := ''
|
||||
for file in files {
|
||||
file_path := os.join_path(source_code_path, file)
|
||||
|
||||
// Skip directories and non-Rust files
|
||||
if os.is_dir(file_path) || !file.ends_with('.rs') {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read the file content
|
||||
file_content := os.read_file(file_path) or {
|
||||
println('Failed to read file ${file_path}: ${err}')
|
||||
continue
|
||||
}
|
||||
|
||||
// Add file content to the combined source code
|
||||
source_code += '// File: ${file}\n${file_content}\n\n'
|
||||
}
|
||||
|
||||
if source_code == '' {
|
||||
return error('No Rust files found in directory: ${source_code_path}')
|
||||
}
|
||||
|
||||
return source_code
|
||||
}
|
||||
|
||||
// Determines the crate path from the source code path
|
||||
pub fn determine_crate_path(source_code_path string) !string {
|
||||
// Extract the path relative to the src directory
|
||||
src_index := source_code_path.index('src/') or {
|
||||
return error('Could not determine crate path: src/ not found in path')
|
||||
}
|
||||
|
||||
mut path_parts := source_code_path[src_index+4..].split('/')
|
||||
// Remove the last part (the file name)
|
||||
if path_parts.len > 0 {
|
||||
path_parts.delete_last()
|
||||
}
|
||||
rel_path := path_parts.join('::')
|
||||
return 'sal::${rel_path}'
|
||||
}
|
||||
|
||||
// Extracts the module name from a directory path
|
||||
pub fn extract_module_name_from_path(path string) string {
|
||||
dir_parts := path.split('/')
|
||||
return dir_parts[dir_parts.len - 1]
|
||||
}
|
||||
|
||||
// Build and run a Rust project with an example
|
||||
pub fn run_example(project_dir string, example_name string) !(string, string) {
|
||||
// Change to the project directory
|
||||
os.chdir(project_dir) or {
|
||||
return error('Failed to change directory to project: ${err}')
|
||||
}
|
||||
|
||||
// Run cargo build first
|
||||
build_result := os.execute('cargo build')
|
||||
if build_result.exit_code != 0 {
|
||||
return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}')
|
||||
}
|
||||
|
||||
// Run the example
|
||||
run_result := os.execute('cargo run --example ${example_name}')
|
||||
|
||||
return build_result.output, run_result.output
|
||||
}
|
||||
|
||||
|
||||
// Extract function names from wrapper code
|
||||
fn extract_functions_from_code(code string) []string {
|
||||
mut functions := []string{}
|
||||
lines := code.split('\n')
|
||||
|
||||
for line in lines {
|
||||
if line.contains('pub fn ') && !line.contains('//') {
|
||||
// Extract function name
|
||||
parts := line.split('pub fn ')
|
||||
if parts.len > 1 {
|
||||
name_parts := parts[1].split('(')
|
||||
if name_parts.len > 0 {
|
||||
fn_name := name_parts[0].trim_space()
|
||||
if fn_name != '' {
|
||||
functions << fn_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return functions
|
||||
}
|
||||
@@ -14,6 +14,7 @@ interface Backend {
|
||||
// Prompt methods
|
||||
prompt_exists(name string) !bool
|
||||
prompt_get(name string) !Prompt
|
||||
prompt_call(name string, arguments []string) ![]PromptMessage
|
||||
prompt_list() ![]Prompt
|
||||
prompt_messages_get(name string, arguments map[string]string) ![]PromptMessage
|
||||
|
||||
@@ -13,6 +13,7 @@ pub mut:
|
||||
// Prompt related fields
|
||||
prompts map[string]Prompt
|
||||
prompt_messages map[string][]PromptMessage
|
||||
prompt_handlers map[string]PromptHandler
|
||||
|
||||
// Tool related fields
|
||||
tools map[string]Tool
|
||||
@@ -20,6 +21,9 @@ pub mut:
|
||||
}
|
||||
|
||||
pub type ToolHandler = fn (arguments map[string]json2.Any) !ToolCallResult
|
||||
|
||||
pub type PromptHandler = fn (arguments []string) ![]PromptMessage
|
||||
|
||||
fn (b &MemoryBackend) resource_exists(uri string) !bool {
|
||||
return uri in b.resources
|
||||
}
|
||||
@@ -105,6 +109,16 @@ fn (b &MemoryBackend) prompt_messages_get(name string, arguments map[string]stri
|
||||
return messages
|
||||
}
|
||||
|
||||
|
||||
fn (b &MemoryBackend) prompt_call(name string, arguments []string) ![]PromptMessage {
|
||||
// Get the tool handler
|
||||
handler := b.prompt_handlers[name] or { return error('tool handler not found') }
|
||||
|
||||
// Call the handler with the provided arguments
|
||||
return handler(arguments) or {panic(err)}
|
||||
}
|
||||
|
||||
|
||||
// Tool related methods
|
||||
|
||||
fn (b &MemoryBackend) tool_exists(name string) !bool {
|
||||
68
lib/mcp/cmd/compile.vsh
Executable file
68
lib/mcp/cmd/compile.vsh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env -S v -n -cg -w -parallel-cc -enable-globals run
|
||||
|
||||
import os
|
||||
import flag
|
||||
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application('compile.vsh')
|
||||
fp.version('v0.1.0')
|
||||
fp.description('Compile MCP binary in debug or production mode')
|
||||
fp.skip_executable()
|
||||
|
||||
prod_mode := fp.bool('prod', `p`, false, 'Build production version (optimized)')
|
||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||
|
||||
if help_requested {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
additional_args := fp.finalize() or {
|
||||
eprintln(err)
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if additional_args.len > 0 {
|
||||
eprintln('Unexpected arguments: ${additional_args.join(' ')}')
|
||||
println(fp.usage())
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Change to the mcp directory
|
||||
mcp_dir := os.dir(os.real_path(os.executable()))
|
||||
os.chdir(mcp_dir) or { panic('Failed to change directory to ${mcp_dir}: ${err}') }
|
||||
|
||||
// Set MCPPATH based on OS
|
||||
mut mcppath := '/usr/local/bin/mcp'
|
||||
if os.user_os() == 'macos' {
|
||||
mcppath = os.join_path(os.home_dir(), 'hero/bin/mcp')
|
||||
}
|
||||
|
||||
// Set compilation command based on OS and mode
|
||||
compile_cmd := if prod_mode {
|
||||
'v -enable-globals -w -n -prod mcp.v'
|
||||
} else {
|
||||
'v -w -cg -gc none -cc tcc -d use_openssl -enable-globals mcp.v'
|
||||
}
|
||||
|
||||
println('Building MCP in ${if prod_mode { 'production' } else { 'debug' }} mode...')
|
||||
|
||||
if os.system(compile_cmd) != 0 {
|
||||
panic('Failed to compile mcp.v with command: ${compile_cmd}')
|
||||
}
|
||||
|
||||
// Make executable
|
||||
os.chmod('mcp', 0o755) or { panic('Failed to make mcp binary executable: ${err}') }
|
||||
|
||||
// Ensure destination directory exists
|
||||
os.mkdir_all(os.dir(mcppath)) or { panic('Failed to create directory ${os.dir(mcppath)}: ${err}') }
|
||||
|
||||
// Copy to destination paths
|
||||
os.cp('mcp', mcppath) or { panic('Failed to copy mcp binary to ${mcppath}: ${err}') }
|
||||
os.cp('mcp', '/tmp/mcp') or { panic('Failed to copy mcp binary to /tmp/mcp: ${err}') }
|
||||
|
||||
// Clean up
|
||||
os.rm('mcp') or { panic('Failed to remove temporary mcp binary: ${err}') }
|
||||
|
||||
println('**MCP COMPILE OK**')
|
||||
92
lib/mcp/cmd/mcp.v
Normal file
92
lib/mcp/cmd/mcp.v
Normal file
@@ -0,0 +1,92 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import cli { Command, Flag }
|
||||
import freeflowuniverse.herolib.osal
|
||||
// import freeflowuniverse.herolib.mcp.vcode
|
||||
// import freeflowuniverse.herolib.mcp.mcpgen
|
||||
// import freeflowuniverse.herolib.mcp.baobab
|
||||
import freeflowuniverse.herolib.mcp.rhai.mcp as rhai_mcp
|
||||
|
||||
fn main() {
|
||||
do() or { panic(err) }
|
||||
}
|
||||
|
||||
pub fn do() ! {
|
||||
mut cmd_mcp := Command{
|
||||
name: 'mcp'
|
||||
usage: '
|
||||
## Manage your MCPs
|
||||
|
||||
example:
|
||||
|
||||
mcp
|
||||
'
|
||||
description: 'create, edit, show mdbooks'
|
||||
required_args: 0
|
||||
}
|
||||
|
||||
// cmd_run_add_flags(mut cmd_publisher)
|
||||
|
||||
cmd_mcp.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'debug'
|
||||
abbrev: 'd'
|
||||
description: 'show debug output'
|
||||
})
|
||||
|
||||
cmd_mcp.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'verbose'
|
||||
abbrev: 'v'
|
||||
description: 'show verbose output'
|
||||
})
|
||||
|
||||
mut cmd_inspector := cli.Command{
|
||||
sort_flags: true
|
||||
name: 'inspector'
|
||||
execute: cmd_inspector_execute
|
||||
description: 'will list existing mdbooks'
|
||||
}
|
||||
|
||||
cmd_inspector.add_flag(Flag{
|
||||
flag: .string
|
||||
required: false
|
||||
name: 'name'
|
||||
abbrev: 'n'
|
||||
description: 'name of the MCP'
|
||||
})
|
||||
|
||||
cmd_inspector.add_flag(Flag{
|
||||
flag: .bool
|
||||
required: false
|
||||
name: 'open'
|
||||
abbrev: 'o'
|
||||
description: 'open inspector'
|
||||
})
|
||||
|
||||
|
||||
cmd_mcp.add_command(rhai_mcp.command)
|
||||
// cmd_mcp.add_command(baobab.command)
|
||||
// cmd_mcp.add_command(vcode.command)
|
||||
cmd_mcp.add_command(cmd_inspector)
|
||||
// cmd_mcp.add_command(vcode.command)
|
||||
cmd_mcp.setup()
|
||||
cmd_mcp.parse(os.args)
|
||||
}
|
||||
|
||||
fn cmd_inspector_execute(cmd cli.Command) ! {
|
||||
open := cmd.flags.get_bool('open') or { false }
|
||||
if open {
|
||||
osal.exec(cmd: 'open http://localhost:5173')!
|
||||
}
|
||||
name := cmd.flags.get_string('name') or { '' }
|
||||
if name.len > 0 {
|
||||
println('starting inspector for MCP ${name}')
|
||||
osal.exec(cmd: 'npx @modelcontextprotocol/inspector mcp ${name} start')!
|
||||
} else {
|
||||
osal.exec(cmd: 'npx @modelcontextprotocol/inspector')!
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import log
|
||||
import x.json2
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
@[params]
|
||||
pub struct ServerParams {
|
||||
@@ -38,6 +37,7 @@ pub fn new_server(backend Backend, params ServerParams) !&Server {
|
||||
// Prompt handlers
|
||||
'prompts/list': server.prompts_list_handler,
|
||||
'prompts/get': server.prompts_get_handler,
|
||||
'completion/complete': server.prompts_get_handler,
|
||||
|
||||
// Tool handlers
|
||||
'tools/list': server.tools_list_handler,
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import log
|
||||
import x.json2
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
// initialize_handler handles the initialize request according to the MCP specification
|
||||
fn (mut s Server) initialize_handler(data string) !string {
|
||||
@@ -6,7 +6,6 @@ import log
|
||||
import x.json2
|
||||
import json
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
// Prompt related structs
|
||||
|
||||
@@ -88,27 +87,30 @@ pub:
|
||||
// This request is used to retrieve a specific prompt with arguments
|
||||
fn (mut s Server) prompts_get_handler(data string) !string {
|
||||
// Decode the request with name and arguments parameters
|
||||
request := jsonrpc.decode_request_generic[PromptGetParams](data)!
|
||||
request_map := json2.raw_decode(data)!.as_map()
|
||||
params_map := request_map['params'].as_map()
|
||||
|
||||
if !s.backend.prompt_exists(request.params.name)! {
|
||||
return jsonrpc.new_error_response(request.id, prompt_not_found(request.params.name)).encode()
|
||||
if !s.backend.prompt_exists(params_map['name'].str())! {
|
||||
return jsonrpc.new_error_response(request_map['id'].int(), prompt_not_found(params_map['name'].str())).encode()
|
||||
}
|
||||
|
||||
// Get the prompt by name
|
||||
prompt := s.backend.prompt_get(request.params.name)!
|
||||
prompt := s.backend.prompt_get(params_map['name'].str())!
|
||||
|
||||
// Validate required arguments
|
||||
for arg in prompt.arguments {
|
||||
if arg.required && request.params.arguments[arg.name] == '' {
|
||||
return jsonrpc.new_error_response(request.id, missing_required_argument(arg.name)).encode()
|
||||
if arg.required && params_map['arguments'].as_map()[arg.name].str() == '' {
|
||||
return jsonrpc.new_error_response(request_map['id'].int(), missing_required_argument(arg.name)).encode()
|
||||
}
|
||||
}
|
||||
|
||||
// Get the prompt messages with arguments applied
|
||||
messages := s.backend.prompt_messages_get(request.params.name, request.params.arguments)!
|
||||
messages := s.backend.prompt_call(params_map['name'].str(), params_map['arguments'].as_map().values().map(it.str()))!
|
||||
|
||||
// // Get the prompt messages with arguments applied
|
||||
// messages := s.backend.prompt_messages_get(request.params.name, request.params.arguments)!
|
||||
|
||||
// Create a success response with the result
|
||||
response := jsonrpc.new_response_generic[PromptGetResult](request.id, PromptGetResult{
|
||||
response := jsonrpc.new_response_generic[PromptGetResult](request_map['id'].int(), PromptGetResult{
|
||||
description: prompt.description
|
||||
messages: messages
|
||||
})
|
||||
@@ -6,7 +6,6 @@ import log
|
||||
import x.json2
|
||||
import json
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
pub struct Resource {
|
||||
pub:
|
||||
@@ -7,7 +7,6 @@ import x.json2
|
||||
import json
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
// Tool related structs
|
||||
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import log
|
||||
import x.json2
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
const protocol_version = '2024-11-05'
|
||||
// MCP server implementation using stdio transport
|
||||
18
lib/mcp/rhai/cmd/main.v
Normal file
18
lib/mcp/rhai/cmd/main.v
Normal file
@@ -0,0 +1,18 @@
|
||||
module main
|
||||
|
||||
import freeflowuniverse.herolib.mcp.rhai.mcp
|
||||
import log
|
||||
|
||||
fn main() {
|
||||
// Create a new MCP server
|
||||
mut server := mcp.new_mcp_server() or {
|
||||
log.error('Failed to create MCP server: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
// Start the server
|
||||
server.start() or {
|
||||
log.error('Failed to start MCP server: ${err}')
|
||||
return
|
||||
}
|
||||
}
|
||||
0
lib/mcp/servers/rhai/example/example copy.vsh → lib/mcp/rhai/example/example copy.vsh
Executable file → Normal file
0
lib/mcp/servers/rhai/example/example copy.vsh → lib/mcp/rhai/example/example copy.vsh
Executable file → Normal file
279
lib/mcp/rhai/logic/logic.v
Normal file
279
lib/mcp/rhai/logic/logic.v
Normal file
@@ -0,0 +1,279 @@
|
||||
module logic
|
||||
|
||||
import freeflowuniverse.herolib.ai.escalayer
|
||||
import freeflowuniverse.herolib.lang.rust
|
||||
import freeflowuniverse.herolib.ai.utils
|
||||
import os
|
||||
|
||||
pub fn generate_rhai_wrapper(name string, source_path string) !string {
|
||||
prompt := rhai_wrapper_generation_prompt(name, source_path) or {panic(err)}
|
||||
return run_wrapper_generation_task(prompt, RhaiGen{
|
||||
name: name
|
||||
dir: source_path
|
||||
}) or {panic(err)}
|
||||
}
|
||||
|
||||
// Runs the task to generate Rhai wrappers
|
||||
pub fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string {
|
||||
// Create a new task
|
||||
mut task := escalayer.new_task(
|
||||
name: 'rhai_wrapper_creator.escalayer'
|
||||
description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
|
||||
)
|
||||
|
||||
// Create model configs
|
||||
sonnet_model := escalayer.ModelConfig{
|
||||
name: 'anthropic/claude-3.7-sonnet'
|
||||
provider: 'anthropic'
|
||||
temperature: 0.7
|
||||
max_tokens: 25000
|
||||
}
|
||||
|
||||
gpt4_model := escalayer.ModelConfig{
|
||||
name: 'gpt-4'
|
||||
provider: 'openai'
|
||||
temperature: 0.7
|
||||
max_tokens: 25000
|
||||
}
|
||||
|
||||
// Create a prompt function that returns the prepared content
|
||||
prompt_function := fn [prompt_content] (input string) string {
|
||||
return prompt_content
|
||||
}
|
||||
|
||||
// Define a single unit task that handles everything
|
||||
task.new_unit_task(
|
||||
name: 'create_rhai_wrappers'
|
||||
prompt_function: prompt_function
|
||||
callback_function: gen.process_rhai_wrappers
|
||||
base_model: sonnet_model
|
||||
retry_model: gpt4_model
|
||||
retry_count: 1
|
||||
)
|
||||
|
||||
// Initiate the task
|
||||
return task.initiate('')
|
||||
}
|
||||
|
||||
// Define a Rhai wrapper generator function for Container functions
|
||||
pub fn rhai_wrapper_generation_prompt(name string, source_code string) !string {
|
||||
current_dir := os.dir(@FILE)
|
||||
example_rhai := os.read_file('${current_dir}/prompts/example_script.md') or {panic(err)}
|
||||
wrapper_md := os.read_file('${current_dir}/prompts/wrapper.md') or {panic(err)}
|
||||
errors_md := os.read_file('${current_dir}/prompts/errors.md') or {panic(err)}
|
||||
|
||||
// Load all required template and guide files
|
||||
guides := os.read_file('/Users/timurgordon/code/git.ourworld.tf/herocode/sal/aiprompts/rhaiwrapping_classicai.md')!
|
||||
engine := $tmpl('./prompts/engine.md')
|
||||
vector_vs_array := os.read_file('/Users/timurgordon/code/git.ourworld.tf/herocode/sal/aiprompts/rhai_array_vs_vector.md')!
|
||||
rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.ourworld.tf/herocode/sal/aiprompts/rhai_integration_fixes.md')!
|
||||
rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.ourworld.tf/herocode/sal/aiprompts/rhai_syntax_guide.md')!
|
||||
generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs')
|
||||
|
||||
prompt := $tmpl('./prompts/main.md')
|
||||
return prompt
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct WrapperModule {
|
||||
pub:
|
||||
lib_rs string
|
||||
example_rs string
|
||||
engine_rs string
|
||||
cargo_toml string
|
||||
example_rhai string
|
||||
generic_wrapper_rs string
|
||||
wrapper_rs string
|
||||
}
|
||||
|
||||
// functions is a list of function names that AI should extract and pass in
|
||||
pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string)! string {
|
||||
|
||||
// Define project directory paths
|
||||
project_dir := '${path}/rhai'
|
||||
|
||||
// Create the project using cargo new --lib
|
||||
if os.exists(project_dir) {
|
||||
os.rmdir_all(project_dir) or {
|
||||
return error('Failed to clean existing project directory: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Run cargo new --lib to create the project
|
||||
os.chdir(path) or {
|
||||
return error('Failed to change directory to base directory: ${err}')
|
||||
}
|
||||
|
||||
cargo_new_result := os.execute('cargo new --lib rhai')
|
||||
if cargo_new_result.exit_code != 0 {
|
||||
return error('Failed to create new library project: ${cargo_new_result.output}')
|
||||
}
|
||||
|
||||
// Create examples directory
|
||||
examples_dir := '${project_dir}/examples'
|
||||
os.mkdir_all(examples_dir) or {
|
||||
return error('Failed to create examples directory: ${err}')
|
||||
}
|
||||
|
||||
// Write the lib.rs file
|
||||
if wrapper.lib_rs != '' {
|
||||
os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or {
|
||||
return error('Failed to write lib.rs: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Write the wrapper.rs file
|
||||
if wrapper.wrapper_rs != '' {
|
||||
os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or {
|
||||
return error('Failed to write wrapper.rs: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Write the generic wrapper.rs file
|
||||
if wrapper.generic_wrapper_rs != '' {
|
||||
os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or {
|
||||
return error('Failed to write generic wrapper.rs: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Write the example.rs file
|
||||
if wrapper.example_rs != '' {
|
||||
os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or {
|
||||
return error('Failed to write example.rs: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Write the engine.rs file if provided
|
||||
if wrapper.engine_rs != '' {
|
||||
os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or {
|
||||
return error('Failed to write engine.rs: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Write the Cargo.toml file
|
||||
os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or {
|
||||
return error('Failed to write Cargo.toml: ${err}')
|
||||
}
|
||||
|
||||
// Write the example.rhai file
|
||||
os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or {
|
||||
return error('Failed to write example.rhai: ${err}')
|
||||
}
|
||||
|
||||
return project_dir
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Extract module name from wrapper code
|
||||
fn extract_module_name(code string) string {
|
||||
lines := code.split('\n')
|
||||
|
||||
for line in lines {
|
||||
// Look for pub mod or mod declarations
|
||||
if line.contains('pub mod ') || line.contains('mod ') {
|
||||
// Extract module name
|
||||
mut parts := []string{}
|
||||
if line.contains('pub mod ') {
|
||||
parts = line.split('pub mod ')
|
||||
} else {
|
||||
parts = line.split('mod ')
|
||||
}
|
||||
|
||||
if parts.len > 1 {
|
||||
// Extract the module name and remove any trailing characters
|
||||
mut name := parts[1].trim_space()
|
||||
// Remove any trailing { or ; or whitespace
|
||||
name = name.trim_right('{').trim_right(';').trim_space()
|
||||
if name != '' {
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
// RhaiGen struct for generating Rhai wrappers
|
||||
struct RhaiGen {
|
||||
name string
|
||||
dir string
|
||||
}
|
||||
|
||||
// Process the AI response and compile the generated code
|
||||
fn (gen RhaiGen) process_rhai_wrappers(response string)! string {
|
||||
// Extract code blocks from the response
|
||||
code_blocks := extract_code_blocks(response) or {
|
||||
return err
|
||||
}
|
||||
|
||||
name := gen.name
|
||||
|
||||
// Create a WrapperModule struct with the extracted content
|
||||
wrapper := WrapperModule{
|
||||
lib_rs: $tmpl('./templates/lib.rs')
|
||||
wrapper_rs: code_blocks.wrapper_rs
|
||||
example_rs: $tmpl('./templates/example.rs')
|
||||
engine_rs: code_blocks.engine_rs
|
||||
generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs')
|
||||
cargo_toml: $tmpl('./templates/cargo.toml')
|
||||
example_rhai: code_blocks.example_rhai
|
||||
}
|
||||
|
||||
// Create the wrapper module
|
||||
project_dir := write_rhai_wrapper_module(wrapper, gen.name, gen.dir) or {
|
||||
return error('Failed to create wrapper module: ${err}')
|
||||
}
|
||||
|
||||
// Build and run the project
|
||||
build_output, run_output := rust.run_example(project_dir, 'example') or {
|
||||
return err
|
||||
}
|
||||
|
||||
return format_success_message(project_dir, build_output, run_output)
|
||||
}
|
||||
|
||||
// CodeBlocks struct to hold extracted code blocks
|
||||
struct CodeBlocks {
|
||||
wrapper_rs string
|
||||
engine_rs string
|
||||
example_rhai string
|
||||
}
|
||||
|
||||
// Extract code blocks from the AI response
|
||||
fn extract_code_blocks(response string)! CodeBlocks {
|
||||
// Extract wrapper.rs content
|
||||
wrapper_rs_content := utils.extract_code_block(response, 'wrapper.rs', 'rust')
|
||||
if wrapper_rs_content == '' {
|
||||
return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```')
|
||||
}
|
||||
|
||||
// Extract engine.rs content
|
||||
mut engine_rs_content := utils.extract_code_block(response, 'engine.rs', 'rust')
|
||||
if engine_rs_content == '' {
|
||||
// Try to extract from the response without explicit language marker
|
||||
engine_rs_content = utils.extract_code_block(response, 'engine.rs', '')
|
||||
}
|
||||
|
||||
// Extract example.rhai content
|
||||
mut example_rhai_content := utils.extract_code_block(response, 'example.rhai', 'rhai')
|
||||
if example_rhai_content == '' {
|
||||
// Try to extract from the response without explicit language marker
|
||||
example_rhai_content = utils.extract_code_block(response, 'example.rhai', '')
|
||||
if example_rhai_content == '' {
|
||||
return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```')
|
||||
}
|
||||
}
|
||||
|
||||
return CodeBlocks{
|
||||
wrapper_rs: wrapper_rs_content
|
||||
engine_rs: engine_rs_content
|
||||
example_rhai: example_rhai_content
|
||||
}
|
||||
}
|
||||
|
||||
// Format success message
|
||||
fn format_success_message(project_dir string, build_output string, run_output string) string {
|
||||
return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}'
|
||||
}
|
||||
101
lib/mcp/rhai/logic/prompts/main.md
Normal file
101
lib/mcp/rhai/logic/prompts/main.md
Normal file
@@ -0,0 +1,101 @@
|
||||
You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files.
|
||||
@{guides}
|
||||
@{vector_vs_array}
|
||||
@{example_rhai}
|
||||
@{wrapper_md}
|
||||
|
||||
## Common Errors to Avoid
|
||||
@{errors_md}
|
||||
@{rhai_integration_fixes}
|
||||
@{rhai_syntax_guide}
|
||||
|
||||
## Your Task
|
||||
|
||||
Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers:
|
||||
|
||||
## Rust Code to Wrap
|
||||
|
||||
```rust
|
||||
@{source_code}
|
||||
```
|
||||
|
||||
IMPORTANT NOTES:
|
||||
1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use
|
||||
2. The following dependencies are available in Cargo.toml:
|
||||
- rhai = "1.21.0"
|
||||
- serde = { version = "1.0", features = ["derive"] }
|
||||
- serde_json = "1.0"
|
||||
- sal = { path = "../../../" }
|
||||
|
||||
3. For the wrapper: `use sal::@{name};` this way you can access the module functions and objects with @{name}::
|
||||
|
||||
4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there.
|
||||
|
||||
```rust
|
||||
@{generic_wrapper_rs}
|
||||
```
|
||||
|
||||
5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary.
|
||||
- For example, return `Result<String, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a string
|
||||
- Use `Result<bool, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a boolean
|
||||
- Use `Result<Vec<String>, Box<EvalAltResult>>` instead of `Dynamic` when a function returns a list of strings
|
||||
|
||||
6. Your code should include public functions that can be called from Rhai scripts
|
||||
|
||||
7. Make sure to implement all necessary helper functions for type conversion
|
||||
|
||||
8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine
|
||||
|
||||
9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings
|
||||
|
||||
10. When returning path references, convert them to owned strings (e.g., path().to_string())
|
||||
|
||||
11. For error handling, use proper Result types with Box<EvalAltResult> for the error type:
|
||||
```rust
|
||||
// INCORRECT:
|
||||
pub fn some_function(arg: &str) -> Dynamic {
|
||||
match some_operation(arg) {
|
||||
Ok(result) => Dynamic::from(result),
|
||||
Err(err) => Dynamic::from(format!("Error: {}", err))
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT:
|
||||
pub fn some_function(arg: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
some_operation(arg).map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Error: {}", err).into(),
|
||||
rhai::Position::NONE
|
||||
))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
12. IMPORTANT: Format your response with the code between triple backticks as follows:
|
||||
|
||||
```rust
|
||||
// wrapper.rs
|
||||
// Your wrapper implementation here
|
||||
```
|
||||
|
||||
```rust
|
||||
// engine.rs
|
||||
// Your engine.rs implementation here
|
||||
```
|
||||
|
||||
```rhai
|
||||
// example.rhai
|
||||
// Your example Rhai script here
|
||||
```
|
||||
|
||||
13. The example.rhai script should demonstrate the use of all the wrapper functions you create
|
||||
|
||||
14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example:
|
||||
|
||||
@{engine}
|
||||
|
||||
MOST IMPORTANT:
|
||||
import package being wrapped as `use sal::<n>`
|
||||
your engine create function is called `create_rhai_engine`
|
||||
|
||||
```
|
||||
23
lib/mcp/rhai/mcp/command.v
Normal file
23
lib/mcp/rhai/mcp/command.v
Normal file
@@ -0,0 +1,23 @@
|
||||
module mcp
|
||||
|
||||
import cli
|
||||
|
||||
pub const command := cli.Command{
|
||||
sort_flags: true
|
||||
name: 'rhai'
|
||||
// execute: cmd_mcpgen
|
||||
description: 'rhai command'
|
||||
commands: [
|
||||
cli.Command{
|
||||
name: 'start'
|
||||
execute: cmd_start
|
||||
description: 'start the Rhai server'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
fn cmd_start(cmd cli.Command) ! {
|
||||
mut server := new_mcp_server()!
|
||||
server.start()!
|
||||
}
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
module pugconvert
|
||||
module mcp
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import log
|
||||
|
||||
pub fn new_mcp_server() !&mcp.Server {
|
||||
logger.info('Creating new Rhai MCP server')
|
||||
log.info('Creating new Developer MCP server')
|
||||
|
||||
// Initialize the server with the empty handlers map
|
||||
mut server := mcp.new_server(mcp.MemoryBackend{
|
||||
tools: {
|
||||
'rhai_interface': specs
|
||||
'generate_rhai_wrapper': generate_rhai_wrapper_spec
|
||||
}
|
||||
tool_handlers: {
|
||||
'rhai_interface': handler
|
||||
'generate_rhai_wrapper': generate_rhai_wrapper_handler
|
||||
}
|
||||
prompts: {
|
||||
'rhai_wrapper': rhai_wrapper_prompt_spec
|
||||
}
|
||||
prompt_handlers: {
|
||||
'rhai_wrapper': rhai_wrapper_prompt_handler
|
||||
}
|
||||
}, mcp.ServerParams{
|
||||
config: mcp.ServerConfiguration{
|
||||
server_info: mcp.ServerInfo{
|
||||
name: 'developer'
|
||||
name: 'rhai'
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
})!
|
||||
return server
|
||||
}
|
||||
}
|
||||
42
lib/mcp/rhai/mcp/prompts.v
Normal file
42
lib/mcp/rhai/mcp/prompts.v
Normal file
@@ -0,0 +1,42 @@
|
||||
module mcp
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.mcp.rhai.logic
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import freeflowuniverse.herolib.lang.rust
|
||||
import x.json2 as json { Any }
|
||||
|
||||
// Tool definition for the create_rhai_wrapper function
|
||||
const rhai_wrapper_prompt_spec = mcp.Prompt{
|
||||
name: 'rhai_wrapper'
|
||||
description: 'provides a prompt for creating Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file'
|
||||
arguments: [
|
||||
mcp.PromptArgument{
|
||||
name: 'source_path'
|
||||
description: 'Path to the source directory'
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Tool handler for the create_rhai_wrapper function
|
||||
pub fn rhai_wrapper_prompt_handler(arguments []string) ![]mcp.PromptMessage {
|
||||
source_path := arguments[0]
|
||||
|
||||
// Read and combine all Rust files in the source directory
|
||||
source_code := rust.read_source_code(source_path)!
|
||||
|
||||
// Extract the module name from the directory path (last component)
|
||||
name := rust.extract_module_name_from_path(source_path)
|
||||
|
||||
|
||||
result := logic.rhai_wrapper_generation_prompt(name, source_code)!
|
||||
return [mcp.PromptMessage{
|
||||
role: 'assistant'
|
||||
content: mcp.PromptContent{
|
||||
typ: 'text'
|
||||
text: result
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
module pugconvert
|
||||
module mcp
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import x.json2 as json { Any }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
import log
|
||||
|
||||
const specs = mcp.Tool{
|
||||
name: 'rhai_interface'
|
||||
39
lib/mcp/rhai/mcp/tools.v
Normal file
39
lib/mcp/rhai/mcp/tools.v
Normal file
@@ -0,0 +1,39 @@
|
||||
module mcp
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.mcp.rhai.logic
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import x.json2 as json { Any }
|
||||
|
||||
// Tool definition for the generate_rhai_wrapper function
|
||||
const generate_rhai_wrapper_spec = mcp.Tool{
|
||||
name: 'generate_rhai_wrapper'
|
||||
description: 'generate_rhai_wrapper receives the name of a V language function string, and the path to the module in which it exists.'
|
||||
input_schema: jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'source_path': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['name', 'source_path']
|
||||
}
|
||||
}
|
||||
|
||||
// Tool handler for the generate_rhai_wrapper function
|
||||
pub fn generate_rhai_wrapper_handler(arguments map[string]Any) !mcp.ToolCallResult {
|
||||
name := arguments['name'].str()
|
||||
source_path := arguments['source_path'].str()
|
||||
result := logic.generate_rhai_wrapper(name, source_path)
|
||||
or {
|
||||
return mcp.error_tool_call_result(err)
|
||||
}
|
||||
return mcp.ToolCallResult{
|
||||
is_error: false
|
||||
content: mcp.result_to_mcp_tool_contents[string](result)
|
||||
}
|
||||
}
|
||||
1
lib/mcp/rhai/rhai.v
Normal file
1
lib/mcp/rhai/rhai.v
Normal file
@@ -0,0 +1 @@
|
||||
module rhai
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import log
|
||||
import x.json2
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
|
||||
// Server is the main MCP server struct
|
||||
@[heap]
|
||||
@@ -19,7 +18,7 @@ pub mut:
|
||||
|
||||
// start starts the MCP server
|
||||
pub fn (mut s Server) start() ! {
|
||||
logger.info('Starting MCP server')
|
||||
log.info('Starting MCP server')
|
||||
for {
|
||||
// Read a message from stdin
|
||||
message := os.get_line()
|
||||
@@ -30,6 +29,7 @@ pub fn (mut s Server) start() ! {
|
||||
|
||||
// Handle the message using the JSON-RPC handler
|
||||
response := s.handler.handle(message) or {
|
||||
log.error('message: ${message}')
|
||||
log.error('Error handling message: ${err}')
|
||||
|
||||
// Try to extract the request ID
|
||||
@@ -49,6 +49,7 @@ pub fn (mut s Server) start() ! {
|
||||
// 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()
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
module main
|
||||
|
||||
import freeflowuniverse.herolib.mcp.pugconvert
|
||||
|
||||
fn main() {
|
||||
// Create a new MCP server
|
||||
mut server := pugconvert.new_mcp_server() or {
|
||||
eprintln('Failed to create MCP server: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
// Start the server
|
||||
server.start() or {
|
||||
eprintln('Failed to start MCP server: ${err}')
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
module rhaiconvert
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import x.json2 as json { Any }
|
||||
import freeflowuniverse.herolib.mcp.servers.rhai.logic as rhaido
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
//TODO: implement
|
||||
|
||||
pub fn handler(arguments map[string]Any) !mcp.ToolCallResult {
|
||||
path := arguments['path'].str()
|
||||
|
||||
// Check if path exists
|
||||
if !os.exists(path) {
|
||||
return mcp.ToolCallResult{
|
||||
is_error: true
|
||||
content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if path is a file or directory
|
||||
is_directory := os.is_dir(path)
|
||||
|
||||
mut message := ""
|
||||
|
||||
//TODO: implement
|
||||
|
||||
if is_directory {
|
||||
// Convert all rhai files in the directory
|
||||
rhaido.convert_rhai(path) or {
|
||||
return mcp.ToolCallResult{
|
||||
is_error: true
|
||||
content: mcp.result_to_mcp_tool_contents[string]("Error converting rhai files in directory: ${err}")
|
||||
}
|
||||
}
|
||||
message = "Successfully converted all rhai files in directory '${path}'"
|
||||
} else if path.ends_with(".rhai") {
|
||||
// Convert a single rhai file
|
||||
rhaido.convert_rhai_file(path) or {
|
||||
return mcp.ToolCallResult{
|
||||
is_error: true
|
||||
content: mcp.result_to_mcp_tool_contents[string]("Error converting rhai file: ${err}")
|
||||
}
|
||||
}
|
||||
message = "Successfully converted rhai file '${path}'"
|
||||
} else {
|
||||
return mcp.ToolCallResult{
|
||||
is_error: true
|
||||
content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' is not a directory or .rhai file")
|
||||
}
|
||||
}
|
||||
|
||||
return mcp.ToolCallResult{
|
||||
is_error: false
|
||||
content: mcp.result_to_mcp_tool_contents[string](message)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module main
|
||||
|
||||
import freeflowuniverse.herolib.mcp.servers.vcode
|
||||
import freeflowuniverse.herolib.mcp.vcode
|
||||
|
||||
fn main() {
|
||||
// Create a new MCP server
|
||||
Reference in New Issue
Block a user