Skip to content

Commit 2d59604

Browse files
hkaltaiclaude
andauthored
feat: model-free tool CLI and MCP server for external coding agents (#1609)
* feat: experiment: model-free `tool` CLI and MCP server on one adapter Do not merge: experiment for evaluating direct altimate-code tools in Claude Code. - Bring in `altimate-code mcp serve` and the workspace tools from the MCP draft branches - `tool list|schema|run|context|daemon`: run the same adapter as `mcp serve` without a model - `mcp serve` default set adds impact, lineage, PII, data-diff, equivalence and translation tools; `--all-tools` exposes the full allow-list - Long-running per-project process keeps connections warm and releases DuckDB/SQLite file locks between calls - `--connections` / `ALTIMATE_TOOL_CONNECTIONS`, `--args -` from stdin, `tool schema` for one tool - DROP DATABASE, DROP SCHEMA and TRUNCATE stay blocked even with `--allow-write` - `link --claude-md` writes CLI routing guidance for clients without MCP Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: experiment: workspace tools on by default for linked projects Do not merge: experiment. - `tool list|schema|run|context|daemon` enable workspace tools automatically when the project is linked; `--no-workspace-tools` disables them - `tool context` lists the linked workspace, its skills and how to use the workspace tools - Daemon identity and spawn arguments follow the effective workspace-tools value Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: experiment: mirror workspace skills into Claude Code project skills Do not merge: experiment. - Every workspace skill sync also writes the skills into `<project>/.claude/skills/`, so linking or refreshing a project puts them on disk before a Claude Code session starts - Copies follow the Claude Code plugin's rules: frontmatter reduced to `name` and `description`, command blocks neutralised, hook and plugin component paths skipped, user-written skills never touched, each copy marked and git-ignored - Unlink and the feature-off path remove the copies; projects without `.claude` are left alone; `ALTIMATE_CLAUDE_SKILL_SYNC=0` disables it - A mirror failure never changes the sync result - Tests: `claude-skill-mirror.test.ts`, mirror wiring in `skill-sync.test.ts` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: harden tool daemon startup, lock release, MCP write gate and skill mirror - daemon: release file locks when the last request finishes; serialize bind and stale-socket recovery with a lock; clients never unlink sockets; refuse a symlinked or foreign-owned socket directory - mcp: warehouse_remove and driver installs need --allow-write over MCP too - analyst effort: a per-model effort or thinking option wins over the default - adapter: bound the answer_commit history kept across turns - tool CLI: keep spilled results under the state directory, not /tmp - skill mirror: skip a symlinked .claude or .claude/skills; swap copies without deleting the old one first - docs: callers in one directory share the daemon Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: restore shared dispatcher handlers and MCP SDK modules after mocking files Files that reset the native dispatcher or mock the MCP SDK client modules now restore them, so tests that run later in the same process see the real ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: share the Claude Code plugin's skill-copy safeguards in the workspace skill mirror Copies carry a marker naming the skill, so a renamed copy is the user's; nested CLAUDE.md files are not copied; staging uses an .altimate-staging- infix, gets its .gitignore first and is swept when abandoned; an unreadable snapshot, or one missing while it is swapped, leaves the copies alone; and the mirror and the plugin's sync take turns under one mkdir lock in .claude/skills. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: reload daemon connections on edit, release locks in mcp serve, guard read-only warehouse_add - daemon: reload connections when a connections file changes, only when no other call is in flight, with calls arriving during a reload waiting for it; key the socket on the caller's ALTIMATE_CODE_CONN_* environment; resolve --connections once - registry: validate a connections file before closing anything; never release in-memory DuckDB/SQLite connections - mcp serve: release file database locks when no call is in flight - warehouse_add: a read-only session cannot replace a connection named in the active registry, the global or project file, or the credential store - adapter: bound the answer_commit history on every call, not only on commit - skill mirror: keep copies when the snapshot or a skill cannot be read; let withdrawal run with ALTIMATE_CLAUDE_SKILL_SYNC=0 - tool CLI: decide the workspace tools default from the saved link, without a network call - workspace-client: telemetry name and disabled-workspace stub - docs and help text for the daemon, workspace tools and --all-tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep the workspace skill mirror in step with the plugin's sync under concurrent and partial reads The mkdir lock shared with the plugin is renewed by its holder, taken over from a dead one by rename, released only by the holder (an empty token directory), and never waited on past its deadline; a holder that lost it stops before the next swap. The manifest is read without following symlinks and re-read before the removal pass; a listed skill directory that is gone, or credentials that exist but cannot be read, change nothing. One skill's failed swap no longer stops the others; tree comparison streams one file pair at a time; frontmatter whitespace matches Python's; readManifest rejects a skills array. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: publish skill copies only for an unchanged, still-owned snapshot, and withdraw them on sign-out Each copy is swapped in only while the lock is held and the snapshot is the one read and still the signed-in account's, so a mid-copy swap, takeover or sign-out publishes nothing. Swap detection uses the staging directory's mtime, which a rename updates on every platform. A SKILL.md that is not a regular file skips that skill instead of freezing every sync; markers decode strictly with their BOM kept, as the plugin's Python reads them. The sync waits at most 2 s for the lock. Signing out withdraws the project's copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep workspace skill copies when a SKILL.md disappears between its check and its read The plugin's copy treats that as a snapshot changing underneath and changes nothing; the mirror dropped the skill instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a SKILL.md that disappears between its check and its read keeps every copy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: address review findings in the tool CLI, MCP server and workspace tools - `daemon`: requests wait for an in-progress file release, `stop` replies before shutting down, and the daemon key includes `ALTIMATE_DISABLE_SCHEMA_VALUES`, `ALTIMATE_INTEGRATIONS` and `ALTIMATE_DISABLE_WORKSPACE`. - `permissions`: the blocked-statement check runs before the read-permission shortcut. - `claude-md`: a symlinked `CLAUDE.md` is left untouched. - `workspace-tools`: workspace-authored names and descriptions render as one inert line, skills are read through `WorkspaceClient.readSkill`, and the workspace is labelled with `workspaceLabel`. - MCP workspace adapter: an adapter is closed only after the operations using it finish; `serverInstructions` releases file connections it opened. - `outputLimits` reads organisation-managed config and preferences. - `schema-list`: MySQL and Redshift (whose drivers ignore binds) get an escaped schema literal; schema names are sorted before the limit. - `registry.releaseFileConnections` keeps remote and in-memory URI stores. - `sql_execute` batches: `truncated` reports row-limited or clipped items, references resolve against the default warehouse, duplicate labels are rejected, backslashes are escaped for BigQuery, Snowflake, Databricks and ClickHouse, and an unmaterialized spill returns no table. - `answer_commit`: integers keep every digit; coverage and number checks stand down when the run budget is ending (one shared `budgetEnding`). - `warehouse_add` resolves relative store paths against the project root; `warehouse_list` skips only workspace-routed connections in discovery. - `analyst-effort` honours SAP `modelParams` and the configured `model.variants`. - Workspace client: `readSkill` opens with `O_NOFOLLOW`; context lists only skills with a regular `SKILL.md`. `releaseLocalSession` clears every per-session map; skill withdrawal runs in `finally` on purge. - MongoDB `listTables` checks that an empty database exists. - Docs and `--allow-write` help match the actual policies. - Tests for each behaviour change; new suites restore their spies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: never send a doubled quote to BigQuery or Databricks in batch references BigQuery and Spark/Databricks have no `''` escape inside a string literal (Databricks reads `'it''s'` as two adjacent literals, `its`). Batch references now escape the quote with a backslash for those two dialects and keep `''` for the rest. The read-only check does not lex their backslash escape, so such a step is refused instead of running with a wrong value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep data_diff and the tool daemon within read-only and release rules; address review findings - data_diff refuses any source, target or where_clause that is not one complete read-only query (classifier read, single statement, balanced parentheses, no side-effect or dynamic query() calls) before connecting, checks every statement it runs, and opens every connector read-only. sql_check keeps its stricter rules (shared read-only-sql helper). - isBlockedStatement classifies the whole pattern, so leading comments and multi-statement patterns stay hard-blocked. - Redshift values in batch references get their backslashes escaped. - Tool daemon: the idle timer waits for a pending file release; only a request that ran a tool leads to a release, and a stop is answered without waiting for one; the socket is owner-only. - Workspace: refresh clears the active adapter before closing it; a snapshot removed mid-read lists no skills; copies into .claude/skills are opt-in with ALTIMATE_CLAUDE_SKILL_SYNC=1 (the Claude Code plugin sets it). - Analyst effort ignores non-object model params and output_config without effort; managed output preferences resolve files against their own directory; warehouse_add shares the registry's project root; mcp --allow-write help names the hard blocks. - Docs for warehouse_add's local writes, Claude Code skill copies, analyst effort and answer_commit; tests for each change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: dialect-aware hard blocks, failed data_diff reported as an error, daemon shutdown and row-bound fixes - isBlockedStatement classifies with the warehouse's dialect and, without one, checks only the leading statement, so dollar-quoted or backticked text is never hard-denied. - A failed or refused data_diff carries its error, so tool run and MCP report it as one. - Daemon: a run that finishes after stop neither releases files nor re-arms the idle timer; a chmod failure after listen leaves no server or socket behind. - {{label.column}} refuses a step with more than the row bound even when not truncated. - Managed output preferences strip the mobileconfig: prefix before resolving {file:...}. - readSql reuses the masked SQL; tests for each change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: hard blocks classify with each warehouse's dialect; daemon listen cleanup; test fixes - The sql_execute write ask and data_diff's ask now carry their warehouse, one ask per side when source and target differ, so the unoverridable hard-block check classifies each statement in its own dialect (comments, nested comments, dollar quotes). isBlockedStatement is the classifier; without a warehouse it uses the classifier's generic lexing. - A chmod failure after listen destroys accepted sockets and waits for the server to close before removing the socket and rejecting. - Tests: batch limit at the top level, a tick before the after-stop release check, and a managed-preferences fixture shaped like real plist JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: data_diff checks an omitted source in the default warehouse's dialect The diff engine runs an omitted source_warehouse on the registry's first warehouse; the permission check now resolves it the same way, so one warehouse gets one check in its own dialect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: data_diff treats an empty warehouse name as the default, as the diff engine does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: read sql_execute SQL in every dialect an omitted warehouse can reach; fail closed where quoting is uncertain - With no warehouse, sql_execute classifies the SQL under the generic lexing, the registry default's dialect and the dbt manifest's adapter type: a write if any reads it as one, blocked if any blocks it. The dialects are found without starting dbt or loading the handlers (new side-effect-free dbt-adapter-type helper); the write ask names the registry warehouse when no dbt project can take the call. - Lexing: BigQuery '...'/"..." (not raw r'...') and Redshift '...' take backslash escapes; Databricks/Spark/Hive backslashes inside literals are uncertain; with no dialect, a quote after an odd run of backslashes, or a #, is uncertain. - Whenever the SQL cannot be read with certainty, DROP DATABASE, DROP SCHEMA or TRUNCATE anywhere in it (block comments removed) is hard-blocked. - Tests for both reported payloads (BigQuery default, MySQL default with --allow-write), a comment holding ; on Databricks, and the lexer rules; BigQuery batch values with a quote now run escaped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: uncertain SQL is hard-blocked on the words alone; dbt adapter type read without subprocesses - When the tokenizer cannot read the SQL with certainty, DROP followed anywhere by DATABASE or SCHEMA, or TRUNCATE not followed by "(", blocks it: quotes and comments are ignored, so no placement of them can hide the statement. One forward pass. - The backslash-before-quote and # guards apply to every dialect the lexer does not model ("generic", "unknown", oracle, trino ...), not only an omitted one. - dbt adapter type: no Python discovery; the manifest under the project's target-path (DBT_TARGET_PATH), parsed only when it changes; a missing or unreadable manifest, or a malformed saved config, still counts as a dbt project. The registry fallback is read before any await. - Tests for each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: Oracle, Trino and Fabric keep their reads; TRUNCATE /* c */ ( is the function - Oracle, Trino and Fabric are lexed as the supported dialects they are, so backslashes and # in their reads no longer make the SQL uncertain. - TRUNCATE followed by a closed block comment and ( counts as the function. - The dbt adapter-type tests cover the modification-time part of the cache key and the saved altimate-dbt config. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: classify SQL as each warehouse reads it, and keep the hard block on every reachable path - SQL Server/Fabric: no E'' strings; statements without a separator count only in their statement forms - BigQuery triple-quoted and backslash-escaped strings, Snowflake // comments and $$ quotes, Spark comment line continuation, MySQL -- before ASCII whitespace, non-ASCII dollar tags - uncertain comments and backslash runs make SQL uncertain; uncertain SQL is checked word by word - dynamic SQL literals naming DROP DATABASE/SCHEMA or TRUNCATE are blocked; sequence advances are writes - an omitted warehouse is classified under each dialect execution can reach, with dbt following its cached adapter - data_diff and the write ask check the whole SQL, not a preview Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: dbt-adapter-type resets the shared adapter route before each test Another test file in the same bun process can create a dbt adapter and leave its project recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent bdb7dcc commit 2d59604

119 files changed

Lines changed: 12871 additions & 365 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/docs/configure/agents.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ The agent for asking questions about your data. Truly read-only mode for safe da
3030
- **Schema/warehouse/finops**: All inspection tools available
3131
- **Workspace knowledge**: Loads workspace and project skills; reads workspace memory and views this checkout's memory audit log. Can read files bundled with skills, including global skills stored outside the project. Reloading memory syncs the local copy with the workspace, which can remove a local copy of a block the workspace archived. Saving memory is done with `builder`.
3232
- **Config overrides**: A permission config such as `"*": "allow"` cannot turn file edits, SQL writes (including data diffs of arbitrary queries), shell, memory saves, warehouse connection or MCP config changes, training imports, sample project setup, feedback submission, batch tool calls or delegation beyond `data-explorer` back on. A global `"*": "allow"` does turn on MCP and custom tools for analyst, including ones that write (for example a SQL MCP server); to keep them from analyst, put the `"*": "allow"` under `agent.builder` instead of the global config, or deny those tools under `agent.analyst.permission`.
33+
- **Reasoning effort**: On adaptive-thinking models the analyst requests `high` effort unless the agent or model options already choose one. Set `ALTIMATE_ANALYST_EFFORT` (`low`, `medium`, `high`, `xhigh`, `max`, or `off` to send none) to change it.
34+
- **Final answers**: `answer_commit` asks the interactive analyst once per turn to re-check any number that did not appear in tool output before the answer is committed.
3335

3436
!!! tip
3537
Use `analyst` when exploring data to ensure no accidental writes. Switch to `builder` when you're ready to create or modify models.

‎docs/docs/configure/mcp-servers.md‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,43 @@ description: "Configure Model Context Protocol (MCP) servers in Altimate Code to
66

77
altimate supports the Model Context Protocol (MCP) for connecting to external tool servers.
88

9+
## Use Altimate Code tools from another MCP client
10+
11+
Run `altimate-code mcp serve` to expose data tools over stdio. Your MCP client's model handles reasoning; this server runs tools directly without calling a model.
12+
13+
Add this entry to your MCP client's configuration (for example, Claude Code's `.mcp.json`):
14+
15+
```json
16+
{
17+
"mcpServers": {
18+
"altimate-code": {
19+
"command": "altimate-code",
20+
"args": ["mcp", "serve", "--connections", "/absolute/path/to/connections.json"]
21+
}
22+
}
23+
}
24+
```
25+
26+
By default the server exposes `warehouse_list`, `warehouse_add`, `sql_execute`, `sql_materialize` and `answer_commit`, plus `impact_analysis`, `dbt_lineage`, `altimate_core_column_lineage`, `schema_detect_pii`, `data_diff`, `altimate_core_equivalence` and `sql_translate`. Pass `tables` to `sql_execute` to get those tables' columns and value profiles in the same call as its queries. Start with `--all-tools` to expose every data tool, the same set `altimate-code tool list` shows. Without `--connections`, the server reads the usual connection sources and re-reads them while none are configured, so a connections file written mid-session, or a connection added with `warehouse_add`, is picked up without a restart. A connections file is a JSON object keyed by connection name:
27+
28+
```json
29+
{
30+
"local": { "type": "sqlite", "path": "./analytics.sqlite" }
31+
}
32+
```
33+
34+
Relative database paths resolve beside this file. With `--connections`, this file is the only source of connections: global and project `connections.json` files and `ALTIMATE_CODE_CONN_*` overrides are ignored. Omit `--connections` to use normal connection discovery: global and project `.altimate-code/connections.json` files, followed by `ALTIMATE_CODE_CONN_*` environment overrides. Set your client's working directory to the project when using project discovery.
35+
36+
At startup the server lists each connection's name and type in its instructions, read from configuration only: it does not connect to remote or server databases or run queries on them. For local SQLite and DuckDB files (not behind an SSH tunnel) it also lists table names from one catalog query (no row counts); a file with more than 40 tables is noted as such instead. Call `warehouse_list` for columns and sample rows. Listing tools use catalog row estimates where available (including DuckDB); SQLite listings omit row counts. They never count rows by scanning each table.
37+
38+
<!-- altimate_change start — MCP read-only limits and private output lifecycle -->
39+
40+
Source writes are refused by default. Without `--allow-write`, source queries request database-enforced read-only execution on PostgreSQL, SQLite and DuckDB. For other connection types, read-only is enforced by SQL classification only; stored functions with side effects are not blocked — use a read-only database user. The server prints this caveat on stderr at startup and includes it in `warehouse_list` for each affected connection. Add `--allow-write` to enable SQL write permissions; existing hard safety blocks still apply (`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` stay blocked). `warehouse_add` may write local connection configuration (the connections file and the operating system credential store) even in read-only mode. `sql_materialize` always requires a read-only source query and writes only into the server session's temporary `scratch` warehouse. Scratch tables disappear when the server exits. Large results may include a local spill file path that your client can open with any file reader. Spill files use a private directory and are deleted when the server exits. Local `tool_output.max_lines` and `tool_output.max_bytes` settings control output limits; remote agent configuration is not loaded.
41+
42+
<!-- altimate_change end -->
43+
44+
The server writes only MCP protocol messages to stdout and sends logs to stderr. It honors `ALTIMATE_TELEMETRY_DISABLED`.
45+
946
## Local MCP Servers
1047

1148
Run an MCP server as a local subprocess:

‎docs/docs/configure/skills.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,7 @@ The skill list the agent sees marks each skill with where it was found:
341341
When more than one skill fits a task, the agent is told to load the one from the highest source in that order, and to fall back to a lower source only when nothing higher covers the task. So a skill your team added to the workspace is preferred over a similar skill another team left in the repository. The label comes from the skill's location, not from anything written in the skill.
342342

343343
One exception: a project skill with exactly the same name as a workspace skill replaces it, and is listed as `project`. That keeps a skill you are editing in the repository, and may later publish, from being shadowed by the workspace's older copy.
344+
345+
### Copies for Claude Code
346+
347+
In a linked project that has a `.claude` directory, each workspace sync can also write the workspace skills into `.claude/skills/` so Claude Code finds them. Copies are written only when `ALTIMATE_CLAUDE_SKILL_SYNC=1` is set; the Altimate Code plugin for Claude Code sets it when it refreshes the workspace. The copies are marked as owned by Altimate Code and git-ignored, and they are withdrawn when you unlink the project or sign out (withdrawal runs whether or not the variable is set).

‎docs/docs/usage/cli.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ altimate --agent analyst
2828
| ----------- | ------------------------------ |
2929
| `run` | Run a prompt non-interactively |
3030
| `check` | Run deterministic SQL checks (no LLM required) -- see [SQL Check](check.md) |
31+
| `tool` | Run the data tools without a model, optionally via a warm daemon -- see [Tool CLI](tool.md) |
3132
| `serve` | Start the HTTP API server |
3233
| `web` | Start the web UI |
3334
| `agent` | Agent management |

‎docs/docs/usage/tool.md‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
title: "Tool CLI (Headless) — Altimate Code"
3+
description: "Call Altimate Code data tools from any shell, agent or CI job without an LLM. Optional warm daemon keeps connections and the schema cache ready."
4+
---
5+
6+
# Tool CLI (Headless)
7+
8+
Expose the data tools -- warehouse, SQL, schema, lineage, impact, PII, data-diff, FinOps and dbt -- as plain commands. The local data tools use no model or API key, so Claude Code, Codex and CI jobs can call them directly. Linked workspace tools authenticate to Altimate and `workspace_execute` may use backend models.
9+
10+
## List tools
11+
12+
```bash
13+
altimate-code tool list # id and description
14+
altimate-code tool list --json # id, description and JSON input schema
15+
```
16+
17+
`tool` and `mcp serve` share one implementation, so compact schemas, `sql_execute` with `tables`, `query_from` chaining, spilled result files, read-only enforcement and `answer_commit` (with its `request` argument) are consistent in both. Their permission policies differ: the CLI requires `--allow-write` for local configuration writes such as `warehouse_add`, while `mcp serve` allows `warehouse_add` by default. `tool` exposes the full tool list. In a project linked to a workspace it also exposes `workspace_context`, `workspace_skill` and `workspace_execute`; the link is read from the project's saved state, with no network request. Pass `--workspace-tools` to add them to an unlinked project, or `--no-workspace-tools` to leave them out of a linked one.
18+
19+
## Startup context
20+
21+
```bash
22+
altimate-code tool context [--connections <file>] [--json]
23+
```
24+
25+
Prints the analyst method and the connections known at startup (the same base instructions `mcp serve` sends, plus the linked workspace's name, skills and tools when workspace tools are enabled), so a shell agent gets it in one call.
26+
27+
## Run a tool
28+
29+
```bash
30+
altimate-code tool run sql_format --args '{"sql":"select a,b from t","dialect":"postgres"}'
31+
altimate-code tool run warehouse_list
32+
altimate-code tool run schema_inspect --args-file inspect.json --json
33+
echo '{"query":"select 1"}' | altimate-code tool run sql_execute --args -
34+
```
35+
36+
| Option | Description |
37+
|---|---|
38+
| `--args '<json>'` | Tool arguments as a JSON object. `-` reads them from stdin. |
39+
| `--args-file <path>` | Read the arguments from a file. |
40+
| `--allow-write` | Allow writes. Without it, only reads run. |
41+
| `--no-daemon` | Run in this process instead of using the daemon. |
42+
| `--connections <file>` | Use only this connections JSON file instead of the project and global connection configs. Defaults to `$ALTIMATE_TOOL_CONNECTIONS`. |
43+
| `--workspace-tools` / `--no-workspace-tools` | Force the linked workspace tools on or off. Unset, they are on when the project is linked. |
44+
| `--json` | Print `{"status","title","output","metadata"}`. Otherwise only `output` is printed. |
45+
46+
Arguments are validated against the tool's schema. An unknown tool, invalid arguments or a failing tool exits non-zero.
47+
48+
Only the data tools are exposed. Coding tools (`bash`, `read`, `edit`, ...), `dbt_pr_review` (use `altimate-code review`), memory and training tools are not available here.
49+
50+
## Permissions
51+
52+
Runs are read-only by default. Reads such as `sql_execute` on a SELECT and `schema_inspect` are allowed; anything else is refused with `Permission denied: <kind>. Re-run with --allow-write to enable writes.` Tools that change local configuration (`warehouse_add`, `warehouse_remove`, `warehouse_install_driver`) also need `--allow-write`.
53+
54+
`DROP DATABASE`, `DROP SCHEMA` and `TRUNCATE` stay blocked even with `--allow-write`.
55+
56+
## Daemon
57+
58+
`tool run` starts a background process per project directory and CLI version on first use, and reuses it afterwards so warehouse connections and the schema cache stay warm. If it cannot be reached within about 3 seconds, the call runs in-process. `scratch` tables created by `sql_materialize` live as long as the daemon does, with one exception: with `--connections`, an edit to that file is reloaded by replacing the registry entirely, which also closes and empties `scratch`, so a `--connections` daemon's `scratch` tables only live between connection file edits. Every caller in that directory shares the daemon, so concurrent agents share its `scratch` tables and its `answer_commit` history; use `--no-daemon` to keep a caller separate.
59+
60+
The daemon reloads its connections when the global or project `connections.json` (or the `--connections` file) changed since its last reload. The check runs on a call that has no other call in flight, so an edit made while calls overlap is picked up by the next call that runs alone; calls that arrive during a reload wait for it.
61+
62+
```bash
63+
altimate-code tool daemon status
64+
altimate-code tool daemon stop
65+
```
66+
67+
`status` and `stop` locate the running daemon the same way `run` does: by project directory, CLI version, the `--connections` file (if any), whether workspace tools are on, and the caller's `ALTIMATE_CODE_CONN_*`, `ALTIMATE_DISABLE_SCHEMA_VALUES`, `ALTIMATE_INTEGRATIONS` and `ALTIMATE_DISABLE_WORKSPACE` environment variables. Run them with the same flags and variables as the call that started the daemon, or they will report no daemon running for this project even though one is.
68+
69+
| Setting | Description |
70+
|---|---|
71+
| `ALTIMATE_TOOL_DAEMON=0` | Always run in-process. |
72+
| `ALTIMATE_TOOL_DAEMON_IDLE_MS` | Idle time before the daemon exits (default `600000`). |
73+
74+
The daemon listens on a local socket (a named pipe on Windows) under the Altimate Code state directory and removes it on exit.
75+
76+
## Claude Code routing
77+
78+
For clients that cannot use the `altimate-code` MCP server, the Claude Code plugin adds CLI routing guidance at session start: `altimate-code tool context` followed by `altimate-code tool run` for data questions, and `altimate-code run` for long multi-step jobs. To persist the same guidance in the repository, run `altimate-code link --claude-md`; it writes a managed block to the project's `CLAUDE.md` and never changes content outside the `<!-- altimate-code:routing start -->` / `<!-- altimate-code:routing end -->` markers.

‎docs/mcp-workspaces.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Workspaces for external coding clients (experimental)
2+
3+
`altimate-code mcp serve --workspace-tools` adds workspace context, named skills and known read operations from the linked workspace's existing `datamate` engine. The default MCP server remains unchanged. This draft builds on the experimental MCP server; no rollout or merge is implied.
4+
5+
The client must run in the intended project directory. Sign-in and linking may complete after connecting: `workspace_context` returns structured setup status and the catalog remains stable. `workspace_context` returns the current workspace, bounded project memory, a summary of skills/knowledge/integrations, and skill names. `workspace_skill` reads a named skill from the validated snapshot; refresh missing skills with `altimate-code workspace refresh`. It rejects traversal, symlinks, oversized skills and a changed snapshot/account.
6+
7+
The engine is pinned with its existing `--datamate` option. Only knowledge, supported metadata reads and SQL reads are exposed; arbitrary tools, writes and engine memory mutations are excluded. SQL uses the existing classifier. Unsupported SQL argument schemas are withheld. The engine authenticates through its normal local configuration. Use read-only warehouse credentials: classification is not database isolation. Local SQL remains available if workspace authentication, linking or engine startup fails. An account/binding change invalidates stale receipts. Read fresh context and use the new receipt; reconnecting MCP is not required.
8+
9+
For non-model clients:
10+
11+
```
12+
altimate-code workspace-client list
13+
altimate-code workspace-client context --directory /absolute/project
14+
printf '%s' 'Use booked ARR for recurring revenue.' | altimate-code workspace-client remember --directory /absolute/project --token=<context-workspace-token> --id=metrics/arr
15+
```
16+
17+
These commands output JSON. A context receipt binds account, workspace and project. `remember` requires that receipt, a valid memory id and 1–2,048 characters on stdin; it saves exactly one project block. It suppresses the ordinary fire-and-forget mirror, then uses the existing sync path with the explicitly selected binding. Requests pin credentials across async API/index operations. A backend failure after local persistence reports `savedLocally: true, cloudConfirmed: false`; retrying is a user decision. Confirmed cloud publication is reported only when the sync reports one successful write. No model-callable memory-write tool or bulk-sync operation is added.
18+
19+
Workspace context adds bounded data to a client's prompt; skills load on demand. Explicit knowledge calls can use backend models, memory publication can invoke the existing backend extractor, and warehouse calls retain warehouse costs. There is no new reasoning agent or per-row model loop. A CLI link can normally backfill existing memory; clients that expose linking without a backfill confirmation must disable memory for that subprocess, as the Claude plugin does. Do not pass `--yes` unless replacing a link was explicitly requested.
20+
21+
Verification covers unit contracts, a real stdio protocol fixture and existing SQLite MCP behavior. Production read-only testing listed visible workspaces and loaded matching context through a temporary local project. Production bindings and memory were not written. A non-empty cloud memory round trip and real workspace-engine SQL/knowledge calls remain rollout checks.
22+
23+
## Automatic-client setup and stable gateway
24+
25+
The opt-in workspace catalog is always `workspace_context`, `workspace_skill` and `workspace_execute`, including when initially unlinked. `workspace_context` reports `needs_auth`, `needs_workspace`, `disabled` or `unavailable` without returning raw credential errors. A ready context includes the workspace receipt. Context loads coalesce and cache for 30 seconds; identity is verified on every use. `refresh: true` bypasses that cache.
26+
27+
Pass `include_tools: true` to request lazy engine discovery. The response includes `engineStatus` (`ready`, `needs_engine` or `unavailable`) and an operation catalog with the engine's actual input schemas. After explicit engine installation, request both `refresh: true` and `include_tools: true`. `workspace_execute` takes `workspace_token`, `operation` and an `arguments` object matching the returned schema. Unknown operations, invalid schemas, stale receipts and source-write SQL are refused. `workspace_skill` also requires `workspace_token`.
28+
29+
This gateway intentionally changes the host permission surface: allowing `workspace_execute` permits its entire closed read-operation allowlist, including knowledge requests that can invoke backend models. It does not inherit per-operation approvals from the old dynamically discovered tool names. No workspace memory write is exposed as an MCP tool.
30+
31+
`workspace-client context` returns the same structured setup status. `workspace-client engine-install` invokes the existing native installer; a UI must require an explicit user action before invoking it. The Claude pilot runs native sign-in and link actions from its first-use panel. Linking disables memory backfill for that subprocess. Authentication output stays outside model context. Default MCP startup without `--workspace-tools` is unchanged.

‎docs/mkdocs.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ nav:
104104
- TUI: usage/tui.md
105105
- CLI: usage/cli.md
106106
- SQL Check: usage/check.md
107+
- Tool CLI: usage/tool.md
107108
- Web UI: usage/web.md
108109
- CI: usage/ci-headless.md
109110
- IDE: usage/ide.md
@@ -186,6 +187,7 @@ nav:
186187
- TUI: usage/tui.md
187188
- CLI: usage/cli.md
188189
- SQL Check: usage/check.md
190+
- Tool CLI: usage/tool.md
189191
- Web UI: usage/web.md
190192
- CI: usage/ci-headless.md
191193
- IDE: usage/ide.md

0 commit comments

Comments
 (0)