Skip to main content
Glama

Chat Unleashed

Chat Unleashed is a local MCP server that lets ChatGPT Chat use a Mac the way a person does: run commands, read and write files, look at the screen, drive apps through their UI, and reach the everyday apps (browser, Calendar, Reminders, Notes, Contacts, Music). It is built for Chat, reaches the Mac through an OpenAI Secure MCP Tunnel, and keeps every machine-specific detail on the machine.

Chat is where most work starts, and on its own it cannot touch the Mac. Work and Codex can act on a machine, and both draw on the shared agentic allowance. This connector closes that gap: the chat you already use can read files, run commands, drive apps and finish the job instead of describing the steps. Chat is not part of that shared allowance today, and its thinking is still unlimited, so the local agent runs on the Chat you already use.

The package is local-agent-mcp. The connector and the chat-unleashed command are the product surface.

Tool activity is recorded as JSON lines in the per-user application-data directory under logs/audit.log.

Quickstart: Clone, Build & Run

Clone the repository and build the server executable right away:

# 1. Clone the repository
git clone https://github.com/PotterSkxawng/chat-unleashed.git
cd chat-unleashed

# 2. Install dependencies and compile
npm install
npm run build

# 3. Link CLI tool to your PATH (or run directly with npm run unleashed)
npm link

# 4. Run setup walkthrough
chat-unleashed setup
# (or: npm run unleashed -- setup)

The build script compiles TypeScript with tsc and bundles the standalone executable into dist/index.js using esbuild.


Related MCP server: chatgpt-mac-bridge

Chat Unleashed CLI

The connector is named Chat Unleashed, with local mac as its alias. The chat-unleashed command is the terminal front end for it. It reads the machine before it claims anything: the launchd jobs, the tunnel profile, the env file, /health, /readyz, and a real MCP handshake.

chat-unleashed            # what is running, and the one next step
chat-unleashed setup      # guided install, skips every step already true
chat-unleashed doctor     # every check, with the fix for each failure
chat-unleashed connect    # prints exact connector settings for ChatGPT
local-mac                 # the same tool under its alias

Command

What it does

chat-unleashed

Reads this Mac, the profile and both services, then names the next step.

chat-unleashed setup

Walks client, tunnel ID, runtime key, profile, server build and services. It creates a tunnel profile pointed at this server, keeps the key by reference, writes only what is missing, then waits for both services. --check prints the plan and writes nothing.

chat-unleashed connect

Prints the ChatGPT steps with your tunnel ID. --open opens the tunnel, key and connector pages.

chat-unleashed doctor

Four groups of checks with the failing detail and the exact fix under each failure. Exits 1 when something is broken, so it works in scripts.

chat-unleashed update

Checks GitHub for updates, pulls new commits, recompiles bundle, redeploys runtime, and restarts services. Use --check to inspect without applying, or --force to force rebuild.

chat-unleashed status

One line, or the full state with --json.

chat-unleashed start / stop / restart

Loads the launchd jobs, starts them, and waits for readiness.

chat-unleashed logs [server|tunnel|launchd]

Follows the matching log.

New machine, from nothing

ChatGPT cannot reach a server on your loopback interface. The supported path is an outbound Secure MCP Tunnel:

ChatGPT -> OpenAI tunnel -> tunnel-client -> http://127.0.0.1:3344/mcp
  1. Create a tunnel at platform.openai.com and copy its ID, which starts with tunnel_.

  2. Create a runtime API key at API keys with Tunnels Read and Use. It is shown once.

  3. Run chat-unleashed setup in a terminal and paste both. The key input is hidden and is never echoed.

  4. Setup writes the tunnel profile for this server, stores the key at mode 0600, installs the launchd jobs, starts the server, waits for the MCP handshake, starts the tunnel, and checks that the tunnel reports ready.

  5. Run chat-unleashed connect --open and fill the connector form with these values:

    • Name: Chat Unleashed

    • Description: Runs commands, reads and writes files, looks at the screen and drives apps on my Mac. Use it when a request needs work done on the local machine instead of only an answer.

    • Connection: Tunnel

    • Tunnel: the ID from step 1

    • Authentication: None. The runtime key stays inside the tunnel on this Mac; ChatGPT never gets a token for the server.

    Save, refresh the tools in the connector details, then start a new chat and pick Chat Unleashed from the plus menu.

  6. Grant macOS permissions when a tool first needs them: Accessibility, Screen Recording, Automation per app, and optionally Full Disk Access.

