apexe 0.2.0

Outside-In CLI-to-Agent Bridge
Documentation
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# apexe Examples

> Hands-on examples for every apexe use case — from 30-second quick start to full Rust library integration.

## Overview

| Example | Type | What You'll Learn | Run |
|---------|------|-------------------|-----|
| [basic]basic/ | Shell script | Scan a tool, inspect results, start MCP server | `./examples/basic/run.sh` |
| [programmatic]programmatic.rs | Rust code | Use apexe as a library: scan → convert → export → serve | `cargo run --example programmatic` |

## Prerequisites

```bash
# From the repo root
cargo install --path .
apexe --version
# apexe 0.1.0
```

---

## Example 1: basic — Full CLI Walkthrough

### What it does

A shell script that walks through the complete apexe workflow:

```
scan git → write bindings → write ACL → list modules → print configs → start server
```

### Run it

```bash
cd examples/basic
./run.sh
```

### Expected output

```
=== Step 1: Scan git ===
Tool: git (2.39.5)
  Binary: /usr/bin/git
  Scan tier: 2
  Subcommands: 12
  Global flags: 3

=== Step 2: List generated modules ===
MODULE ID                                DESCRIPTION
──────────────────────────────────────── ────────────────────────────────────────
cli.git.add                              Add file contents to the index
cli.git.commit                           Record changes to the repository
cli.git.push                             Update remote refs
cli.git.status                           Show the working tree status
...

12 module(s) found.

=== Step 3: Inspect ACL ===
rules:
  - callers: ["*"]
    targets: ["cli.git.status", "cli.git.log", "cli.git.diff"]
    effect: allow
    description: Auto-allow readonly CLI commands
  - callers: ["*"]
    targets: ["cli.git.rm"]
    effect: deny
    description: Block destructive CLI commands by default
    conditions:
      require_approval: true
default_effect: deny
```

### Tip: Force re-scan for better results

If git shows 0 subcommands, the cache has a stale result. Force re-scan:

```bash
apexe scan git --no-cache --depth 3
```

### Generated files

```
output/
  modules/
    cli.git.binding.yaml     # JSON Schema for every git subcommand
~/.apexe/
  acl.yaml                   # Access control rules
```

---

## Example 2: programmatic — Rust Library API

### What it does

Shows how to embed apexe in your own Rust application:

1. Scan CLI tools programmatically
2. Convert scan results to apcore `ScannedModule`
3. Write binding YAML files
4. Export OpenAI-compatible tool definitions
5. Build an MCP server (without starting it)

### Run it

```bash
cargo run --example programmatic
```

### Expected output

```
=== Scanning 'echo' (a simple tool for demonstration) ===
Scanned: echo (tier 1, 0 subcommands, 0 global flags)

=== Converting to ScannedModules ===
Module: cli.echo — Execute echo
  readonly=false, destructive=false, requires_approval=false
  display alias: echo

=== Writing binding files ===
Written: /Users/you/.apexe/modules/cli.echo.binding.yaml

=== Exporting OpenAI-compatible tool definitions ===
1 tool(s) exported:
[
  {
    "function": {
      "description": "Execute echo",
      "name": "cli-echo",
      "parameters": { "type": "object", "properties": {} }
    },
    "type": "function"
  }
]

=== Building MCP server (not starting) ===
MCP server built successfully. Call server.serve() to start.
```

### Key code snippets

**Scan a tool:**
```rust
use apexe::config::ApexeConfig;
use apexe::scanner::ScanOrchestrator;

let config = ApexeConfig::default();
let orchestrator = ScanOrchestrator::new(config);
let tools = orchestrator.scan(&["git".to_string()], false, 2)?;
```

**Convert to ScannedModules:**
```rust
use apexe::adapter::CliToolConverter;

let converter = CliToolConverter::new();
let modules = converter.convert_all(&tools);
// Each module has: module_id, input_schema, output_schema, annotations, display metadata
```

**Write binding YAML:**
```rust
use apexe::output::YamlOutput;

let yaml = YamlOutput::new(); // with verification
let results = yaml.write(&modules, output_dir, false)?;
```

**Export OpenAI function calling format:**
```rust
use apexe::mcp::McpServerBuilder;

let tools = McpServerBuilder::new()
    .modules_dir("/path/to/modules")
    .export_openai_tools()?;
// Returns Vec<serde_json::Value> in OpenAI function_tools format
```

**Build and start MCP server:**
```rust
let server = McpServerBuilder::new()
    .name("my-tools")
    .transport("http")            // or "stdio", "sse"
    .port(8000)
    .explorer(true)               // browser-based tool explorer UI
    .modules_dir("/path/to/modules")
    .enable_logging(true)         // structured logging middleware
    .enable_approval(true)        // approval dialog for destructive commands
    .tags(vec!["readonly".into()]) // only expose readonly tools
    .build()?;

server.serve()?; // blocking — starts the server
```

---

## Common Scenarios

### Scan multiple tools at once

```bash
apexe scan ls jq curl --no-cache
apexe list
# Shows all modules from all tools
```

### Start HTTP server with Explorer UI

```bash
apexe serve --transport http --port 8000 --explorer
# Open http://127.0.0.1:8000/explorer in your browser
```

### Try it: Explorer UI step-by-step

