The first two posts in this series covered getting a terminal on your Mac from your phone, and the mental model behind all the moving parts. Both were about Claude Code: a terminal tool where you watch each step happen. This post covers the other path, where you describe what you want, send it from your phone, and come back to a finished result.
That path runs through Dispatch and Cowork. Most failures I’ve seen with them come from not understanding that there are two separate execution environments behind the scenes, each with different capabilities and blind spots.
What Cowork is
Inside the Claude Desktop app, Cowork is the mode where Claude gets access to your files and connected services. Grant it a folder, and it reads, creates, edits, and deletes files there. Connect an MCP service like Linear or Obsidian, and it can interact with those too. Skills (procedural templates for things like spreadsheet creation or PDF generation) load automatically when relevant.
All of this runs inside a sandboxed Linux VM on your Mac, which is an important detail. The sandbox executes code, installs packages, and runs scripts. But it’s isolated from your Mac’s native environment: your Homebrew installations, shell configuration, SSH keys, and most of your filesystem are invisible to it.
What Dispatch is
Open the Claude iOS app, go to the Dispatch tab, type what you need. The task gets routed to the Cowork session on your Mac, runs there, and you see the result on your phone.
You pair the two devices once via QR code, which means you need to see your Mac’s screen (physically or through VNC). After that initial scan, all interaction happens from the phone.
By default, tasks run in the Cowork sandbox. But when something requires your Mac’s filesystem or CLI tools, a separate code task gets spawned that runs directly on your Mac through Claude Code. Which environment a task lands in depends on the prompt and what resources it needs, and the two environments have very different capabilities.
Sandbox tasks
Most work runs here. The sandbox has access to any MCP connector you’ve set up (Linear for issues, Obsidian for vault notes, Google Drive, Slack), can execute Python, Node.js, and bash, install packages, and create files. Web search and fetch work. Claude in Chrome works if enabled. You can create and manage scheduled automations on a cron schedule, all within the sandbox.
What it can’t reach: your Mac’s filesystem beyond the mounted folder, anything installed via Homebrew (gh, git, tailscale, brew), your shell’s environment variables or PATH, SSH keys, or auth tokens stored on your Mac. Certain domains including github.com are blocked from the sandbox network, so GitHub API calls and repo cloning won’t work from here either.
Code tasks
For anything that needs the host machine, Dispatch spawns a code task. These run on your Mac via Claude Code with full filesystem access (scoped to a working directory), every CLI tool you’ve installed, your complete shell environment, and the ability to do git operations like cloning, branching, committing, and pushing.
The tradeoff: code tasks have no access to MCP connectors. Linear, Obsidian, Google Drive, and Slack are managed by the Cowork desktop app and aren’t available to Claude Code sessions. Scheduled task management is also exclusive to Cowork.
Where the split causes failures
The tasks that break are usually the ones that need something from both sides.
“Fetch my GitHub activity and write a summary” needs gh (host only) and might need to write to Obsidian (MCP only). Neither environment handles both, so you split it: a code task runs gh and writes the output to a file, then a sandbox task picks up that file via Obsidian MCP and generates the summary.
“Create a Linear issue for this bug” stays entirely in the sandbox. The Linear MCP connector handles it.
“Clone my repo and run the tests” goes to the host. Dispatch spawns a code task with access to git and the filesystem.
“Read my journal entries and generate insights” depends on where the journal lives. If it’s in an Obsidian vault with an MCP connector, the sandbox handles it fine. Local markdown files without MCP need a code task, or you need to mount the folder into Cowork.
Three types of automations
Once you know which environment a task belongs in, you can schedule it to run without intervention.
MCP-based automations
Scheduled tasks running entirely in the sandbox, using only MCP connectors.
I run a daily activity journal this way. At 2 AM, a scheduled task fetches yesterday’s Linear issues via Linear MCP, reads a pre-staged GitHub activity file from Obsidian MCP, generates a consolidated summary, and writes the journal entry back through Obsidian MCP. No host access involved.
To set one up, describe the task and schedule to Dispatch. On the first run you’ll need to approve each MCP tool when prompted. After that, permissions are cached and it runs autonomously.
The constraint: these tasks can only talk to MCP-connected services. No shell commands, no filesystem access, no CLI tools. If a connector goes down or auth expires, the task fails without warning.
Host-based automations
Shell scripts scheduled through macOS launchd, with full filesystem and CLI access but no MCP.
My GitHub activity dump works this way. At 1:50 AM, ten minutes before the journal task, a launchd job runs a script that uses gh to pull yesterday’s events, writes them to a staging file in the Obsidian vault, and runs git-sync to push it to the remote. The sandbox task at 2 AM then reads that staged file through MCP.
Setting these up means writing a shell script, creating a launchd plist, and loading it with launchctl. Dispatch can do all of this for you by spawning a code task on your Mac.
One thing that will catch you: launchd jobs writing to ~/Documents or other TCC-protected directories need Full Disk Access granted to /bin/bash in System Settings. There’s no popup prompt when it fails. The script just silently errors out in the logs with “Operation not permitted.”
Hybrid automations
The daily journal pipeline I actually run combines both environments. A launchd job at 1:50 AM dumps GitHub data and pushes it. A scheduled Cowork task at 2 AM reads that data through Obsidian MCP, pulls Linear issues through Linear MCP, generates a summary, and writes the journal entry through Obsidian MCP. An hourly git-sync job then pulls the remotely-written entry back to the local vault.
Each piece stays within its environment’s capabilities. The launchd job has gh and the filesystem. The scheduled task has MCP connectors. git-sync bridges the remote vault (where MCP writes) and the local vault (where you read in Obsidian on your phone).
Building it all from your phone
The entire pipeline above was set up through Dispatch on an iPhone:
- Asked Dispatch to create the shell script and launchd job, which spawned a code task on the Mac
- Asked Dispatch to create the scheduled task with the MCP logic
- Triggered both manually once to cache permissions and verify output
- Now monitoring through journal entries that appear in the vault each morning
Getting there involved debugging PATH issues in launchd (it doesn’t source your shell profile), granting Full Disk Access for bash, and dealing with stale staging files when the Mac was asleep at 1:50 AM. Once those were sorted, the pipeline has run nightly without issues.
Permissions and first-run setup
Scheduled tasks prompt for MCP tool approvals on their first run (“Allow this task to use Linear?”). You trigger a manual “Run now” from the Cowork sidebar, approve each tool, and permissions stay cached for all future runs.
Launchd jobs need Full Disk Access configured in System Settings before the first run. Unlike MCP permissions, there’s no interactive prompt. The job just fails silently if it’s missing.
Automation patterns worth trying
A daily standup prep task at 8:30 AM that reads your Linear issues and calendar data via MCP and writes a morning briefing to the vault.
Vault sync as an hourly launchd job running git-sync bidirectionally. Pulls remote changes, commits local edits, pushes. Purely host-based.
A blog publishing pipeline where you write a draft in Obsidian on your phone, tell Dispatch to publish it, and the sandbox reads the draft via Obsidian MCP while a code task converts it to MDX, adds frontmatter, commits to a feature branch, and creates a PR. You get a Vercel preview URL back on your phone.
A weekly review that runs Sunday evening, aggregating journal entries and completed Linear issues into a summary note.
Troubleshooting
Scheduled task ran but produced nothing. Likely missing MCP permissions. Trigger “Run now” from the sidebar and approve the tools. Also check whether the task prompt references Bash commands, which the sandbox can’t execute.
Launchd job fails silently. Check the log path from the plist. Most often it’s Full Disk Access not granted. Second most common: PATH in launchd’s environment is missing /opt/homebrew/bin. Add it to the plist’s EnvironmentVariables.
Code task can’t find a tool. Confirm it’s installed and on your PATH. If you’re trying to use an MCP connector from a code task, that’s the wrong environment. Move that piece to the sandbox.
Dispatch session expired. VNC into your Mac, open Claude Desktop, re-scan the QR from your phone.
Staging file has yesterday’s data. The launchd dump job ran late (Mac was asleep) or not at all. launchd catches up on wake, but the timestamps in the data may be off. Check the log file.
What comes next
The next post walks through building the daily journal pipeline from scratch, including every wrong turn and debugging session that got it to a working state.
This is post 3 in the Remote Agent Engineering series. Previous: The Five Layers of a Remote Mac Agent Setup. Next: Building a Daily Automation Pipeline (coming soon).