# CLI Integration
Integrate catenary-mcp with existing AI coding assistants (Claude Code, Gemini
CLI) by disabling their built-in tools and replacing them with catenary's
LSP-backed alternatives.
## Why Not a Custom CLI?
The original plan was to build `catenary-cli` to control the model agent loop.
This was abandoned because:
**Subscription plans are tied to official CLI tools.** Claude Code and Gemini
CLI use subscription billing ($20/month Pro tier). A custom CLI would require
API keys with pay-per-token billing — different billing system, higher cost for
the target audience (individual developers).
**The constraint we wanted is achievable without a custom CLI.** Both tools
support:
1. Disabling built-in tools
2. Adding MCP servers as replacements
3. Workspace-level configuration
We get the same outcome — model forced to use catenary tools — without
maintaining a CLI.
## Design Principles
Preserved from the original CLI design:
### Controlled Shell
No arbitrary shell access. The `run` tool requires an explicit allowlist — only
commands you configure are permitted.
**Why:**
- Model can't bypass `search` with raw `grep`
- Model can't `cat` files instead of using `read_file`
- No accidental `rm -rf` or destructive commands
- Every action is intentional and auditable
- Token efficient — no parsing noisy shell output
See [Configuration: Shell Execution](configuration.md#shell-execution-toolsrun)
for allowlist setup.
### LSP-First
- Hover instead of file read (for type info)
- Symbols instead of grep (for definitions)
- Diagnostics on write (catch errors immediately)
### Efficient
- Every token counts — users are on Pro tier, not unlimited
- LSP queries cost fewer tokens than file reads
- Diagnostics prevent wasted cycles on broken code
## Configuration
### Gemini CLI
Location: `.gemini/settings.json` (workspace) or `~/.gemini/settings.json`
(user)
**Key finding:** Use `tools.core` (allowlist). `tools.exclude` doesn't work
reliably.
```json
{
"tools": {
"core": ["web_fetch", "google_web_search", "save_memory"]
},
"mcpServers": {
"catenary": {
"command": "catenary"
}
}
}
```
**Built-in tool names** (from `packages/core/src/tools/tool-names.ts`):
| LSTool | `list_directory` |
| ReadFileTool | `read_file` |
| WriteFileTool | `write_file` |
| EditTool | `replace` |
| GrepTool | `grep_search` |
| GlobTool | `glob` |
| ReadManyFilesTool | `read_many_files` |
| ShellTool | `run_shell_command` |
| WebFetchTool | `web_fetch` |
| WebSearchTool | `google_web_search` |
| MemoryTool | `save_memory` |
### Claude Code
Location: `.claude/settings.json` (workspace) or `~/.claude/settings.json`
(user)
**Key finding:** Must deny `Task` to prevent sub-agent escape hatch.
```json
{
"permissions": {
"deny": [
"Read",
"Edit",
"Write",
"Bash",
"Grep",
"Glob",
"Task",
"NotebookEdit"
],
"allow": [
"WebSearch",
"WebFetch",
"mcp__catenary__*",
"ToolSearch",
"AskUserQuestion"
]
},
"mcpServers": {
"catenary": {
"command": "catenary"
}
}
}
```
## Experiment Results
Validated 2026-02-06.
| Restriction method | `tools.core` allowlist | `permissions.deny` + block `Task` |
| MCP tools discovered | ✓ | ✓ |
| Built-in tools blocked | ✓ | ✓ |
| Model adapts gracefully | ✓ (slowly) | ✓ (quickly) |
| Sub-agent escape blocked | N/A | ✓ (requires denying `Task`) |
### Model Behavior When Constrained
**Gemini:**
1. Tried WebFetch to read local file (wrong tool)
2. Tried `run_shell_command` → blocked
3. Adapted to `codebase_map`
4. Used `document_symbols`
5. Delegated to sub-agent (burned tokens before admitting defeat)
**Claude:**
1. Tried `Skill(read)` → failed
2. No Read/Bash/Task available
3. Adapted to `document_symbols`
4. Admitted limitations gracefully
5. Offered LSP-based alternatives
**Key difference:** Claude admits defeat faster and communicates limitations
clearly. Gemini burns tokens trying workarounds.
## Catenary Tool Coverage
Catenary provides a complete toolkit — LSP intelligence, file I/O, and
controlled shell execution:
| `read_file` | File I/O | With line numbers and diagnostics |
| `write_file` | File I/O | With post-write diagnostics |
| `edit_file` | File I/O | Search-and-replace with diagnostics |
| `list_directory` | File I/O | Files, dirs, symlinks |
| `run` | Shell | Allowlist-enforced command execution |
| `search` | LSP | Workspace symbols + grep fallback |
| `find_references` | LSP | LSP references |
| `codebase_map` | LSP | File tree with symbols |
| `document_symbols` | LSP | File structure |
| `hover` | LSP | Type info, docs |
| `diagnostics` | LSP | Errors, warnings |
| ... | LSP | [Full list](overview.md#available-tools) |
## Limitations
### Shell is Allowlist-Only
The `run` tool only executes commands on the configured allowlist. There is no
general-purpose shell — models can't escape to `grep`, `cat`, or arbitrary
commands unless you explicitly allow them.
When catenary lacks a tool the model needs, it must either:
- Use available catenary tools creatively
- Use `run` if the command is on the allowlist
- Admit it can't complete the task
### LSP Dependency
Some operations require LSP:
- Find references (no grep fallback currently)
- Rename symbol
- Code actions / quick fixes
If LSP is unavailable for a language, these tools return errors. `search` has
a grep fallback for basic text matching when no LSP server covers the file.
## See Also
- [Archive: CLI Design](archive/cli-design.md) — Original custom CLI design
(abandoned)
- [Configuration](configuration.md) — catenary-mcp configuration reference