1. **Scan ls, jq, and curl**:

```bash
apexe scan ls jq curl --no-cache
apexe serve --transport http --port 8000 --explorer
```

2. **Open** http://127.0.0.1:8000/explorer in your browser

3. **Click `cli.ls`** → type `{}` → click **Call**. Instant file listing:

```json
{}
```

**Response:**
```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"stdout\":\"Cargo.toml\\nREADME.md\\nsrc\\n...\",\"stderr\":\"\",\"exit_code\":0}"
    }
  ]
}
```

4. **Click `cli.jq`** → 22 form fields! Try:

```json
{"compact_output": true, "raw_output": true}
```

5. **Click `cli.curl`** → 12 form fields. Try:

```json
{"verbose": true, "silent": true}
```

### Try it: Call tools via MCP API (full control)

Start the server in one terminal, then call tools from another:

```bash
# Terminal 1: start server
apexe scan ls jq curl --no-cache
apexe serve --transport http --port 8000 --explorer
```

```bash
# Terminal 2: MCP protocol — initialize handshake
curl -s -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {"name": "curl-test", "version": "1.0"}
    }
  }' | python3 -m json.tool
```

```bash
# List all available tools
curl -s -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }' | python3 -m json.tool
```

```bash
# Execute: list files in current directory (cli.ls)
curl -s -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "cli.ls",
      "arguments": {}
    }
  }' | python3 -m json.tool
```

```bash
# Execute: search for "TODO" in .rs files (cli.grep)
curl -s -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "cli.grep",
      "arguments": {}
    }
  }' | python3 -m json.tool
```

```bash
# Execute via Explorer UI shortcut (simpler format)
curl -s -X POST http://localhost:8000/explorer/tools/cli.ls/call \
  -H "Content-Type: application/json" \
  -d '{}' | python3 -m json.tool
```

```bash
# Execute: find files by name pattern (cli.find)
curl -s -X POST http://localhost:8000/explorer/tools/cli.find/call \
  -H "Content-Type: application/json" \
  -d '{}' | python3 -m json.tool
```

**Expected response** (cli.ls example):

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"stdout\":\"Cargo.toml\\nREADME.md\\nsrc\\ntests\\n...\",\"stderr\":\"\",\"exit_code\":0,\"trace_id\":\"abc-123\",\"duration_ms\":5}"
    }
  ]
}
```

**If you get `AclDenied`**: you're running with `--acl`. Remove it or start without ACL:

```bash
apexe serve --transport http --port 8000 --explorer
# No --acl flag = no access control = all tools allowed
```

### Claude Desktop integration

```bash
# 1. Scan tools
apexe scan git curl grep

# 2. Get integration config
apexe serve --show-config claude-desktop
# Output:
# {
#   "mcpServers": {
#     "apexe": {
#       "command": "apexe",
#       "args": ["serve", "--transport", "stdio"]
#     }
#   }
# }

# 3. Copy to config file
# macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
# Linux: ~/.config/claude/claude_desktop_config.json

# 4. Restart Claude Desktop — tools appear in MCP tool list
```

### Cursor integration

```bash
apexe serve --show-config cursor
# Add the JSON to Cursor's MCP settings
```

### Custom output directory

```bash
apexe scan git --output-dir ./my-project/tools
apexe serve --modules-dir ./my-project/tools
```

### View generated JSON Schema

```bash
apexe scan git --format json | jq '.subcommands[0].flags'
# Shows parsed flags with types, defaults, descriptions
```

### Check what annotations were inferred

```bash
apexe scan git --format yaml | grep -A5 "annotations"
# readonly: true/false, destructive: true/false, requires_approval: true/false
```

---

## Troubleshooting

### `Using cached scan result` with 0 subcommands

Cache has a stale entry. Force re-scan:

```bash
apexe scan git --no-cache --depth 3
```

Or clear the entire cache:

```bash
rm -rf ~/.apexe/cache/
```

### `Tool not found on PATH`

The tool must be installed and accessible:

```bash
which git    # should print a path
apexe scan git
```

### MCP server does nothing in stdio mode

Stdio mode communicates via stdin/stdout — it's meant to be launched by AI agents, not run interactively. Use `--show-config` to get the agent integration snippet, or use HTTP mode for manual testing:

```bash
apexe serve --transport http --port 8000 --explorer
```

### Permission denied in ACL

The default ACL denies destructive and unknown commands. Edit `~/.apexe/acl.yaml`:

```yaml
rules:
  - callers: ["*"]
    targets: ["cli.git.push"]
    effect: allow
    description: "Allow git push"
default_effect: deny
```

---

## Adding Your Own Example

### Rust example

Create a `.rs` file in `examples/` — Cargo auto-discovers it:

```bash
# examples/my_example.rs
cargo run --example my_example
```

Key imports:

```rust
use apexe::adapter::CliToolConverter;
use apexe::config::ApexeConfig;
use apexe::governance::{AclManager, AuditManager};
use apexe::mcp::McpServerBuilder;
use apexe::module::CliModule;
use apexe::output::{YamlOutput, load_modules_from_dir};
use apexe::scanner::ScanOrchestrator;
```

### Shell script example

Create a directory under `examples/` with a `run.sh` and `README.md`:

```
examples/my-scenario/
  README.md
  run.sh        # chmod +x
```