Chat is the surface this unlocks. Work and Codex act through the shared agentic allowance; Chat is not part of that pool today, so a chat that calls these tools gives you a local agent without spending it.

See docs/CONNECT_CHATGPT.md for the full walkthrough and what to do when a chat session goes stale.

Secrets never print. The runtime key is read from ~/.local/share/local-agent-mcp/.env.tunnel (mode 0600) or from CONTROL_PLANE_API_KEY, and masked input is the only way setup accepts it.

Saved state

Saved settings are kept outside the published package at ~/Library/Application Support/local-agent-mcp/config.json on macOS ($XDG_CONFIG_HOME/local-agent-mcp/config.json elsewhere, or $LOCAL_AGENT_MCP_HOME when set). Tunnel credentials and profiles are stored separately by the tunnel client and must not be committed.

The file is created from defaults on first run, written atomically with mode 0600, and holds the device name, workspace/download/screenshot paths, screenshot size and format, shell timeouts, the Computer Use plugin location and any API keys you add. Change it with the app_config tool, the JSON API below, or any editor: the server re-reads it on every tool call, so edits apply without a restart, and typed values are validated instead of silently accepted. Secrets are masked whenever settings are read.

Local JSON API (for a desktop frontend)

With --http (loopback by default) the server also serves a small JSON API for a settings UI. Loopback callers only.

Route

Purpose

GET /api/health

process id and uptime

GET /api/config

current settings (secrets masked), file path, defaults

PATCH /api/config

merge a partial settings object

PUT /api/config

replace settings, filling gaps with defaults

POST /api/config/reset

restore defaults

GET /api/status

permissions, process identity, config path, version

GET /api/diagnose

the same diagnosis mac_permissions prints

POST /api/permissions/request

ask macOS for grants ({"kinds":[...]})

POST /api/permissions/open

open the matching System Settings pane

Permissions

The server asks macOS for what it needs and names the exact binary that has to be trusted, whatever launched it.

  • Accessibility - read UI trees, click, type, arrange windows

  • Screen Recording - screenshots with real pixels

  • Automation - Apple Events to Calendar, Notes, browsers and the rest (per app, prompted on first use)

  • Full Disk Access - optional, only for protected stores

mac_permissions reports each one; action=request triggers the OS prompts and action=fix opens the right panes. Tools that need a grant say so in their error, with the path to add.

Tools

Tool

What it does

run_command

Full terminal access. Runs any shell command with cwd, timeoutSeconds (default 60, max 600), optional env. Returns stdout, stderr, exit code (capped at 200 KB/stream).

download_file

Fetches an http(s) URL to disk (default ~/Downloads/<filename>), follows redirects, 2 GiB cap, no silent overwrite.

search_files

Finds files by name (substring or * glob) and/or by content, skipping node_modules, .git, caches and ~/Library. 20 s budget, partial results allowed.

read_file

Reads text files with offset/limit; returns images as images so the agent can see them; reports binary files.

list_directory

Lists a directory: type, size, modified time, dirs first.

pages_status

Diagnoses the Pages integration: app path/version, running state, Automation permission, open documents, optional template list.

pages_create

Writes a real .pages document from blocks (title/h1-h3/body/bullet/quote/code/...) with native Pages typography.

pages_read

Returns a document's full text plus a per-paragraph outline of index, point size and font.

pages_inspect

Format report: every paragraph's face/size/colour/bold-italic flags, plus each table's header fill, faces in use and header-marked grid.

pages_styles

list presets/families, get the stored default plus its resolved type scale, set a new default, apply restyle an existing document by inferred role.

pages_replace

Find-and-replace with explicit scope (document / paragraph / table / row / column / cell) and an explicit format mode.

pages_edit

Text and structure: append, prepend, setBody, insertParagraph, replaceParagraph, insertText, deleteText, splitParagraph, mergeParagraphs, duplicateParagraph, moveParagraph, deleteParagraph.

pages_format

Typography and colour: paragraphs (whole paragraphs), characters (a range inside each paragraph), matches (every occurrence of a string).

pages_table

Table values and structure: setCell, setCells, format, insertRows/deleteRows, insertColumns/deleteColumns, merge, unmerge, sort, clear, resize.

pages_find

Locates text across paragraphs and table cells, with a snippet and address for each hit.

