cade-cli 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cade_cli-0.10.0 → cade_cli-0.11.0}/PKG-INFO +19 -16
- {cade_cli-0.10.0 → cade_cli-0.11.0}/README.md +13 -12
- {cade_cli-0.10.0 → cade_cli-0.11.0}/pyproject.toml +6 -4
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/__init__.py +10 -0
- cade_cli-0.11.0/src/cade_mcp_local/_metadata.py +84 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/errors.py +46 -0
- cade_cli-0.11.0/src/cade_mcp_local/resources.py +131 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/server.py +16 -15
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/__init__.py +5 -10
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/context.py +22 -4
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/filesystem.py +57 -104
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/git.py +26 -7
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/notifications.py +10 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/questions.py +20 -6
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/results.py +76 -29
- cade_cli-0.11.0/src/cade_mcp_local/tools/schemas.py +128 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/search.py +22 -5
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/shell.py +14 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/snippets.py +16 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/tasks.py +31 -19
- cade_cli-0.11.0/src/cade_mcp_local/tools/web_fetch.py +256 -0
- cade_cli-0.11.0/src/cade_mcp_local/tools/web_search.py +158 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/__init__.py +1 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/arcade_client.py +131 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/cache.py +136 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/extract.py +54 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/markdown.py +64 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/preapproved.py +57 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/prompts.py +29 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/scrape.py +77 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/search/__init__.py +5 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/search/arcade.py +103 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/search/base.py +41 -0
- cade_cli-0.11.0/src/cade_mcp_local/web/search/http.py +129 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ai/prompts.py +15 -19
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/app.py +130 -73
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/auth.py +34 -46
- cade_cli-0.11.0/src/cadecoder/cli/commands/account.py +203 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/auth.py +30 -38
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/chat.py +20 -21
- cade_cli-0.11.0/src/cadecoder/cli/commands/context.py +184 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/mcp.py +1 -6
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/mem.py +4 -6
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/model.py +12 -9
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/persona.py +15 -158
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/serve.py +7 -15
- cade_cli-0.11.0/src/cadecoder/core/config.py +556 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/logging.py +5 -5
- cade_cli-0.11.0/src/cadecoder/core/paths.py +147 -0
- cade_cli-0.11.0/src/cadecoder/core/runtime.py +116 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/context_window.py +3 -1
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/mcp_schema_index.py +4 -9
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/orchestrator.py +42 -25
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/parallel.py +102 -27
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/tool_result_store.py +4 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/tool_schema_cache.py +5 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/__init__.py +1 -1
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/config.py +33 -21
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/providers/__init__.py +10 -4
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/__init__.py +1 -1
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/allowlist.py +2 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/config.py +40 -16
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/daemon.py +17 -11
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/install.py +4 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/lock.py +2 -2
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/observability.py +4 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/secrets.py +4 -4
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/worker.py +0 -4
- cade_cli-0.11.0/src/cadecoder/storage/personas.py +216 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/storage/threads.py +20 -14
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/channels.py +4 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/lock.py +6 -7
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/scheduler.py +6 -6
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/store.py +3 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/types.py +1 -1
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/serve/launchd.plist.tmpl +4 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/serve/serve.toml.example +9 -8
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/serve/systemd.service.tmpl +2 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/composite.py +243 -47
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/config.py +20 -28
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/mcp.py +51 -10
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/search/discovered.py +3 -3
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/search/service.py +1 -1
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ui/display.py +207 -33
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ui/input.py +4 -18
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ui/session.py +39 -24
- cade_cli-0.11.0/src/cadecoder/ui/slash.py +46 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/session.py +26 -32
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/tts.py +47 -37
- cade_cli-0.10.0/src/cade_mcp_local/tools/channels.py +0 -76
- cade_cli-0.10.0/src/cade_mcp_local/tools/schemas.py +0 -186
- cade_cli-0.10.0/src/cade_mcp_local/tools/sleep.py +0 -38
- cade_cli-0.10.0/src/cadecoder/cli/commands/context.py +0 -338
- cade_cli-0.10.0/src/cadecoder/core/config.py +0 -444
- cade_cli-0.10.0/src/cadecoder/storage/personas.py +0 -403
- {cade_cli-0.10.0 → cade_cli-0.11.0}/.gitignore +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/LICENSE +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/__main__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/config.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/tools/_read_cache.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cade_mcp_local/utils.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ai/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/channels.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/cron.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/hooks.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/tasks.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/thread.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/cli/commands/tools.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/constants.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/errors.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/git.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/names.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/core/types.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/execution/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/engine.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/callback.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/command.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/http.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/prompt.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/matchers.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/registry.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/hooks/types.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/providers/anthropic.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/providers/base.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/providers/ollama.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/providers/openai.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/adapters/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/adapters/base.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/adapters/registry.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/adapters/stub.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/adapters/telegram.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/chunker.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/commands.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/elicitation.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/format.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/progress_sink.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/router.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/serve/sinks.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/storage/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tasks/notifications.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/login_failed.html +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/login_success.html +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/templates/styles.css +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/manager/base.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/search/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/tools/search/scoring.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ui/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/ui/elicitation.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/__init__.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/audio.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/cleanup.py +0 -0
- {cade_cli-0.10.0 → cade_cli-0.11.0}/src/cadecoder/voice/stt.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cade-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Cade - The CLI Agent from Arcade.dev
|
|
5
5
|
Project-URL: Homepage, https://arcade.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.arcade.dev
|
|
@@ -45,12 +45,14 @@ Classifier: Typing :: Typed
|
|
|
45
45
|
Requires-Python: >=3.11
|
|
46
46
|
Requires-Dist: agent-library<1.0.0,>=0.12.0
|
|
47
47
|
Requires-Dist: anthropic<1.0.0,>=0.34.0
|
|
48
|
-
Requires-Dist: arcade-core<5.0.0,>=4.
|
|
49
|
-
Requires-Dist: arcade-mcp-server
|
|
50
|
-
Requires-Dist: arcade-tdk
|
|
48
|
+
Requires-Dist: arcade-core<5.0.0,>=4.7.0
|
|
49
|
+
Requires-Dist: arcade-mcp-server<2.0.0,>=1.21.0
|
|
50
|
+
Requires-Dist: arcade-tdk<4.0.0,>=3.8.0
|
|
51
|
+
Requires-Dist: arcadepy<2.0.0,>=1.10.0
|
|
51
52
|
Requires-Dist: authlib<2.0.0,>=1.6.0
|
|
52
53
|
Requires-Dist: croniter<4.0.0,>=2.0.0
|
|
53
54
|
Requires-Dist: filelock<4.0.0,>=3.0.0
|
|
55
|
+
Requires-Dist: html2text>=2024.2.26
|
|
54
56
|
Requires-Dist: httpx<1.0.0,>=0.27.0
|
|
55
57
|
Requires-Dist: keyring<26.0,>=24.0
|
|
56
58
|
Requires-Dist: openai<2.0.0,>=1.0.0
|
|
@@ -159,7 +161,8 @@ cade --voice # speak / hear (voice mode)
|
|
|
159
161
|
| `/logs` | Recent log entries |
|
|
160
162
|
| `/thread`, `/history` | Current thread info |
|
|
161
163
|
| `/pin`, `/unpin` | Pin reference material into the session |
|
|
162
|
-
| `/
|
|
164
|
+
| `/tasks`, `/cron`, `/hooks`, `/channels`, `/notify` | Coordination surfaces (mirrored as `cade tasks` / `cade cron` / `cade hooks` / `cade channels`) |
|
|
165
|
+
| `/usage` | Context-window status and token usage |
|
|
163
166
|
| `/cd`, `/pwd`, `/!` | Shell shortcuts |
|
|
164
167
|
| `Ctrl+C` | Exit |
|
|
165
168
|
|
|
@@ -191,7 +194,7 @@ cade mcp authorize my-server # OAuth flow (browser)
|
|
|
191
194
|
cade mcp list / status / test / enable / disable / rm
|
|
192
195
|
```
|
|
193
196
|
|
|
194
|
-
User MCP servers register at `~/.
|
|
197
|
+
User MCP servers register at `~/.cade/config/contexts/<context>.toml [[mcp]]`. Servers with large catalogs are auto-deferred (parameter schemas stripped to keep tool-list bytes down) and fetched on demand via `Local_ToolSchema`.
|
|
195
198
|
|
|
196
199
|
---
|
|
197
200
|
|
|
@@ -206,7 +209,7 @@ cade mem add ~/notes # index a directory
|
|
|
206
209
|
cade mem search "auth design" # semantic search
|
|
207
210
|
```
|
|
208
211
|
|
|
209
|
-
Memory is persona-scoped — each persona has its own `~/.
|
|
212
|
+
Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<context>/memory/<persona>/.librarian/` index, so a `reviewer` persona's notes never leak into `default`.
|
|
210
213
|
|
|
211
214
|
---
|
|
212
215
|
|
|
@@ -221,7 +224,7 @@ cade serve --install launchd # macOS LaunchAgent (or --install syste
|
|
|
221
224
|
cade serve status / stop # health, uptime; SIGTERM running daemon
|
|
222
225
|
```
|
|
223
226
|
|
|
224
|
-
Config lives at `~/.
|
|
227
|
+
Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
|
|
225
228
|
|
|
226
229
|
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
227
230
|
|
|
@@ -257,7 +260,7 @@ export OPENAI_BASE_URL="http://localhost:11434/v1"
|
|
|
257
260
|
export OPENAI_API_KEY="ollama" # any non-empty value
|
|
258
261
|
cade chat --model llama3
|
|
259
262
|
|
|
260
|
-
# Config — ~/.
|
|
263
|
+
# Config — ~/.cade/config/cade.toml
|
|
261
264
|
[model_settings]
|
|
262
265
|
host = "http://localhost:11434/v1"
|
|
263
266
|
api_key = "ollama"
|
|
@@ -269,15 +272,15 @@ Skip Arcade Cloud authentication entirely with `--local-only` or `CADE_LOCAL_ONL
|
|
|
269
272
|
|
|
270
273
|
## Configuration
|
|
271
274
|
|
|
272
|
-
All state lives under `~/.
|
|
275
|
+
All state lives under `~/.cade/`. Override the location with `CADE_HOME=/path/to/alt`.
|
|
273
276
|
|
|
274
277
|
| File | Purpose |
|
|
275
278
|
|---|---|
|
|
276
|
-
| `
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
279
|
-
| `
|
|
280
|
-
| `serve
|
|
279
|
+
| `config/cade.toml` | Model / provider / UI settings |
|
|
280
|
+
| `data/contexts/<context>/history.db` | Thread + message storage |
|
|
281
|
+
| `logs/cade.log` | Rotating log file |
|
|
282
|
+
| `config/contexts/<context>.toml [[mcp]]` | User MCP server registry |
|
|
283
|
+
| `config/contexts/<context>.toml [serve]` | `cade serve` daemon config |
|
|
281
284
|
| `personas/`, `memory/`, `tasks/`, `channels/` | Persona-scoped state |
|
|
282
285
|
|
|
283
286
|
Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/configuration.md).
|
|
@@ -290,7 +293,7 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
|
|
|
290
293
|
| `OPENAI_BASE_URL` | Custom OpenAI-compatible endpoint |
|
|
291
294
|
| `ARCADE_API_KEY`, `ARCADE_BASE_URL` | Arcade Cloud (alternative to OAuth) |
|
|
292
295
|
| `CADE_LOCAL_ONLY=1` | Skip remote tools entirely |
|
|
293
|
-
| `
|
|
296
|
+
| `CADE_HOME` | Override config directory |
|
|
294
297
|
| `CADE_PROJECT_ROOT` | Sandbox `Local_*` filesystem tools to this root |
|
|
295
298
|
|
|
296
299
|
---
|
|
@@ -72,7 +72,8 @@ cade --voice # speak / hear (voice mode)
|
|
|
72
72
|
| `/logs` | Recent log entries |
|
|
73
73
|
| `/thread`, `/history` | Current thread info |
|
|
74
74
|
| `/pin`, `/unpin` | Pin reference material into the session |
|
|
75
|
-
| `/
|
|
75
|
+
| `/tasks`, `/cron`, `/hooks`, `/channels`, `/notify` | Coordination surfaces (mirrored as `cade tasks` / `cade cron` / `cade hooks` / `cade channels`) |
|
|
76
|
+
| `/usage` | Context-window status and token usage |
|
|
76
77
|
| `/cd`, `/pwd`, `/!` | Shell shortcuts |
|
|
77
78
|
| `Ctrl+C` | Exit |
|
|
78
79
|
|
|
@@ -104,7 +105,7 @@ cade mcp authorize my-server # OAuth flow (browser)
|
|
|
104
105
|
cade mcp list / status / test / enable / disable / rm
|
|
105
106
|
```
|
|
106
107
|
|
|
107
|
-
User MCP servers register at `~/.
|
|
108
|
+
User MCP servers register at `~/.cade/config/contexts/<context>.toml [[mcp]]`. Servers with large catalogs are auto-deferred (parameter schemas stripped to keep tool-list bytes down) and fetched on demand via `Local_ToolSchema`.
|
|
108
109
|
|
|
109
110
|
---
|
|
110
111
|
|
|
@@ -119,7 +120,7 @@ cade mem add ~/notes # index a directory
|
|
|
119
120
|
cade mem search "auth design" # semantic search
|
|
120
121
|
```
|
|
121
122
|
|
|
122
|
-
Memory is persona-scoped — each persona has its own `~/.
|
|
123
|
+
Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<context>/memory/<persona>/.librarian/` index, so a `reviewer` persona's notes never leak into `default`.
|
|
123
124
|
|
|
124
125
|
---
|
|
125
126
|
|
|
@@ -134,7 +135,7 @@ cade serve --install launchd # macOS LaunchAgent (or --install syste
|
|
|
134
135
|
cade serve status / stop # health, uptime; SIGTERM running daemon
|
|
135
136
|
```
|
|
136
137
|
|
|
137
|
-
Config lives at `~/.
|
|
138
|
+
Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
|
|
138
139
|
|
|
139
140
|
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
140
141
|
|
|
@@ -170,7 +171,7 @@ export OPENAI_BASE_URL="http://localhost:11434/v1"
|
|
|
170
171
|
export OPENAI_API_KEY="ollama" # any non-empty value
|
|
171
172
|
cade chat --model llama3
|
|
172
173
|
|
|
173
|
-
# Config — ~/.
|
|
174
|
+
# Config — ~/.cade/config/cade.toml
|
|
174
175
|
[model_settings]
|
|
175
176
|
host = "http://localhost:11434/v1"
|
|
176
177
|
api_key = "ollama"
|
|
@@ -182,15 +183,15 @@ Skip Arcade Cloud authentication entirely with `--local-only` or `CADE_LOCAL_ONL
|
|
|
182
183
|
|
|
183
184
|
## Configuration
|
|
184
185
|
|
|
185
|
-
All state lives under `~/.
|
|
186
|
+
All state lives under `~/.cade/`. Override the location with `CADE_HOME=/path/to/alt`.
|
|
186
187
|
|
|
187
188
|
| File | Purpose |
|
|
188
189
|
|---|---|
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
193
|
-
| `serve
|
|
190
|
+
| `config/cade.toml` | Model / provider / UI settings |
|
|
191
|
+
| `data/contexts/<context>/history.db` | Thread + message storage |
|
|
192
|
+
| `logs/cade.log` | Rotating log file |
|
|
193
|
+
| `config/contexts/<context>.toml [[mcp]]` | User MCP server registry |
|
|
194
|
+
| `config/contexts/<context>.toml [serve]` | `cade serve` daemon config |
|
|
194
195
|
| `personas/`, `memory/`, `tasks/`, `channels/` | Persona-scoped state |
|
|
195
196
|
|
|
196
197
|
Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/configuration.md).
|
|
@@ -203,7 +204,7 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
|
|
|
203
204
|
| `OPENAI_BASE_URL` | Custom OpenAI-compatible endpoint |
|
|
204
205
|
| `ARCADE_API_KEY`, `ARCADE_BASE_URL` | Arcade Cloud (alternative to OAuth) |
|
|
205
206
|
| `CADE_LOCAL_ONLY=1` | Skip remote tools entirely |
|
|
206
|
-
| `
|
|
207
|
+
| `CADE_HOME` | Override config directory |
|
|
207
208
|
| `CADE_PROJECT_ROOT` | Sandbox `Local_*` filesystem tools to this root |
|
|
208
209
|
|
|
209
210
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cade-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0"
|
|
4
4
|
description = "Cade - The CLI Agent from Arcade.dev"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -31,9 +31,10 @@ dependencies = [
|
|
|
31
31
|
"openai>=1.0.0,<2.0.0",
|
|
32
32
|
"anthropic>=0.34.0,<1.0.0",
|
|
33
33
|
"ulid==1.1",
|
|
34
|
-
"arcade-tdk>=
|
|
35
|
-
"arcade-mcp-server>=1.0.0",
|
|
36
|
-
"arcade-core>=4.
|
|
34
|
+
"arcade-tdk>=3.8.0,<4.0.0",
|
|
35
|
+
"arcade-mcp-server>=1.21.0,<2.0.0",
|
|
36
|
+
"arcade-core>=4.7.0,<5.0.0",
|
|
37
|
+
"arcadepy>=1.10.0,<2.0.0",
|
|
37
38
|
"authlib>=1.6.0,<2.0.0",
|
|
38
39
|
"pyperclip>=1.8.0,<2.0.0",
|
|
39
40
|
"prompt-toolkit>=3.0.52,<4.0.0",
|
|
@@ -45,6 +46,7 @@ dependencies = [
|
|
|
45
46
|
"croniter>=2.0.0,<4.0.0",
|
|
46
47
|
"python-telegram-bot>=21.0,<22.0",
|
|
47
48
|
"keyring>=24.0,<26.0",
|
|
49
|
+
"html2text>=2024.2.26",
|
|
48
50
|
]
|
|
49
51
|
|
|
50
52
|
[project.urls]
|
|
@@ -27,12 +27,17 @@ from cade_mcp_local.config import (
|
|
|
27
27
|
from cade_mcp_local.errors import (
|
|
28
28
|
CommandExecutionError,
|
|
29
29
|
CommandTimeoutError,
|
|
30
|
+
ContentTooLargeError,
|
|
30
31
|
FileOperationError,
|
|
31
32
|
GitOperationError,
|
|
32
33
|
InvalidInputError,
|
|
33
34
|
PathNotFoundError,
|
|
34
35
|
PathSecurityError,
|
|
36
|
+
RedirectError,
|
|
35
37
|
SearchError,
|
|
38
|
+
WebFetchError,
|
|
39
|
+
WebSearchError,
|
|
40
|
+
WebTransientError,
|
|
36
41
|
)
|
|
37
42
|
from cade_mcp_local.server import app, create_app
|
|
38
43
|
|
|
@@ -67,6 +72,11 @@ __all__ = [
|
|
|
67
72
|
"GitOperationError",
|
|
68
73
|
"SearchError",
|
|
69
74
|
"FileOperationError",
|
|
75
|
+
"WebFetchError",
|
|
76
|
+
"WebSearchError",
|
|
77
|
+
"RedirectError",
|
|
78
|
+
"ContentTooLargeError",
|
|
79
|
+
"WebTransientError",
|
|
70
80
|
]
|
|
71
81
|
|
|
72
82
|
from cadecoder import __version__ as __version__ # noqa: E402
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Reusable :class:`ToolMetadata` builders for Local_* tools.
|
|
2
|
+
|
|
3
|
+
Local tools run in-process against the user's machine. None of them have a
|
|
4
|
+
``ServiceDomain`` (those describe an external service like Slack or Gmail);
|
|
5
|
+
``open_world`` is therefore ``False`` for everything in this package, and
|
|
6
|
+
the per-tool metadata below only varies on operation/read-only/destructive/
|
|
7
|
+
idempotent.
|
|
8
|
+
|
|
9
|
+
Web tools (``WebFetch`` / ``WebSearch``) are the exception — they declare
|
|
10
|
+
``ServiceDomain.WEB_SCRAPING`` and ``open_world=True`` inline, since their
|
|
11
|
+
backing data lives on the open internet.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from arcade_mcp_server.metadata import (
|
|
15
|
+
Behavior,
|
|
16
|
+
Classification,
|
|
17
|
+
Operation,
|
|
18
|
+
ToolMetadata,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _meta(
|
|
23
|
+
*,
|
|
24
|
+
operations: list[Operation],
|
|
25
|
+
read_only: bool,
|
|
26
|
+
destructive: bool = False,
|
|
27
|
+
idempotent: bool = False,
|
|
28
|
+
) -> ToolMetadata:
|
|
29
|
+
"""All Local_* tools share open_world=False and no service_domains."""
|
|
30
|
+
return ToolMetadata(
|
|
31
|
+
classification=Classification(service_domains=None),
|
|
32
|
+
behavior=Behavior(
|
|
33
|
+
operations=operations,
|
|
34
|
+
read_only=read_only,
|
|
35
|
+
destructive=destructive,
|
|
36
|
+
idempotent=idempotent,
|
|
37
|
+
open_world=False,
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
READ_META = _meta(operations=[Operation.READ], read_only=True, idempotent=True)
|
|
43
|
+
"""Pure read of local state. Idempotent."""
|
|
44
|
+
|
|
45
|
+
READ_VOLATILE_META = _meta(operations=[Operation.READ], read_only=True, idempotent=False)
|
|
46
|
+
"""Reads local state that changes under the agent's feet (e.g. compaction backups)."""
|
|
47
|
+
|
|
48
|
+
CREATE_META = _meta(operations=[Operation.CREATE], read_only=False, idempotent=False)
|
|
49
|
+
"""Creates a new local resource (file, task, snippet, notification)."""
|
|
50
|
+
|
|
51
|
+
UPDATE_META = _meta(operations=[Operation.UPDATE], read_only=False, idempotent=False)
|
|
52
|
+
"""Modifies an existing local resource."""
|
|
53
|
+
|
|
54
|
+
CREATE_OR_UPDATE_META = _meta(operations=[Operation.CREATE, Operation.UPDATE], read_only=False)
|
|
55
|
+
"""Upsert-style write that may create or update."""
|
|
56
|
+
|
|
57
|
+
DELETE_META = _meta(
|
|
58
|
+
operations=[Operation.DELETE], read_only=False, destructive=True, idempotent=True
|
|
59
|
+
)
|
|
60
|
+
"""Removes a local resource. Destructive."""
|
|
61
|
+
|
|
62
|
+
OPAQUE_META = ToolMetadata(
|
|
63
|
+
classification=Classification(service_domains=None),
|
|
64
|
+
behavior=Behavior(
|
|
65
|
+
operations=[Operation.OPAQUE],
|
|
66
|
+
read_only=False,
|
|
67
|
+
destructive=False,
|
|
68
|
+
idempotent=False,
|
|
69
|
+
open_world=False,
|
|
70
|
+
),
|
|
71
|
+
strict=False, # Bash/Channel can do anything; we keep destructive=False per "best case"
|
|
72
|
+
)
|
|
73
|
+
"""Action depends on runtime inputs (e.g. Bash, Channel multiplexer)."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
__all__ = (
|
|
77
|
+
"CREATE_META",
|
|
78
|
+
"CREATE_OR_UPDATE_META",
|
|
79
|
+
"DELETE_META",
|
|
80
|
+
"OPAQUE_META",
|
|
81
|
+
"READ_META",
|
|
82
|
+
"READ_VOLATILE_META",
|
|
83
|
+
"UPDATE_META",
|
|
84
|
+
)
|
|
@@ -120,3 +120,49 @@ class FileOperationError(FatalToolError):
|
|
|
120
120
|
self.operation = operation
|
|
121
121
|
self.path = path
|
|
122
122
|
super().__init__(f"Failed to {operation} '{path}': {message}")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class WebFetchError(FatalToolError):
|
|
126
|
+
"""Generic non-retriable WebFetch failure (DNS, 4xx, blocked host)."""
|
|
127
|
+
|
|
128
|
+
def __init__(self, url: str, message: str):
|
|
129
|
+
self.url = url
|
|
130
|
+
super().__init__(f"WebFetch failed for {url}: {message}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class RedirectError(FatalToolError):
|
|
134
|
+
"""Cross-host redirect; the agent should re-fetch the new URL explicitly."""
|
|
135
|
+
|
|
136
|
+
def __init__(self, from_url: str, to_url: str):
|
|
137
|
+
self.from_url = from_url
|
|
138
|
+
self.to_url = to_url
|
|
139
|
+
super().__init__(
|
|
140
|
+
f"Cross-host redirect from {from_url} to {to_url}; re-call WebFetch with the new URL."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class ContentTooLargeError(FatalToolError):
|
|
145
|
+
"""Body exceeded max_bytes."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, url: str, limit_bytes: int):
|
|
148
|
+
self.url = url
|
|
149
|
+
self.limit_bytes = limit_bytes
|
|
150
|
+
super().__init__(f"Body for {url} exceeded {limit_bytes} bytes; aborted.")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class WebSearchError(FatalToolError):
|
|
154
|
+
"""Backend misconfigured or returned an unparseable response."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, backend: str, message: str):
|
|
157
|
+
self.backend = backend
|
|
158
|
+
super().__init__(f"WebSearch ({backend}) failed: {message}")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class WebTransientError(RetryableToolError):
|
|
162
|
+
"""5xx, network reset, rate-limit. Agent may retry."""
|
|
163
|
+
|
|
164
|
+
def __init__(self, url: str, status: int | None, message: str):
|
|
165
|
+
self.url = url
|
|
166
|
+
self.status = status
|
|
167
|
+
detail = f" (HTTP {status})" if status else ""
|
|
168
|
+
super().__init__(f"Transient web error for {url}{detail}: {message}")
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""MCP resources exposed by the local server.
|
|
2
|
+
|
|
3
|
+
Two resources today:
|
|
4
|
+
|
|
5
|
+
- ``cade://server/instructions`` — the same SERVER_INSTRUCTIONS string the
|
|
6
|
+
server advertises in its initialize handshake, but addressable as a
|
|
7
|
+
resource so MCP clients can re-read it on demand (skill-style discovery).
|
|
8
|
+
- ``cade://memory/sources`` — a JSON listing of the active persona's
|
|
9
|
+
``cade mem`` sources, read from the librarian sources registry.
|
|
10
|
+
|
|
11
|
+
Persona resolution mirrors ``cade mem``: ``CADE_PERSONA`` env var →
|
|
12
|
+
``"default"``. Context data is supplied by the parent process through
|
|
13
|
+
``CADE_CONTEXT_DATA_DIR``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from arcade_mcp_server import MCPApp
|
|
26
|
+
|
|
27
|
+
log = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
INSTRUCTIONS_URI = "cade://server/instructions"
|
|
30
|
+
MEMORY_SOURCES_URI = "cade://memory/sources"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _cade_home() -> Path:
|
|
34
|
+
"""Root for Cade's single-home state."""
|
|
35
|
+
override = os.environ.get("CADE_HOME")
|
|
36
|
+
if override:
|
|
37
|
+
return Path(override)
|
|
38
|
+
return Path.home() / ".cade"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _resolve_persona() -> str:
|
|
42
|
+
"""Return the active persona name. Env var → ``"default"``."""
|
|
43
|
+
name = (os.environ.get("CADE_PERSONA") or "").strip()
|
|
44
|
+
return name or "default"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _sources_path(persona: str) -> Path:
|
|
48
|
+
context_data = os.environ.get("CADE_CONTEXT_DATA_DIR")
|
|
49
|
+
if context_data:
|
|
50
|
+
return Path(context_data) / "memory" / persona / ".librarian" / "sources.json"
|
|
51
|
+
context = os.environ.get("CADE_CONTEXT") or "default"
|
|
52
|
+
return (
|
|
53
|
+
_cade_home()
|
|
54
|
+
/ "data"
|
|
55
|
+
/ "contexts"
|
|
56
|
+
/ context
|
|
57
|
+
/ "memory"
|
|
58
|
+
/ persona
|
|
59
|
+
/ ".librarian"
|
|
60
|
+
/ "sources.json"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _load_memory_sources(persona: str) -> list[dict[str, object]]:
|
|
65
|
+
"""Read ``sources.json`` for ``persona`` and return its list of entries.
|
|
66
|
+
|
|
67
|
+
Missing file → empty list (a valid state: persona has no sources yet).
|
|
68
|
+
Malformed JSON → empty list with a warning; resource read should not
|
|
69
|
+
crash the server on user-edited config.
|
|
70
|
+
"""
|
|
71
|
+
path = _sources_path(persona)
|
|
72
|
+
if not path.exists():
|
|
73
|
+
return []
|
|
74
|
+
try:
|
|
75
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
76
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
77
|
+
log.warning("failed to read memory sources at %s: %s", path, exc)
|
|
78
|
+
return []
|
|
79
|
+
if not isinstance(data, list):
|
|
80
|
+
log.warning("memory sources at %s is not a JSON list; ignoring", path)
|
|
81
|
+
return []
|
|
82
|
+
return [entry for entry in data if isinstance(entry, dict)]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _memory_sources_payload() -> str:
|
|
86
|
+
"""Build the JSON body for the memory-sources resource."""
|
|
87
|
+
persona = _resolve_persona()
|
|
88
|
+
sources = _load_memory_sources(persona)
|
|
89
|
+
payload = {
|
|
90
|
+
"persona": persona,
|
|
91
|
+
"sources_path": str(_sources_path(persona)),
|
|
92
|
+
"count": len(sources),
|
|
93
|
+
"sources": sources,
|
|
94
|
+
}
|
|
95
|
+
return json.dumps(payload, indent=2, default=str)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def register_resources(app: MCPApp, instructions: str) -> None:
|
|
99
|
+
"""Register the local server's static + dynamic MCP resources."""
|
|
100
|
+
app.add_text_resource(
|
|
101
|
+
INSTRUCTIONS_URI,
|
|
102
|
+
text=instructions,
|
|
103
|
+
name="server-instructions",
|
|
104
|
+
title="Local server instructions",
|
|
105
|
+
description=(
|
|
106
|
+
"Guidance on when to reach for Local_* tools (filesystem, shell, "
|
|
107
|
+
"git, web, tasks, channels, …) versus remote/cloud equivalents. "
|
|
108
|
+
"Mirrors the server's MCP initialize-time instructions."
|
|
109
|
+
),
|
|
110
|
+
mime_type="text/markdown",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
app.add_resource(
|
|
114
|
+
MEMORY_SOURCES_URI,
|
|
115
|
+
name="memory-sources",
|
|
116
|
+
title="cade mem sources (active persona)",
|
|
117
|
+
description=(
|
|
118
|
+
"JSON listing of memory sources registered for the active persona "
|
|
119
|
+
"(equivalent to `cade mem list`). Read each call so the listing "
|
|
120
|
+
"reflects sources added after server start."
|
|
121
|
+
),
|
|
122
|
+
mime_type="application/json",
|
|
123
|
+
handler=lambda _uri: _memory_sources_payload(),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = (
|
|
128
|
+
"INSTRUCTIONS_URI",
|
|
129
|
+
"MEMORY_SOURCES_URI",
|
|
130
|
+
"register_resources",
|
|
131
|
+
)
|
|
@@ -13,13 +13,12 @@ os.environ.setdefault("LOGURU_LEVEL", "WARNING")
|
|
|
13
13
|
|
|
14
14
|
from arcade_mcp_server import MCPApp
|
|
15
15
|
|
|
16
|
+
from cade_mcp_local.resources import register_resources
|
|
16
17
|
from cade_mcp_local.tools import (
|
|
17
18
|
ask_user_question_tool,
|
|
18
19
|
bash_tool,
|
|
19
|
-
channel_tool,
|
|
20
20
|
edit_tool,
|
|
21
21
|
git_tool,
|
|
22
|
-
insert_text_tool,
|
|
23
22
|
list_files_tool,
|
|
24
23
|
pin_context_tool,
|
|
25
24
|
push_notification_tool,
|
|
@@ -27,13 +26,13 @@ from cade_mcp_local.tools import (
|
|
|
27
26
|
retrieve_tool_result_tool,
|
|
28
27
|
search_recent_context_tool,
|
|
29
28
|
search_tool,
|
|
30
|
-
sleep_tool,
|
|
31
29
|
task_create_tool,
|
|
32
30
|
task_delete_tool,
|
|
33
|
-
task_get_tool,
|
|
34
31
|
task_list_tool,
|
|
35
32
|
task_update_tool,
|
|
36
33
|
tool_schema_tool,
|
|
34
|
+
web_fetch_tool,
|
|
35
|
+
web_search_tool,
|
|
37
36
|
write_file_tool,
|
|
38
37
|
)
|
|
39
38
|
|
|
@@ -47,14 +46,17 @@ is running. Use these tools when you need to:
|
|
|
47
46
|
- Execute shell commands or bash scripts locally
|
|
48
47
|
- Check git status, diff, log, or branches of the local repository
|
|
49
48
|
- Search compacted context backups stored locally
|
|
50
|
-
- File-backed tasks for durable multi-step work (TaskCreate, TaskList,
|
|
49
|
+
- File-backed tasks for durable multi-step work (TaskCreate, TaskList,
|
|
51
50
|
TaskUpdate, TaskDelete) — the host registers them as Local_* names
|
|
52
|
-
- Cross-session channels (Channel): use action=list when channel names are unknown,
|
|
53
|
-
then action=recv or action=send with a non-empty name
|
|
54
51
|
- PushNotification to inject a structured notice into the agent context at the next
|
|
55
52
|
tool boundary
|
|
56
53
|
- AskUserQuestion when you need structured clarification from the user (host:
|
|
57
54
|
Local_AskUserQuestion)
|
|
55
|
+
- WebFetch to read an external URL (Firecrawl via Arcade by default, httpx
|
|
56
|
+
fallback; preapproved hosts return raw markdown, others go through a fast
|
|
57
|
+
extraction model)
|
|
58
|
+
- WebSearch to find pages on the open web (Arcade-hosted Google Search by
|
|
59
|
+
default; generic HTTP/JSON backend as a fallback)
|
|
58
60
|
|
|
59
61
|
Do NOT use these tools for operations on remote systems, cloud resources, or
|
|
60
62
|
external services. For those, use the appropriate remote/cloud tools instead.
|
|
@@ -75,14 +77,10 @@ def create_app() -> MCPApp:
|
|
|
75
77
|
app.add_tool(read_file_tool)
|
|
76
78
|
app.add_tool(write_file_tool)
|
|
77
79
|
app.add_tool(edit_tool)
|
|
78
|
-
app.add_tool(insert_text_tool)
|
|
79
80
|
|
|
80
81
|
# Shell tool
|
|
81
82
|
app.add_tool(bash_tool)
|
|
82
83
|
|
|
83
|
-
# Sleep tool (async wait, concurrency-safe)
|
|
84
|
-
app.add_tool(sleep_tool)
|
|
85
|
-
|
|
86
84
|
# Search tool (consolidated: ripgrep with grep fallback)
|
|
87
85
|
app.add_tool(search_tool)
|
|
88
86
|
|
|
@@ -104,19 +102,22 @@ def create_app() -> MCPApp:
|
|
|
104
102
|
# Task coordination (file-backed task store)
|
|
105
103
|
app.add_tool(task_create_tool)
|
|
106
104
|
app.add_tool(task_list_tool)
|
|
107
|
-
app.add_tool(task_get_tool)
|
|
108
105
|
app.add_tool(task_update_tool)
|
|
109
106
|
app.add_tool(task_delete_tool)
|
|
110
107
|
|
|
111
108
|
# Notifications (inject XML into context at next tool boundary)
|
|
112
109
|
app.add_tool(push_notification_tool)
|
|
113
110
|
|
|
114
|
-
# Cross-session channels (send/recv/list)
|
|
115
|
-
app.add_tool(channel_tool)
|
|
116
|
-
|
|
117
111
|
# Structured clarification question for the user
|
|
118
112
|
app.add_tool(ask_user_question_tool)
|
|
119
113
|
|
|
114
|
+
# Web tools (read external pages, search the open web)
|
|
115
|
+
app.add_tool(web_fetch_tool)
|
|
116
|
+
app.add_tool(web_search_tool)
|
|
117
|
+
|
|
118
|
+
# MCP resources: server instructions + memory sources for the active persona
|
|
119
|
+
register_resources(app, SERVER_INSTRUCTIONS)
|
|
120
|
+
|
|
120
121
|
return app
|
|
121
122
|
|
|
122
123
|
|
|
@@ -5,11 +5,9 @@ is running. Use these tools when you need to read, write, search, or execute
|
|
|
5
5
|
commands on the same machine as the running cade process.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from cade_mcp_local.tools.channels import channel_tool
|
|
9
8
|
from cade_mcp_local.tools.context import search_recent_context_tool
|
|
10
9
|
from cade_mcp_local.tools.filesystem import (
|
|
11
10
|
edit_tool,
|
|
12
|
-
insert_text_tool,
|
|
13
11
|
list_files_tool,
|
|
14
12
|
read_file_tool,
|
|
15
13
|
write_file_tool,
|
|
@@ -25,15 +23,15 @@ from cade_mcp_local.tools.results import retrieve_tool_result_tool
|
|
|
25
23
|
from cade_mcp_local.tools.schemas import tool_schema_tool
|
|
26
24
|
from cade_mcp_local.tools.search import search_tool
|
|
27
25
|
from cade_mcp_local.tools.shell import bash_tool
|
|
28
|
-
from cade_mcp_local.tools.sleep import sleep_tool
|
|
29
26
|
from cade_mcp_local.tools.snippets import pin_context_tool
|
|
30
27
|
from cade_mcp_local.tools.tasks import (
|
|
31
28
|
task_create_tool,
|
|
32
29
|
task_delete_tool,
|
|
33
|
-
task_get_tool,
|
|
34
30
|
task_list_tool,
|
|
35
31
|
task_update_tool,
|
|
36
32
|
)
|
|
33
|
+
from cade_mcp_local.tools.web_fetch import web_fetch_tool
|
|
34
|
+
from cade_mcp_local.tools.web_search import web_search_tool
|
|
37
35
|
|
|
38
36
|
__all__ = [
|
|
39
37
|
# Filesystem
|
|
@@ -41,7 +39,6 @@ __all__ = [
|
|
|
41
39
|
"read_file_tool",
|
|
42
40
|
"write_file_tool",
|
|
43
41
|
"edit_tool",
|
|
44
|
-
"insert_text_tool",
|
|
45
42
|
# Git
|
|
46
43
|
"git_tool",
|
|
47
44
|
"get_current_branch_name",
|
|
@@ -50,8 +47,6 @@ __all__ = [
|
|
|
50
47
|
"search_tool",
|
|
51
48
|
# Shell
|
|
52
49
|
"bash_tool",
|
|
53
|
-
# Sleep
|
|
54
|
-
"sleep_tool",
|
|
55
50
|
# Context
|
|
56
51
|
"search_recent_context_tool",
|
|
57
52
|
# Pinned context
|
|
@@ -63,13 +58,13 @@ __all__ = [
|
|
|
63
58
|
# Tasks
|
|
64
59
|
"task_create_tool",
|
|
65
60
|
"task_list_tool",
|
|
66
|
-
"task_get_tool",
|
|
67
61
|
"task_update_tool",
|
|
68
62
|
"task_delete_tool",
|
|
69
63
|
# Notifications
|
|
70
64
|
"push_notification_tool",
|
|
71
65
|
# Interactive
|
|
72
66
|
"ask_user_question_tool",
|
|
73
|
-
#
|
|
74
|
-
"
|
|
67
|
+
# Web
|
|
68
|
+
"web_fetch_tool",
|
|
69
|
+
"web_search_tool",
|
|
75
70
|
]
|