MCP tools for agents
Status: ShippedCodeSense runs an MCP server, so the agents it launches can work with the app directly: check a story before saving it, write a dashboard and lint it, open a terminal and run your tests, or ask you a question in the UI.
How agents connect
The bridge is a small binary, codesense-bridge, in ~/.codesense/bin. It relays MCP over stdio to the running app through a local socket (~/.codesense/mcp.sock). There’s no network listener.
You don’t set it up. Every agent CodeSense launches gets the bridge for that session only, from a machine-local file outside your folders: Claude Code through --mcp-config (with its status hooks in a per-launch --settings file), Codex through its own launch flags or a per-session CODEX_HOME. Nothing is written into the folder the agent runs in for this. An agent can narrow the other servers it sees, but the CodeSense bridge is always included.
claude and codex typed in any CodeSense tab
Type plain claude or codex in any CodeSense terminal tab, on this Mac or on a machine, and it starts connected to CodeSense, like an agent CodeSense launched: the codesense MCP server (your inbox and every tool) and, for Claude, CodeSense’s status and new-mail hooks, so the tab’s status dot follows it and new messages reach it. It works in any folder. On a machine, its remote MCP permission must be on, as for any remote agent. Your own arguments pass through unchanged.
The tab’s shell does it. CodeSense’s shell integration (zsh, bash and fish) defines claude and codex functions that add --mcp-config and --settings for Claude and one -c mcp_servers.codesense=… override for Codex. They add nothing when:
- you pass your own
--mcp-config,--strict-mcp-configor--settings(yours win; CodeSense’s hooks are not merged into your settings), - a
.mcp.jsonin the folder or a parent already has acodesenseserver (on this Mac; on a machine the tab’s own server replaces it, since a folder’s entry can’t reach the tab), - you run a subcommand such as
claude mcp list, or Codex with--remote, - you already define your own
claudeorcodexfunction, or - you’re outside a CodeSense tab.
Run command claude or command codex for the plain CLI. A Codex typed by hand gets the MCP server but no status dot. Tabs opened before CodeSense was updated don’t have it; open a new one. On a machine it also needs the terminal server installed by this version of CodeSense.
So that an agent you type yourself in a terminal also connects, the worktrees and workspace folders CodeSense creates for stories, branches, epics and team workspaces carry a codesense entry in .mcp.json and .codex/config.toml, plus the status hooks in .claude/settings.local.json. Each such worktree ignores those files on its own, through an exclude file only that worktree reads, so they never show in its git status and are never committed, while your main checkout still shows its own .mcp.json. That worktree keeps your own core.excludesFile patterns too. CodeSense never writes them into your own checkouts, your team’s context repo or any other folder you launch an agent in.
If the app isn’t running, tools fail with CodeSense is not running. Start CodeSense to use MCP tools. If the app restarts mid-session, the bridge reconnects on its own for up to 30 seconds.
What agents can do
The most useful tool groups:
Stories and plans
| Tool | Does |
|---|---|
get_story_format | Returns the story authoring contract. Agents call it before writing a story. |
validate_story | Checks a story file and returns findings, such as a type that isn’t story, bug or spike, a missing or unknown source_repo, an unknown status, a duplicate story_id or a depends_on that doesn’t resolve. |
read_epic_repos / set_epic_repos | Read or rewrite an epic’s repos: list. |
hydrate_story / list_hydrated | Bring a story’s epic into a remote workspace and list what’s hydrated there. Local workspaces get context from the agent’s selected context. |
Terminals
| Tool | Does |
|---|---|
open_terminal_tab | Opens a terminal tab in a workspace that’s already open and returns a handle. |
run_in_terminal_tab | Runs a command in that tab once its prompt is ready. |
read_terminal_tab | Returns the tail of the tab’s output. |
send_to_session | Sends text to an agent session opened with open_workspace. |
With these, an agent can start npm run dev in one tab, run the tests in another, and read both, the way you would.
Talking to you
| Tool | Does |
|---|---|
post_agent_message | Posts a message into the app without waiting. |
ask_user | Asks you a question in the app and waits for your answer. |
signal_progress / signal_done | Reports a phase, or records that the task finished with success, failed or needs-attention and a short summary. signal_done records and notifies; it doesn’t move the story or run any check. |
Pages, panes and canvases
| Tool | Does |
|---|---|
get_live_data_contract | Returns how to write a live HTML page: the action verbs, routine results and a starter page. |
validate_live_data_doc | Lints a page before it’s saved (embedded tokens, misaddressed verbs, unsafe innerHTML and more). |
pane_list / pane_write / pane_open | List, write and open custom right panes: HTML files at <space>/panes/<id>.html. |
apply_canvas / render_canvas | Draw an Excalidraw diagram from an outline, and render it to PNG so the agent can check it. |
The app itself
get_viewer_state, set_viewer_state and open_workspace let an agent see what’s open and open a story workspace (headed, in the background, or headless).
The full, generated list is in the MCP tools reference.
Configure and use a remote shell
Machine operations use the same authorization, SSH setup and Interactive gate as the Machines tab. Discover saved machines with list_machines({"orgSlug":"acme","team":"default"}) and local SSH choices with list_ssh_sources({}).
For a new private account, use this input object with test_machine_connection, enroll_private_machine_account, install_terminal_server and save_machine:
{ "mode": "private-account", "label": "Development server", "host": "dev.example.com", "port": 22, "username": "dev", "signIn": { "method": "agent" }}- Call
test_machine_connection({"input": input}). Verify the returned host fingerprint through a trusted channel, then addtrustedHostKeyFptoinputand test again. A returned fingerprint alone is not approval to trust it. - Call
enroll_private_machine_account({"input": input}), theninstall_terminal_server({"input": input}). Use the same verified draft throughout. For an existing machine, include itsmachineIdin each call. - Call
save_machine({"input": input,"share":{"kind":"personal"},"connect":true}). Save’sconnectflag validates setup; then callconnect_machine({"machineId": saved.machineId})for actual runtime admission. Auto-connect stays off unless explicitly enabled withset_machine_auto_connect.
input and saved above denote the actual JSON object and returned result, not literal JSON strings. For a key file, use signIn: {"method":"keyFile","keyPath":"/absolute/path/to/key"}. For encrypted keys, capture_machine_passphrase({"keyPath":"/absolute/path/to/key"}) opens the existing secure dialog and returns a credential reference. Never place a password, private key or passphrase in tool arguments.
Use get_viewer_state({}) to choose an existing authorized workspace and its exact team key/workspace ID. open_workspace({"story_id":"SW-EXAMPLE-1"}) remains the existing local story-workspace operation; it does not select a remote machine. Create a remote shell in the selected workspace with a fresh UUID and an explicit remote directory:
{ "operation": "create", "requestId": "824c5c38-9b51-4d8a-929e-18078a8fdbea", "machineId": "MACHINE_ID_FROM_SAVE", "teamKey": "acme:default", "workspaceId": "EXACT_EXISTING_WORKSPACE_ID", "cwd": "/home/dev/project", "kind": "shell", "headed": true}Pass that object to open_remote_tab. Keep its opaque handle and the original requestId. Use run_in_terminal_tab({"handle": handle,"command":"pwd"}), then read_terminal_tab({"handle": handle}). Remote reads return the tab’s emulator text as output, a decimal-string head, and reset (always true: every read is the whole text, not a byte delta). An optional since from an earlier head is accepted; a cursor beyond the current head is refused.
A remote run returns submitted, notSent, unknown or refused. unknown with submitted:null means the command may have executed; inspect output and never automatically replay it. An unknown create similarly keeps its original identity. Recover with open_remote_tab({"operation":"attach","machineId": machineId,"teamKey": teamKey,"requestId": originalRequestId,"headed":true}), or supply handle instead of requestId, never both. Attach does not create another process. headed:false leaves UI focus alone.
disconnect_machine, connect_machine and retry_machine take {"machineId": machineId}. After reconnect, attach using the original handle/request ID. Missing admission or uncertain launch correlation is not evidence that a process ended. Remote kind:"agent" is refused until managed-agent preparation is available; an ordinary shell command does not establish that managed callback contract.
Let a remote agent use desktop CodeSense
In Machines → Manage, enable Allow agents on this machine to use CodeSense and confirm. This setting belongs to your private machine binding and starts off. The equivalent desktop MCP operation is set_machine_remote_mcp_control({"machineId": machineId,"enabled":true}); a remote agent cannot grant itself this permission. A permission change retires the existing connection, so reconnect before opening a new remote shell.
Installing an updated terminal server leaves an already-running daemon and its sessions intact. If that daemon predates reverse MCP, use the explicit server replacement workflow before launching a remote agent; ordinary reconnect keeps the existing daemon and cannot add a newly negotiated capability. Review the replacement’s session consequences before proceeding.
New shells opened after permission is enabled receive an authenticated session broker and the installed codesense-bridge. Existing shells are not respawned to add it. Typing claude or codex in such a shell is enough: it starts connected to CodeSense, with Claude’s status and new-mail hooks (see claude and codex typed in any CodeSense tab). The shell also exposes CODESENSE_MCP_BRIDGE (the installed executable), CODESENSE_MCP_CONFIG (a CodeSense-only configuration unless a saved agent named local MCP servers), CODESENSE_CLAUDE_SETTINGS (Claude’s hook settings) and CODESENSE_CODEX_MCP (Codex’s server override) for other MCP clients. Standard stdio clients may close inherited descriptors: the bridge reacquires its broker from kernel-verified ancestry beneath the still-live managed shell. The generated configuration includes explicit nonsecret remote socket arguments, so a sanitized child environment works. No desktop provider API key or third-party MCP configuration is copied to the remote host. Remote provider installation and sign-in remain separate.
A permitted remote agent can discover and call these existing tool families:
- Contracts:
health_ping,get_story_format. - Bound context:
list_source_repos,sense_sync_list_layers,sense_sync_adopt,sense_sync_read_layer_file,sense_sync_read_lockfile. - Machine services:
list_machines,test_machine_connection,enroll_private_machine_account,install_terminal_server,save_machine,set_machine_hidden,set_machine_auto_connect,unshare_machine,remove_machine,connect_machine,disconnect_machine,retry_machine. - Terminals and scoped desktop discovery:
get_viewer_state,prepare_remote_workspace,remote_workspace_fetch,open_remote_tab,run_in_terminal_tab,read_terminal_tab.
These calls retain existing tool gates. Context calls use the originating session’s captured team even when the desktop switches teams. get_viewer_state returns the bound team’s desktop workspace metadata. Remote-origin terminal run/read accepts scoped remote handles only; local terminal handles and local terminal creation are unavailable through this bridge. Desktop-origin local tools are unchanged. open_remote_tab uses the explicit remote machine/directory contract. sense_sync_read_lockfile requires the bound desktop workspace’s path and repository, not a remote filesystem path. Machine setup keyPath likewise names a key on the desktop.
To publish chosen context, call sense_sync_adopt with codeRepoPath, codeRepoSlug: {"owner":"acme","repo":"api"}, logicalName, targetLayer: "TeamProjection" (the team’s content; the old "TeamWorkspace" still means the same, "TeamRepo" is refused — publish to a repo’s tier with reverse_sync_worktree {to:"repo"}) and UTF-8 content. The adopt tool carries bytes; it does not read codeRepoPath. Read them back with sense_sync_read_layer_file({"codeRepoSlug":{"owner":"acme","repo":"api"},"logicalName":"notes/example.md","layerId":"TeamProjection"}). An optional expectedLayerHash is a 64-digit blake3 hash of the prior layer bytes: a mismatch returns rejectedConflict without overwriting. Omitting it retains the existing overwrite behavior.
Tools needing an unimplemented remote session-status or desktop-path adapter are omitted from remote discovery and refused on invocation. This includes managed-agent callbacks, arbitrary desktop path operations, and granting remote control. The bridge currently fails closed on disconnect; it does not replay a request. If a connection closes during a mutation, its outcome may be unknown—inspect the result before retrying. Disabling permission denies subsequent calls. Existing terminal processes remain separate from that permission.
Use selected desktop MCP servers from a remote agent
The same personal Allow agents on this machine to use CodeSense permission also allows the local MCP servers a saved agent names. Start the saved agent in a new remote terminal (agentKey plus preparedWorkspaceHandle):
{"operation":"create","requestId":"a fresh UUID","machineId":"personal-machine-id","teamKey":"org/repo:team","workspaceId":"an existing workspace ID","cwd":"/home/dev/project","kind":"agent","agentKey":"acme/repo/backend/reviewer","preparedWorkspaceHandle":"a verified prepared workspace handle"}CodeSense reads the agent’s effective MCP configuration on the desktop, applies its mcpServers list, and captures the resulting endpoints for this session. A launch without a saved agent gets CodeSense only. An empty list selects only CodeSense. Missing or disabled servers, unsupported configuration fields and unavailable local environment values refuse preparation. A config change requires a new shell; an existing session is not retargeted.
The remote shell’s CODESENSE_MCP_CONFIG contains one bridge entry for each selected server, with its original server name. A separately installed and signed-in Claude CLI loads it when you type claude in the shell. Other clients should register the generated per-server bridge commands using their normal MCP configuration. Direct bridge integrations use "$CODESENSE_MCP_BRIDGE" --remote-socket "$CODESENSE_MCP_SOCK" --server-id SERVER_NAME; omitting the selector means codesense. The remote configuration contains neither desktop commands/environment nor endpoint URLs, headers or credentials.
Supported desktop configurations are Claude JSON mcpServers and Codex TOML mcp_servers: stdio command, args, env, optional cwd; or Streamable HTTP url with headers/http_headers, env_http_headers, or bearer_token_env_var. ${NAME} expands from the desktop environment. enabled, enabled_tools and disabled_tools are enforced. Unsupported policy/authentication fields are refused. Stdio relay currently requires a Unix desktop; other platforms refuse that transport before launch. HTTP is not restricted to Unix.
A local executable can use its own normal local login. Explicit HTTP authentication stays on the desktop. OAuth caches owned by an agent CLI are not imported: an HTTP authentication failure reports that local authentication is required. Provider CLI installation/sign-in on the remote host remains separate. Existing local CLI approval decisions are not inherited; the remote client’s normal prompts still apply.
Each bridge channel owns one persistent local process or HTTP session. Standard tools, prompts, resources, notifications and cancellation are relayed; roots/resource paths are not rewritten between machines. A remote caller can create or control another remote shell only when that shell’s live captured MCP grant is a subset of its own; unavailable grant evidence is refused. Read-only terminal output does not grant access to those servers.
Limits are 16 selected server IDs including CodeSense, 32 open relay channels per admitted connection, 1 MiB per MCP message, bounded queues, and 120-second request deadlines. Idle channels remain tied to current machine/session authority. Disconnect or permission retirement closes downstream sessions and reaps local stdio children. A lost or expired HTTP mutation may already have executed; the relay does not reinitialize and replay it automatically.
Prepare a remote Git workspace
This operation needs the current terminal server. Install it and explicitly replace an older running daemon before first use; reconnecting preserves the running server and its capabilities.
prepare_remote_workspace asks CodeSense to prepare a configured Git repository and an owned worktree on the selected machine. It reuses the recorded desktop checkout after verifying its configured remote identity. If the selected committed ref exists locally, that local snapshot supplies the pinned commit; it is not a claim that the provider branch is current. If the ref is missing, prepare it in the local repository with your agent or CLI and retry; CodeSense does not fetch it. Desktop HEAD, index and uncommitted files are untouched. Provider tokens, PATs and SSH private keys stay on the desktop; the machine receives only Git objects.
The remote managed repository is reused across worktrees. If it already has the selected commit, no bundle is transferred. Otherwise CodeSense sends an incremental bundle when the daemon confirms an available ancestor, or a full initial bundle when none is available. Transfers use 1 MiB base64 chunks with a pinned commit, size and digest, a 64 GiB bundle limit and disk headroom checks. Git preparation has a 30-minute deadline. Interrupted transfers resume the same immutable operation. Completed bundle payloads are removed on both machines after durable settlement; small receipts remain so retries do not need the deleted bundle. Ordinary remote push, LFS and submodule network access are separate capabilities; preparation does not provision remote credentials.
Pass requestId (UUID), machineId, teamKey, an existing workspaceId, structured sourceRepo (owner, repo, provider, host from list_source_repos), baseRef, branch, absolute repoPath and worktreePath. A bare baseRef selects refs/heads/<name>; supply a full refs/... name for a tag or remote-tracking ref. Destination parents must already exist and be owned by the private remote account.
Keep the returned workspace handle, cwd and resolved commit. Open a shell with open_remote_tab at that exact cwd; opening a terminal and hydrating context are separate operations. Git preparation does not select or copy context. Use the hydration tools below to bring context in afterward. Repeat an uncertain preparation with the same request to reconcile its durable receipt; do not delete partial work or use a new request to bypass uncertainty. Existing unowned destinations and symlinks are refused. Existing worktree edits are preserved.
Hydrate and reconcile a remote workspace
Use the handle returned by prepare_remote_workspace as workspaceHandle:
{"target":{"kind":"remote","machineId":"personal-machine-id","workspaceHandle":"remote-workspace-…"}}Pass this object to hydrate_worktree to copy context into that workspace. It reads the current saved context on the desktop, including uncommitted edits; no context commit or push is required. It only copies in: it never overwrites a file the repo tracks or a copy that was edited there. To send edits the other way, preview with preview_reverse_sync_worktree and publish with reverse_sync_worktree, which accept the same target; a team file that changed since the copy comes back as a conflict and is never overwritten. Nothing runs between these calls: there is no watcher in either direction.
For one story, call hydrate_story with that target and storyId. Its whole epic is selected and recorded in the destination’s hydration state. An optional policy uses the existing closure fields referenceDepth, ancestryLevels, siblingBreadth, and maxMembers. Call list_hydrated with the same target to read its lazy state, requested epics, and tracked logical names. An unrecorded workspace remains eager; requesting a story does not silently turn it into a lazy workspace.
Remote hydration calls finish with completed, conflict, or incomplete, counters, and errors. Inspect an incomplete result; repeating the same target reconciles any retained file-operation receipts before issuing new effects. These tools do not accept a remote absolute path or a caller-supplied repository override. Existing local path/session inputs remain available to desktop-origin callers.
Bring remote work home
After committing in a workspace that lives on a machine, call remote_workspace_fetch to bring those commits into this Mac’s local clone of the repository. Name the workspace by machineId and workspaceHandle, or by the storyId or workspaceId of an open workspace:
{"storyId":"SW-1"}The commits the machine’s checkout is on arrive as refs/remotes/codesense-machine-<machineId>/<branch>. The local branch of the same name is created if it is missing, or fast-forwarded when that loses nothing. It is never merged, rebased or force-updated. A local branch with its own commits (diverged), a checkout of it with uncommitted changes (dirtyCheckout), or a checkout of it in another worktree (checkedOutElsewhere) is reported in reason and left as it was. The result lists the commits that arrived (sha, subject), the fetched ref, and localBranchState. Only committed work on the branch the workspace was prepared on crosses; uncommitted files stay on the machine. A machine reporting another branch is refused, and the commits pass Git’s object checks before any ref moves.
An agent on the machine can call it through the remote MCP bridge, so work comes home without forge credentials on the machine. It fetches only into the clone of the team the workspace belongs to. It needs a current terminal server; an older one is refused with a request to update it. Push and open pull requests from the local clone with your own Git or forge tools.
Repository ownership
CodeSense observes attached local repositories. Use your agent or Git CLI to clone, fetch, commit, push, and open pull requests, including work in remote workspaces. CodeSense does not publish commits or supply forge credentials for repository transport; remote_workspace_fetch only brings a machine’s commits into the local clone. Prepare missing repositories and refs externally before attaching or creating a local worktree.
Local changes, history, explicit worktree creation, and bidirectional file hydration remain available. get_publish_set retains its legacy name and returns local changed-file data only. The former Git synchronization and remote publication tools have been retired.
Run a remote task with a local agent account
start_remote_agent_task runs the official agent on your desktop and its workspace commands/files on the selected remote machine. Provider login stays with the local official CLI; no provider tokens or login files are copied to the remote machine. This is a task API, separate from an interactive remote CLI terminal.
{"requestId":"a fresh UUID","workspaceHandle":"remote-workspace-…","provider":"codex","prompt":"Implement the change and report the result"}Use the existing handle from prepare_remote_workspace. Select provider:"codex" or provider:"claude". Omit both accountId and accountGeneration to use the existing active-account resolver, including SystemDefault when no usable active account is selected, or supply both from an existing CodeSense agent account. A missing or retired explicit account is refused; a running task never switches accounts or falls back to API billing.
Keep the returned taskHandle. Poll read_remote_agent_task with that handle and the previous cursor for new output. cancel_remote_agent_task requests cancellation; poll until settlement. unknown means completion or cleanup could not be confirmed. Retry a lost start reply with the same requestId and unchanged arguments, never a new identity to replay uncertain work.
The initial supported desktop is Unix. Codex requires official CLI 0.159.3 or a newer 0.x release and a native ChatGPT login. Claude requires official Claude Code 2.1.141 or newer and a native Claude subscription login. Both currently use the execution runtime of Codex 0.159.3 or a newer 0.x release on the remote machine, without requiring a Codex login there. Remote workspace commands have no desktop execution fallback. Claude workspace command execution has remote shell privileges; it is not an additional filesystem sandbox.
Tasks receive CodeSense tools scoped to the exact workspace, and no other desktop MCP servers: those come with a saved agent, started in a remote terminal. Ambient native MCP configuration is excluded. This task workflow does not add a new remote worktree UI or reuse login inside an arbitrary remote terminal.
Guardrails
- Handles, not session ids. Tools that drive the UI take opaque handles returned by
open_workspaceoropen_terminal_tab. A raw session id is refused, so an agent can’t reach a session it wasn’t given. - A gate in the app. Tools that open workspaces or send to sessions pass a check inside CodeSense first. It can refuse, and it returns
busywhen the background agent limit is reached. - No secrets through tools. No CodeSense tool accepts a service token. Agents use their own tools and credentials for outside services.
Other MCP clients
The bridge is a standard stdio MCP server, and the tools are the same whatever calls them. The built-in wiring covers Claude Code and Codex.
Attach your existing context checkout
From the welcome screen, sign-in screen, or organization picker, choose Attach context repository and select an existing CodeSense context checkout. No forge account, token, or origin URL is required. CodeSense uses the canonical folder directly, including saved uncommitted and untracked files. It does not copy the checkout, change remotes, or fetch, pull, push, or commit on attachment. Manage Git through your own agents or tools.
Local MCP callers can use the same workflow before selecting a team or signing in:
| Tool | Arguments | Result |
|---|---|---|
attach_context_repository | { "clonePath": "/absolute/path/to/context" } | Registered org identity, canonical root and loaded registry |
list_context_attachments | {} | Registrations, including unavailable roots and availability_error |
detach_context_repository | { "orgSlug": "my-org" } | Removes registration only |
The organization identity comes from org.yml, regardless of the folder name. Attaching the same root again is safe; another root claiming the same organization fails without replacing it. The app refreshes and selects a newly attached organization after local MCP attachment. Local profiles preserve existing numeric data namespaces, including the local namespace. No service login is required.
Attachment tools are local desktop operations and are unavailable to remote agents. Existing remote transport authentication and machine trust still apply. A missing checkout remains listed with its error: restore it at its registered location and choose Refresh context repositories, or choose Detach. Refresh also retries repaired registry files without restarting the app. Detaching never deletes checkout files. A corrupt registration file produces an error rather than silently falling back to a managed copy.
Service access
Use your agent’s own service tools to collect data, then publish routine result snapshots for dashboards. Local profile selection does not authenticate a service account; agent CLI login, SSH machine setup, and MCP transport authorization remain separate.
How we know this is accurate 15 sources
Checked against the app at 1b3d5a69d73473db077dfb44f4390b42cded6bd4.
Feature inventory
-
D-17 -
D-42 -
D-43 -
D-44 -
D-45 -
B-15 -
B-31 -
B-44 -
B-52
Code
-
src-tauri/crates/codesense-bridge/src/main.rs -
src-tauri/src/commands/mcp_scope.rs -
src-tauri/src/commands/managed_worktree_config.rs -
src-tauri/src/mcp/handler.rs -
src-tauri/src/mcp/story_authoring.rs -
src-tauri/src/commands/interactive_gate.rs