pages_stats

Page, section, paragraph, word, character, table and object counts.

pages_insert_image

Places an image file as a real Pages image object (size, position, alt text, opacity).

pages_insert_textbox

Adds a floating text box (position, size, font, colour).

pages_objects

Lists floating objects (images, text boxes, shapes), or deletes one by index.

pages_duplicate

Copies a .pages package on disk - Pages is not involved at all.

pages_verify

Full-document integrity check: counts, typography in use, table structure, and problems (bad sizes, missing fonts, ragged tables, blank-paragraph runs). Built for 20+ page documents.

iwork_apps

Which iWork apps are installed/running, their versions, and every open document.

iwork_document

Overview of any Pages/Numbers/Keynote document: counts of paragraphs, pages, sheets, slides, tables, shapes, images, charts, movies.

iwork_shapes

List, add, move, resize, rotate, recolour or delete shapes in any iWork app.

iwork_export

Export through the owning app's exporter: pdf/txt/rtf/epub/docx, xlsx/csv, pptx/html/movie/images.

iwork_script

Escape hatch: run AppleScript against an iWork app with doc pre-bound. close/quit are refused.

numbers_sheets

List, add, delete, rename or activate sheets in a .numbers document.

numbers_tables

Table structure: add/delete/resize, native add-row-above/below and add-column-before/after, transpose, sort, merge/unmerge, format.

numbers_cells

Read/write cells by A1 reference, including 2D range writes and live = formulas.

keynote_slides

List, add, delete, duplicate and reorder slides; list the document's named layouts.

keynote_content

Set a slide's title/body through its placeholders, or add a text box, image or table.

keynote_notes

Read and write presenter notes.

keynote_show

Start, stop, step through or jump within a slideshow.

mac_apps

List apps with a UI, report the frontmost app, activate/launch/hide. No quit by design.

mac_ui

System Events: list windows, click menu items, send keystrokes, focus an app.

mac_system

Clipboard, notifications, volume/mute, battery, disk, uptime.

mac_finder

Reveal, open, inspect or Trash a path. Trash is the only removal offered.

mac_shortcuts

List and run the user's Shortcuts, optionally with a file input.

pages_export

Exports through Pages' own exporter to PDF, txt, RTF, EPUB or Word.

Pages (native)

The pages_* tools drive the actual Pages app over Apple Events. There is no Word, no docx and no conversion step anywhere in the path - a document created here is a normal .pages file that opens, edits and exports like any other.

Two rules are baked into the implementation:

  • Documents are never closed and Pages is never quit. Anything the tools create is saved and left open for you.

  • No third-party format is used as an interchange layer. Content goes straight into Pages' rich-text model.

Because Pages' scripting dictionary exposes font/size/color but not paragraph styles, format: "styled" maps each block type onto a concrete font + size + color instead of naming Heading 1. Pass format: "plain" (the default when you supply a template) to leave the template's own paragraph styles in charge.

What an edit preserves

Every editing op falls into one of three buckets, and knowing which is which is the difference between a clean edit and a mangled document.

Never rewrites anything it was not pointed at. append, prepend, insertParagraph, deleteParagraph, duplicateParagraph, moveParagraph and pages_table's structural ops work on real paragraph and cell objects. Every other paragraph keeps its runs, including bold/italic inside the text.

Rewrites one paragraph, keeps its look. replaceParagraph, insertText, deleteText, splitParagraph and mergeParagraphs set the paragraph's contents and leave its font, size and colour alone. The paragraph looks the same afterwards - but any character-level formatting inside that one paragraph (a bold phrase mid-sentence) is flattened, because the text is being replaced wholesale. Everything outside the paragraph is untouched.

Formats without touching text. pages_format and pages_replace never change what a paragraph looks like except in the way you asked for. pages_format can address a character range (op: "characters") or every occurrence of a string (op: "matches"), which is how you bold a term everywhere without disturbing the rest of the paragraph.

setBody is the one op that rebuilds the whole body from plain text. Treat it as the "start over" escape hatch, not the default.

Editing without wrecking formatting

pages_replace is deliberately narrow: it only rewrites the units you point it at, and never rebuilds the body.

  • scope - document (all paragraphs + all cells), paragraph (one index), table, row, column, cell. Table scopes take table, row, col.

  • keepFormat - true (default) re-applies the paragraph's own face, size and colour after the rewrite; false lets the paragraph's style default take over. Table cells keep their formatting either way: only the cell value is written, so fill, borders and alignment survive.

