-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogrammatic.rs
More file actions
98 lines (85 loc) · 3.34 KB
/
programmatic.rs
File metadata and controls
98 lines (85 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Programmatic example: use apexe as a library to scan tools, build a
//! filtered MCP server, and export OpenAI-compatible tool definitions.
//!
//! Run with: cargo run --example programmatic
use apexe::adapter::CliToolConverter;
use apexe::config::ApexeConfig;
use apexe::mcp::McpServerBuilder;
use apexe::output::YamlOutput;
use apexe::scanner::ScanOrchestrator;
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().with_env_filter("info").init();
let config = ApexeConfig::default();
config.ensure_dirs()?;
// --- Step 1: Scan a CLI tool ---
println!("=== Scanning 'echo' (a simple tool for demonstration) ===\n");
let orchestrator = ScanOrchestrator::new(config.clone());
let tools = orchestrator.scan(&["echo".to_string()], true, 1)?;
for tool in &tools {
println!(
"Scanned: {} (tier {}, {} subcommands, {} global flags)",
tool.name,
tool.scan_tier,
tool.subcommands.len(),
tool.global_flags.len()
);
}
// --- Step 2: Convert to ScannedModules ---
println!("\n=== Converting to ScannedModules ===\n");
let converter = CliToolConverter::new();
let modules = converter.convert_all(&tools);
for m in &modules {
println!("Module: {} — {}", m.module_id, m.description);
if let Some(ref ann) = m.annotations {
println!(
" readonly={}, destructive={}, requires_approval={}",
ann.readonly, ann.destructive, ann.requires_approval
);
}
if let Some(display) = m.metadata.get("display") {
if let Some(alias) = display.get("alias").and_then(|v| v.as_str()) {
println!(" display alias: {alias}");
}
}
}
// --- Step 3: Write binding YAML ---
println!("\n=== Writing binding files ===\n");
let output_dir = config.modules_dir.clone();
let yaml_output = YamlOutput::new();
let results = yaml_output.write(&modules, &output_dir, false)?;
for wr in &results {
if let Some(ref path) = wr.path {
println!("Written: {path}");
}
}
// --- Step 4: Export OpenAI tools ---
println!("\n=== Exporting OpenAI-compatible tool definitions ===\n");
let openai_tools = McpServerBuilder::new()
.modules_dir(&output_dir)
.export_openai_tools();
match openai_tools {
Ok(tools) => {
println!("{} tool(s) exported:\n", tools.len());
let json = serde_json::to_string_pretty(&tools)?;
// Print first 500 chars to keep output manageable
let truncated: String = json.chars().take(500).collect();
println!("{truncated}...");
}
Err(e) => eprintln!("Export failed: {e}"),
}
// --- Step 5: Build MCP server (don't start it — just show it works) ---
println!("\n=== Building MCP server (not starting) ===\n");
let server = McpServerBuilder::new()
.name("example-server")
.transport("stdio")
.modules_dir(&output_dir)
.enable_logging(true)
.enable_approval(false)
.build();
match server {
Ok(_) => println!("MCP server built successfully. Call server.serve() to start."),
Err(e) => eprintln!("Build failed: {e}"),
}
println!("\nDone. See examples/programmatic/README.md for details.");
Ok(())
}