Inserting text without disturbing the neighbours

Pages has no "insert paragraph at index N" command. The workaround is to splice into the anchor paragraph's own contents (anchorText & linefeed & newText), which creates the new paragraphs in place and lets the anchor keep its formatting. insertParagraph and prepend both work this way, so an insert in the middle of a styled document does not reset the paragraphs around it.

One consequence: the anchor paragraph is rewritten, so inline runs inside that single paragraph are flattened. If that matters, anchor to an empty paragraph or use append.

Default styles

pages_styles owns a persisted default at ~/.config/local-agent-mcp/pages-style.json. set writes it; pages_create picks it up whenever no style is passed, so "the look of new documents" is a machine setting rather than a per-call argument. get prints the resolved type scale so you can see exactly what each role will become.

apply restyles an existing document: it infers each paragraph's role from its size relative to the document's dominant body size (and from italic/mono faces), then rewrites font, size and colour per role, leaving every character of text alone. The heuristic is size-based, so a document with unusual proportions can be misread - pages_inspect first, then pages_format for surgical fixes.

Styles

style takes a preset name - classic, serif, modern, compact - or an object of overrides (family, baseSize, accent, muted, headerFill, headerText, zebra). Families: helvetica-neue, georgia, avenir-next, palatino, times. Every size is derived from baseSize through a fixed type scale, so one number re-proportions the whole document.

Tables

A table block builds a real Pages table: header (creates a true header row), rows, headerColumn, per-column align, columnWidths, and zebra. Tables must be the last blocks, because Pages cannot insert an inline table mid-flow and reordering content silently would be worse than refusing.

Existing tables are edited in place with pages_table:

  • values - setCell (one cell, plus font, size, color, bold, italic, fill, align), setCells (a rectangular grid at once). Writing a value resets the run to the table's body style, so the cell's own face, size and colour are captured first and written back afterwards - a value edit does not quietly restyle the cell.

  • structure - insertRows/deleteRows, insertColumns/deleteColumns, merge, unmerge, resize, clear, sort (by column, ascending or descending).

  • formatting - format with font, size, color, fill, align, verticalAlign, wrap, headerRows, headerCols, footerRows, columnWidths, rowHeights. Omit rows/cols to format the whole table.

bold/italic work by swapping to the matching face of the cell's current family, so a Georgia cell stays Georgia when you bold it. Row and column inserts shift values, not cell formatting: the cell keeps its own styling and the data moves into it.

Set a cell value beginning with = to write a formula (=SUM(B2:B5)); Pages evaluates it, and pages_inspect will report the computed value afterwards.

What Pages does not expose

Worth knowing before you promise something the dictionary cannot do. Pages' scripting interface covers font, size, colour, cell fill, cell alignment, table geometry and object placement. It does not expose, for body text:

  • paragraph alignment (left/centre/right/justify)

  • line spacing, paragraph spacing, tabs, indents

  • text highlight / background colour behind a run

  • paragraph styles by name (which is why format: "styled" maps block types onto concrete font + size + colour instead of naming "Heading 1")

Table cells do have alignment and vertical alignment, so use a table when you need centred or right-aligned content.

Large documents

Reading used to cost four Apple Events per paragraph - a 20-page document meant thousands of round trips, and the output string was built with quadratic concatenation. Both are gone:

  • Bulk property reads. font of every paragraph, size of every paragraph, color of every paragraph and contents of every paragraph each come back in a single event, so a long document is read in a handful of round trips. If any coercion fails the code falls back to the per-paragraph loop automatically.

  • One join at the end. Records are accumulated in a list and joined once with text item delimiters, instead of out & ... inside the loop.

  • Argument-size safety. osascript -e is bounded by the kernel's argument limit, so any script over ~60 KB is written to a temp file and run from there. Large setBody/append payloads no longer fail with "argument list too long".

  • Timeouts scale with the document rather than cutting a long read off.

pages_verify is the companion tool: point it at a 20-page document and it reports counts, the typography actually in use, table structure, and any structural problems - implausible point sizes, missing fonts, empty or ragged tables, runs of blank paragraphs, merged cells that cannot be addressed. Pass expectParagraphs / expectTables to assert an exact structure.

Running in the background

The Pages tools never bring Pages to the front. Nothing in the generated AppleScript calls activate; every edit is sent as an Apple Event to the running app, so the document is modified while you keep working in whatever window you are actually using.

The single exception is opening a document that is not already open - macOS raises the app for that. When it happens the script remembers which app had focus and hands it straight back, so even that case does not leave you looking at Pages.

Verified mechanically: of the 34 scripts the Pages suite generates, zero contain activate. The only scripts in the whole server that do are the four Keynote slideshow commands and Finder's reveal/open, where taking the screen is the point.

Numbers

Numbers is not Pages with a grid. Three differences matter, and the tools are built around them:

  • Content lives on sheets. numbers_sheets addresses them; every table and cell operation takes sheet (default 1).

  • Cells have A1 names. numbers_cells speaks B4 and A1:C10 rather than row/column indices, and get renders the result as a grid.

  • Structure is native. Numbers exposes add row above, add row below, add column before, add column after, transpose, sort, merge and unmerge as real commands. Pages tables needed value-shifting workarounds; Numbers does not, so rows and formulas move correctly.

Values are typed by shape: 42 is stored as a number, true as a boolean, =SUM(B2:B5) as a live formula, anything else as text.

Keynote

A slide's text lives in two placeholder shapes - default title item and default body item. keynote_content writes through those, which keeps the theme's typography; dropping a text box on top would look wrong on every theme. If a layout has no title placeholder the tool says so instead of silently creating an orphan text box.

Reordering is copy-then-delete, because Keynote exposes duplicate ... to <location> but no plain move. Both halves run in one script so the document never sits in a half-moved state.

keynote_show drives a real presentation - start, goto, next, previous, stop. It activates Keynote, because that is what presenting means.

Cross-app iWork

Pages, Numbers and Keynote share one object model: documents hold shapes, images, text items, tables, charts, movies and lines, and each is an iWork item with position, width, height, rotation and opacity. The only real difference is where content lives:

App

Content container

Pages

the document (body text, sections, pages)

Numbers

sheets, each holding tables

Keynote

slides

iwork_shapes, iwork_export and iwork_document are written once against that shared model. Pass path and the app is inferred from the extension (.pages, .numbers, .key); pass app to use the frontmost document.

iwork_script is the escape hatch for anything the structured tools do not cover: your AppleScript runs inside the target app with doc already bound. Scripts containing close or quit are refused - the never-close rule is enforced, not just documented.

Computer use

Seeing and driving the machine itself. These are the tools to reach for when an app has no scripting interface, or when the point is what is on screen.

Tool

What it does

screen_capture

Screenshot of a display, window or region, returned as an image the model can see (and saved to disk). Optional downscaling keeps tokens sane. Needs Screen Recording.

window_list

Every on-screen window with id, app, title, bounds and layer. No permission needed.

ui_tree

An app's accessibility tree as text: roles, titles, values, positions, actions, and a path per node.

ui_find

Search that tree by label text or role and report where each match is.

ui_act

Press/confirm/open-menu/focus/raise an element, or set its value - by label, at any depth. More reliable than clicking coordinates.

mouse

Move, click, double/right/middle click, drag, scroll the real pointer.

keyboard

Type text, press named keys, or send shortcuts like command+shift+t, optionally activating the app first.

window_control

Focus, move, resize, minimise, restore, zoom, fullscreen, tile left/right.

app_control

List apps, read the frontmost one, activate, launch, hide, show, quit gracefully, relaunch, force quit.

System, workflow and everyday apps

Tool

What it does

system_info

OS, chip, memory pressure, swap, load, uptime, disk, power, battery, thermal, top processes.

process_list / process_control

Top processes by cpu/memory; signal, stop, continue or renice one (never this server or its parents).

network_info

Interfaces and addresses, gateway, Wi-Fi, DNS, listening ports.

power

Lock, display sleep, sleep, keep awake with caffeinate for N minutes, allow sleep again.

audio / speak / media

Volume, mute, speech, and Music/Spotify transport + now playing.

shell_session

A persistent shell: start, run and read across calls, write to a running program, Ctrl-C, stop. Keeps dev servers and REPLs alive between turns.

wait_for

Poll until a file appears/changes, a port opens, a URL answers, a process/app runs, or a window title matches.

file_ops

Move, copy, mkdir, symlink, du, zip/tar create and extract, touch, open with a specific app. Refuses to overwrite unless asked; never deletes.

spotlight / recent_files

mdfind by name, content, kind or recency; what changed under a folder in the last N hours.

browser

Tabs with titles and URLs, open a URL, read the active page's text, front tab, close a tab (Safari/Chrome/Edge/Brave/Arc).

calendar / reminders / notes / contacts

Read and add events, reminders and notes; look people up. Nothing is ever deleted.

app_config

Read and edit this machine's saved settings.

mac_permissions

Report, request and open the panes for macOS permissions.

Durable tasks

A task is a plan on disk, not a plan in a conversation. task_plan writes it, task_run executes the steps that can run now, and every step has to be proven by a check before it counts as done.

Tool

What it does

task_plan

Records a goal, success criteria (each with the check that proves it) and ordered steps that name the tool they run.

task_run

Runs the runnable steps, verifies each one, and writes progress after every step.

task_next

The next step, or the approval that is holding the task up.

task_status

Steps, evidence, criteria results and the recent log. Without an id it lists every open task.

task_resume

Reads the task back from disk, re-checks the criteria, and reports where the work stopped.

task_steer

Note, insert, skip or re-queue a step mid-run.

task_approve

Clears the gate on a step that changes something outside the project.

task_close

Re-runs the criteria and refuses to close while anything lacks evidence.

Three rules make the stored state trustworthy:

  • Executed is not verified. A step with no verify check ends unverified. A task with an unmet criterion cannot close as done unless task_close force writes the gap into the reason.

  • Evidence belongs to a definition. Editing a verified step clears its proof and puts it back in the queue, because the old check proved the old command.

  • Approval is a stored decision, not a conversation. Steps that run rm, sudo, git push, force flags or shutdown wait until task_approve records a timestamp for that exact step.

Checks cover a shell command and its output, a file that exists or does not, a window title, interface text, another tool call, and polling with wait. The same vocabulary verifies a step and tests a success criterion.

Everything lives in <appHome>/tasks/<id>.json with an append-only <id>.jsonl action log, so a run survives a dropped conversation, a new client, or a restart of this server. Long work can run with no client attached:

node dist/index.js --run-task task_ab12cd34 --max-steps 10

That entry point loads the same plan, runs what can run, prints the report, and exits non-zero when a step failed or another process holds the lock.

Screen control

Two tools replace guessing at coordinates.

Tool

What it does

screen_observe

One call that answers what is on screen: front app, window title and bounds, every display with its Retina scale, the accessibility tree, and the states that change what to do next (dialog, permission prompt, loading, open menu), plus an optional screenshot and a hash of it. context: true adds the trail of apps and windows this session already observed.

ui_do

Resolves an element by name, role or identifier, picks the most reliable method the app supports, performs it, then looks again and reports what changed. Ambiguous matches are reported with their scores so the caller can pass an index or a role. resume: true continues with the app last observed instead of the frontmost one.

A window that exists but is not drawn is reported as such, which is how a minimized window or one on another Space shows up instead of as an element that cannot be found. The context trail is kept on disk across calls, so a workflow that moves between Pages, Numbers and Finder does not have to re-establish where it was.

The method ladder is the point. In order:

  1. Accessibility action when the element exposes a press, because it survives a window moving, a display changing scale or a Space switch.

  2. Menu shortcut when the app binds the intent (save, find, print) to keys, which needs no target at all.

  3. Scripting command when the app is scriptable and answers the intent directly, such as make new document in Pages.

  4. Pointer click on the element's current center, only when nothing above is available, and always verified afterwards.

A click that changes nothing is reported as verified: no rather than as success. Pixel-only change is labelled as weak evidence. Focus drift and a window moving under a click get one automatic retry at the new bounds, and no more than one.

Documents

Tool

What it does

doc

qa reads the exported PDF page by page and reports blank pages, text outside the page box, text pushed to the bottom edge and text boxes stacked on top of each other. Overlap detection groups characters into lines by baseline and only calls a real overlap when two boxes intersect a couple of points deep in both directions, so an italic overhang or a superscript is not reported. compare reports per page whether the text changed and whether the rendering changed. backup, backups and restore give an edit run a way back.

The document model says a paragraph exists; the page says whether it fits. Tasks can require a clean render with a pdf check, which is how a compile and verify loop refuses to call a document finished while a page is clipped.

Coding

Tool

What it does

code_index

build indexes every function, type, class, protocol and Metal kernel with its file and line; find searches those declarations; summary reads manifests, test layout, git state and TODO count.

code_loop

run executes acceptance commands and reports exactly which ones failed, keeping a history per goal; resources reports load, free memory and swap; profile samples a running process with the system sampler and returns the frames carrying the most samples; crashes reads recent crash reports with the top frames of the failing thread.

bench

Records samples by channel (kernel, model, end_to_end, memory) and only compares like with like, so a kernel microbenchmark is never averaged with a full model run.

git_work

status, diff, stage, unstage, commit, isolated worktrees on their own branch, and checkpoints. checkpoint snapshots the whole working tree, untracked files included, through a throwaway index, so the real index and the files on disk are untouched and nothing has to be stashed. restore_checkpoint first checkpoints whatever is on disk, then makes the tree match the checkpoint exactly, so a restore cannot be the step that loses work.

code_loop is the goal engine: it runs the acceptance commands, prints the failing output and records the iteration. It records the machine's resource state with every run, because a benchmark taken at load 200 on eight cores measures the thermal state, not the code.

Browser

Tool

What it does

web

Structured control of a Chromium browser through the DevTools protocol: tabs, navigation, DOM outline with stable element ids, locators (css=, text=, role=, label=, id=, xpath=), click, type, text, screenshots, console and network capture, waiting, evaluation, downloads, and page snapshots for change detection. changes diffs the current text against the stored snapshot line by line and prints what appeared and disappeared. download sets the download folder, clicks the link that offers the file and waits for the partial file to finish; downloads lists what arrived.

Clicks and typing are verified against the url and the page text. The browser is started with its own profile, separate from the one the person is signed into, so a page that needs their session has to be signed into once in that profile. Without a Chromium, engine=safari gives tab listing, navigation and page text, and says what else is unavailable instead of pretending.

Skills

Tool

What it does

skill

save a workflow with trigger phrases, steps and acceptance criteria; list and show; match routes a request to the skills whose phrases appear, ranked by measured success rate; compile turns a skill into a durable task; report records a finished task's verdict against it; seed writes the builtin skills for Swift packages, Metal kernels, MLX models and Apple silicon measurement.

Success rates are Laplace smoothed, so a new skill is not punished for having no history and one lucky run does not read as certainty. A compiled skill is a task, which means it inherits retries, checkpoints, approval gates and evidence.

System automation

mac_* tools cover the parts of a Mac that are not documents:

  • mac_apps - what is running, what is in front, activate/launch/hide.

  • mac_ui - System Events: windows, menu clicks, keystrokes. This is the long tail for apps with no scripting interface, and it needs Accessibility permission in addition to Automation. The error message tells you where.

  • mac_system - clipboard text, notifications, volume, battery, disk, uptime.

  • mac_finder - reveal, open, inspect, Trash.

  • mac_shortcuts - list and run Shortcuts, optionally with a file input. Prefer this over UI scripting when a shortcut exists.

The mac_apps and mac_finder convenience tools favor explicit app activation and recoverable Trash operations. Other tools have broader authority: app_control can quit or force-quit applications, and shell access can modify or delete files. Restrict which agents can connect and review the per-user audit log. iwork_apps only queries already-running apps and does not launch them.

One-time permission

macOS blocks Apple Events until the host app is granted Automation access. The first call raises a prompt; if it was declined, every call returns -1743 / -10004 and pages_status prints the exact System Settings path plus the process chain, so you know which app to authorise:

System Settings → Privacy & Security → Automation → [host app] → Pages → on

Pages is addressed by bundle id (com.apple.Pages) rather than by name, so a renamed copy of the app still works.

Install and validate

Requires an Apple Silicon Mac with Node.js 20.11.1 or newer. Run from a checkout or unpacked package:

npm install
npm run build
npm run smoke:portability
npm run validate:release

npm run build typechecks and bundles the current source in dist/; it does not deploy or restart the running service. npm run deploy:local installs the bundle atomically into ~/.local/share/local-agent-mcp/dist/, retaining the previous installed file as index.js.previous when one exists. Deploying the file does not replace the code already loaded by a running Node process. Do not restart a tunnel carrying an active chat merely to update the bundle.

A checkout with Git metadata enables Git-aware project_context and project_review features. A source copy without .git can still build and run, but those review features cannot inspect history or diffs.

Connect ChatGPT through the Secure MCP Tunnel

Normal ChatGPT sessions do not connect directly to localhost on your Mac. The remote architecture is:

ChatGPT -> OpenAI Secure MCP Tunnel -> tunnel-client -> local-agent-mcp

The recommended private local target is the Streamable HTTP endpoint on 127.0.0.1:

npm run http
curl --fail http://127.0.0.1:3344/health

Use tunnel-client init --help to generate a profile for your own tunnel ID and endpoint, selecting --mcp-server-url http://127.0.0.1:3344/ for an HTTP-backed tunnel. The profile belongs in the tunnel client's user configuration directory. Refer to your tunnel provider's connection flow for the control-plane key and ChatGPT plugin binding. Never commit a real tunnel ID, credential, private profile or LaunchAgent environment file to the package.

A correctly supervised tunnel can run with a user-specific LaunchAgent. Provide credentials through a protected environment or secret file (mode 0600) and keep application-data directories restricted to the current user. The installer does not currently create the tunnel or register a LaunchAgent automatically; those are separate setup steps. The default HTTP endpoint is loopback only and must not be exposed directly to a network or public reverse proxy.

Stdio MCP remains supported for compatible clients, including local CLIs. If a stdio tunnel reports MCP server is not initialized, consult the initialization-specific troubleshooting guidance before changing transports. The MCP_STDIO_SEND_INITIALIZED_NOTIFICATION option is specific to tunnel-client; it is not required for HTTP-backed targets.

See Connection and runtime troubleshooting for diagnosing Session terminated, missing tunnel credentials, server exits, 503/413 responses, permissions, app discovery and browser CDP issues.

Deferred updates without cutting off ChatGPT

After validating a candidate, npm run update:install registers an independent per-user launchd watcher and npm run update:stage stages the bundle by hash. The watcher only activates it after at least 20 minutes without observed local/remote tool traffic. Only the separately supervised HTTP MCP server is restarted; the Secure MCP Tunnel stays alive. A watchdog cannot prove that an idle ChatGPT session has disconnected, so a request arriving during the small restart window may still need retrying. Failed health checks trigger a rollback.

See Deferred updates for the idle criteria, staging, rollback, status and cancellation commands.

Optional dependencies

Pages/Numbers/Keynote are needed for their native document tools. A supported Chromium-family browser and debugging target are needed for CDP automation. Python, MLX, Metal development tools, and Homebrew are optional and discovered at runtime when present. Features that rely on them should report a missing dependency instead of assuming an exact installation path or chip generation.

Chat acts during the turn: it can call tools, read the results and keep going until the answer is done, but it does not keep working after the turn ends. Work and Codex provide that background loop. This connector is local reach for Chat, not a second scheduler.

Audit log

The audit history is stored under the per-user application-data root. With the default macOS configuration:

tail -f "$HOME/Library/Application Support/local-agent-mcp/logs/audit.log"

Setting LOCAL_AGENT_MCP_HOME changes the application-data root. The audit log may contain file paths, command arguments and tool summaries: treat it as sensitive local history and exclude it from release archives.

Security notes

  • No built-in HTTP authentication - the endpoint is loopback-only and rejects foreign Host/Origin headers. Do not pass --host 0.0.0.0 and do not port-forward it.

  • The tools have full user-level permissions: anything your account can do, the connected agent can do. The audit log is your accountability trail, not a guardrail.

  • ChatGPT's replies can be manipulated by content it reads on this machine (a webpage or file telling it to run a command). Treat the audit log as must-review after agent sessions, and run it as a non-admin user if you want a harder ceiling.

  • Disconnect or disable the ChatGPT connector to revoke ChatGPT access. Stop the tunnel for an immediate remote cutoff; stopping only the local HTTP process does not revoke tunnel credentials. Keep audit history private.

Contributing and project policies

Chat Unleashed is licensed under the MIT License. The repository has a contribution guide, code of conduct, security reporting policy, support guidance and governance notes. Please use the GitHub issue and pull request templates when proposing changes, and avoid including secrets, personal data or unredacted audit logs in reports. CI checks the build, logic tests and release hygiene on Apple Silicon macOS; passing CI is not a replacement for real macOS permission and UI testing.

Development

npm run stdio   # stdio transport
npm run http    # Streamable HTTP on 127.0.0.1:3344
npx tsc -p tsconfig.json --noEmit   # typecheck

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers