# com.local-mcp/local-mcp (remote · local-mcp.com)

Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.

- Trust score: 64/100 (medium)
- Change this week: +1
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-03

## Components

- remote · `local-mcp.com`: 64/100 (this document), [markdown](https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp.md), [page](https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp)
- npm · `local-mcp`: 35/100, [markdown](https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp-2.md), [page](https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp-2)

## Channel facts

- Endpoint: `https://local-mcp.com/mcp`
- Transports: `streamable-http`
- Auth: `none`
- Version: `3.0.360`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-03.

- **Endpoint Security**: 66/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (create_omnifocus_task).
  - HTTPS is enforced; there's no plaintext access path.
  - The HSTS (Strict-Transport-Security) header is present.
  - DNSSEC is configured correctly; the domain's records validate against the full chain to the root.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 69/100
  - 75% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 23427 tokens (~103/item across 227 items; 226 tools + 1 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 11/100
  - Stability check failed: schema churn in the 8 days we've observed: 38 tool removals, 0 breaking changes, 0 auth/transport breaks, 26 additions.
- **Tool Coverage**: 95/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 81% of tool parameters carry a description.
  - Structured output schemas are declared (84% of tools); any adoption earns full credit.
- **Capabilities**: 47/100
  - Spec-recency check failed: implements MCP spec 2024-11-05; the latest is 2026-07-28.
  - Supports UI / widget rendering.

## Install

### Claude

```bash
claude mcp add --transport http com-local-mcp-local-mcp https://local-mcp.com/mcp
```

### Codex

```toml
[mcp_servers.com-local-mcp-local-mcp]
url = "https://local-mcp.com/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-local-mcp-local-mcp": {
      "type": "remote",
      "url": "https://local-mcp.com/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add com-local-mcp-local-mcp --url https://local-mcp.com/mcp --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  com-local-mcp-local-mcp:
    url: "https://local-mcp.com/mcp"
```

### Other

```json
{
  "mcpServers": {
    "com-local-mcp-local-mcp": {
      "type": "http",
      "url": "https://local-mcp.com/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-03 (score 64, +1)

No change was recorded against any check on this day. Stability & Change Management went from 7 to 11.

### 2026-08-02 (score 63, 0)

- [security regression] Tool “update_self_diagnosis” was removed
- [security regression] Tool “update_local_mcp” was removed
- [security regression] Tool “todo_list_lists” was removed
- [security regression] Tool “delete_reminder_list” was removed
- [security regression] Tool “signal_friction” was removed
- [security regression] Tool “rename_reminder_list” was removed
- [security regression] Tool “list_reminder_lists” was removed
- [security] New tool “delete_reminder_folder”, which the server declares destructive
- [security] Tool “word_append” rewrote its description, which is the text the model reads
- [security] Tool “word_create” rewrote its description, which is the text the model reads
- [security] Tool “file_read” rewrote its description, which is the text the model reads
- [security] Tool “ppt_create” rewrote its description, which is the text the model reads
- [security] Tool “safari_wait_for” rewrote its description, which is the text the model reads
- [security] Tool “chrome_wait_for” rewrote its description, which is the text the model reads
- [functional] Schema quality: good → fair
- [functional] Server version: ff5a8769-3ea → 592a6ae2-fec
- [functional] Server version: 08fa612d-2a3 → ff5a8769-3ea
- [functional] New tool “todo_get_folders”
- [functional] New tool “report_friction”
- [functional] New tool “rename_reminder_folder”
- [functional] New tool “lmcp_upgrade_diagnostics”
- [functional] New tool “lmcp_install_upgrade”
- [functional] New tool “get_reminder_folders”
- [functional] New tool “todoist_create_task”
- [functional] New tool “connect_todoist”
- [functional] New tool “todoist_list_tasks”
- [functional] New tool “todoist_list_projects”
- [functional] New tool “survey_respond”
- [functional] New tool “disconnect_todoist”
- [functional] New tool “survey_skip”
- [functional] New tool “todoist_complete_task”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “include_completed”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “inbox”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “flagged”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “due_today”
- [cosmetic] “list_omnifocus_projects” reworded the description of “limit”
- [cosmetic] “list_omnifocus_projects” reworded the description of “include_completed”
- [cosmetic] “list_omnifocus_folders” reworded the description of “limit”
- [cosmetic] “create_omnifocus_task” reworded the description of “name”
- [cosmetic] “create_omnifocus_task” reworded the description of “due_date”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “limit”
- [cosmetic] “create_omnifocus_task” reworded the description of “flagged”
- [cosmetic] “complete_omnifocus_task” reworded the description of “confirm”
- [cosmetic] “complete_omnifocus_task” reworded the description of “task_id”
- [cosmetic] “complete_omnifocus_task” reworded the description of “task_name”
- [cosmetic] “create_omnifocus_task” reworded the description of “confirm”
- [cosmetic] “create_omnifocus_task” reworded the description of “defer_date”
- [cosmetic] “update_reminder” reworded the description of “list_name”
- [cosmetic] “todo_list_tasks” reworded the description of “list”
- [cosmetic] “todo_create_task” reworded the description of “list”
- [cosmetic] “create_omnifocus_task” reworded the description of “project”
- [cosmetic] “create_omnifocus_task” reworded the description of “note”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “project”
- [cosmetic] “search_omnifocus_tasks” reworded the description of “query”
- [cosmetic] “search_omnifocus_tasks” reworded the description of “limit”
- [cosmetic] “list_omnifocus_tasks” reworded the description of “tag”
- [cosmetic] “safari_wait_for” reworded the description of “selector”
- [cosmetic] “safari_wait_for” reworded the description of “text_match”
- [cosmetic] “chrome_wait_for” reworded the description of “selector”
- [cosmetic] “chrome_wait_for” reworded the description of “text_match”
- [cosmetic] “safari_wait_for” made “selector” optional
- [cosmetic] “chrome_wait_for” made “selector” optional

### 2026-08-01 (score 63, +1)

- [functional] Server version: b4f82cea-409 → 08fa612d-2a3

### 2026-07-31 (score 62, −4)

- [security regression] Tool “servicenow_get_incident” was removed
- [security regression] Tool “connect_servicenow” was removed
- [security regression] Tool “disconnect_servicenow” was removed
- [security regression] Tool “servicenow_add_comment” was removed
- [security regression] Tool “servicenow_create_incident” was removed
- [security regression] Tool “servicenow_list_my_incidents” was removed
- [security regression] Tool “servicenow_search_incidents” was removed
- [security regression] Tool “servicenow_search_kb” was removed
- [security regression] Tool “servicenow_update_incident” was removed
- [security] The server rewrote its instructions, which are the text every model session reads
- [security] Tool “create_calendar_event” rewrote its description, which is the text the model reads
- [security] Tool “create_referral_invites” rewrote its description, which is the text the model reads
- [security] Tool “delete_calendar_event” rewrote its description, which is the text the model reads
- [security] Tool “file_read” rewrote its description, which is the text the model reads
- [security] Tool “finder_list” rewrote its description, which is the text the model reads
- [security] Tool “finder_search” rewrote its description, which is the text the model reads
- [security] Tool “report_problem” rewrote its description, which is the text the model reads
- [functional regression] Schema quality: pass → fail
- [functional regression] MCP protocol: pass → fail
- [functional] The server now declares the “prompts” capability
- [functional] The server now declares the “resources” capability
- [functional] First check of Schema quality: 75
- [functional] New prompt “draft_email”
- [functional] New prompt “daily_briefing”
- [functional] New prompt “summarize_inbox”
- [functional] MCP protocol version: 2025-11-25 → 2024-11-05
- [functional] New resource “welcome.html”
- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server
- [functional] Server version: 1.0 → b4f82cea-409
- [cosmetic] “create_calendar_event” added an optional parameter “recurrence”
- [cosmetic] “create_calendar_event” added an optional parameter “recurrence_count”
- [cosmetic] “create_calendar_event” added an optional parameter “recurrence_interval”
- [cosmetic] “create_calendar_event” added an optional parameter “recurrence_until”
- [cosmetic] “delete_calendar_event” added an optional parameter “span”

### 2026-07-30 (score 66, +6)

- [security regression] Stability: 0.10 → fail
- [functional] Schema quality: fair → good
- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

### 2026-07-29 (score 60, +1)

No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-07-28 (score 59, −4)

- [security] Tool “lmcp_state” rewrote its description, which is the text the model reads
- [functional] Schema quality: good → fair
- [functional] New tool “list_missing_permissions”
- [functional] New tool “configure_clients”

### 2026-07-27 (score 63, +1)

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

## MCP tools (226)

### `setup_install` (~207 tokens)

Install LMCP

Returns a personalized LMCP install link and setup steps (~30 sec to install). LMCP is a free Mac app that gives access to Mail, Calendar, Contacts, Teams, OneDrive, Notes, Reminders, and 100+ tools on the user's Mac (data stays local). A user would invoke this to install LMCP or reconnect it. Pass os ("macos", or windows/linux/ios/android). Optional: email, step, issue.

Input parameters:

- `email` (string): Optional. Helps Cloud Relay auto-connect after install.
- `issue` (string): Optional tag: gatekeeper_error, dot_not_green, install_failed, etc.
- `os` (string, required): macos | windows | linux | ios | android. Cloud connectors must pass os (or server asks). Desktop terminal clients may omit → macOS. Windows/Linux/mobile → waitlist (macOS-only today).
- `step` (string): If stuck: connector | install | email | connecting_stuck | server_down.

Output parameters:

- `install_command` (string): One-line terminal command to install LMCP, when applicable to this OS/step.
- `instructions` (string): Full human-readable, step-by-step install/setup text.
- `os` (string): Target operating system the instructions are for, when known.

### `chrome_click` (~139 tokens)

Chrome Click

Clicks the first element matching a CSS selector in the current Google Chrome tab. Returns the tag name and visible text of the clicked element so you can confirm the right thing was hit. Pass `wait_for_navigation: true` to wait up to 3 seconds for the page to load after the click.

Input parameters:

- `nth` (integer): Which match to click if there are several (0-based, default 0)
- `selector` (string, required): CSS selector (e.g. 'button.primary', '#save', '[data-testid=login]')
- `wait_for_navigation` (boolean): Wait up to 3s for page load after click (default false)

### `chrome_fill_form` (~43 tokens)

Chrome Fill Form

Fills multiple form fields in one shot in the current Google Chrome tab. Pass fields as a JSON object mapping CSS selector to value.

Input parameters:

- `fields` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `chrome_go_back` (~30 tokens)

Chrome Go Back

Navigates the current Google Chrome tab back to the previous page.

Input parameters:

- `window_index` (integer)

Output parameters:

- `from` (string)
- `to` (string)
- `went_back` (boolean)

### `chrome_history` (~117 tokens)

Chrome History

Lists or searches the user's Google Chrome browsing history (local SQLite, read-only — no page is opened). Optional `query` matches the URL or page title (case-insensitive substring). Returns url, title, visit_count and last_visit (ISO), newest first. Requires Full Disk Access. For Safari use safari_history.

Input parameters:

- `limit` (integer): Max entries to return (default 50, max 200).
- `query` (string): Substring to match against the URL or page title. Omit to list the most recent history.

### `chrome_list_tabs` (~29 tokens)

Chrome List Tabs

Lists every open tab across all Google Chrome windows with title, URL, and whether it is active.

Output parameters:

- `count` (integer)
- `tabs` (array)

### `chrome_navigate` (~51 tokens)

Chrome Navigate

Navigates Google Chrome to a URL. Pass new_tab=true to open in a new tab.

Input parameters:

- `new_tab` (boolean)
- `url` (string, required)
- `window_index` (integer)

Output parameters:

- `navigated` (boolean)
- `new_tab` (boolean)
- `url` (string)

### `chrome_query_selector_all` (~44 tokens)

Chrome Query Selector All

Runs document.querySelectorAll in the current Google Chrome tab and returns a compact summary of each match.

Input parameters:

- `limit` (integer)
- `selector` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `chrome_read_tab` (~252 tokens)

Chrome Read Tab

Reads the rendered text content of a Google Chrome tab. Identify the tab either by `url_match` (substring match against URL; first hit wins) or by `window_index` + `tab_index` (from chrome_list_tabs). Text is capped at `max_bytes` (default 100 KB). Pass `include_html: true` to also get the raw HTML source. Pass `include_links: true` to extract all links with their href and text. Requires 'Allow JavaScript from Apple Events' (Chrome → View → Developer); run chrome_setup_check if reads come back empty.

Input parameters:

- `include_html` (boolean): Also return the HTML source (default false)
- `include_links` (boolean): Extract all links with href + visible text (default false). Great for navigating SPAs.
- `max_bytes` (integer): Max bytes of text (and html) to return (default 102400)
- `tab_index` (integer): Tab index from chrome_list_tabs (default active tab of that window)
- `url_match` (string): Substring to match against the tab URL. Takes precedence over indices.
- `window_index` (integer): Window index from chrome_list_tabs (default 0)

Output parameters:

- `html` (string)
- `html_bytes` (integer)
- `html_truncated` (boolean)
- `link_count` (integer)
- `links` (array)
- `links_error` (string)
- `text` (string)
- `text_bytes` (integer)
- `title` (string)
- `truncated` (boolean)
- `url` (string)

### `chrome_search_tabs` (~117 tokens)

Chrome Search Tabs

Searches the rendered text of every open Google Chrome tab for a substring. Returns each matching tab with the surrounding snippet. Useful for 'do I have a tab open with X?' across many tabs. Requires 'Allow JavaScript from Apple Events' (Chrome → View → Developer).

Input parameters:

- `context` (integer): Characters of context around each match (default 120)
- `max_tabs` (integer): Max tabs to scan (default 30). Higher = slower.
- `query` (string, required): Substring to search for (case-insensitive)

Output parameters:

- `hits` (array)
- `query` (string)
- `scanned` (integer)

### `chrome_setup_check` (~48 tokens)

Chrome Setup Check

Reports whether Google Chrome is ready for interactive tools (chrome_click, chrome_type, chrome_evaluate_js, chrome_read_tab text). Returns setup instructions if JavaScript from Apple Events is not enabled.

Output parameters:

- `instructions` (string)
- `ready_for_js_tools` (boolean)
- `tabs_open` (integer)

### `chrome_type` (~53 tokens)

Chrome Type

Sets the value of an input/textarea matching a CSS selector in the current Google Chrome tab and fires input/change events.

Input parameters:

- `clear` (boolean)
- `selector` (string, required)
- `value` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `chrome_wait_for` (~153 tokens)

Chrome Wait For

Polls the current Google Chrome tab until a CSS `selector` appears, or until `text_match` appears (anywhere on the page, or inside `selector` if you also pass one). Pass at least one of `selector`/`text_match`. Useful after chrome_click to wait for the next page or a modal to render.

Input parameters:

- `selector` (string): CSS selector to wait for (optional if text_match is given)
- `text_match` (string): Text to wait for. With `selector`, must appear inside that element; without it, anywhere on the page.
- `timeout_ms` (integer): Max time to wait (default 10000 = 10s, max 30000)

### `complete_omnifocus_task` (~93 tokens)

Complete OmniFocus Task

Marks an OmniFocus task as complete by task ID or name. Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to complete; called without it, returns a preview.
- `task_id` (string): Exact task id from list_omnifocus_tasks (preferred). Provide this OR task_name.
- `task_name` (string): Task title to match when you don't have the id.

Output parameters:

- `completed` (boolean)
- `id` (string)
- `name` (string)

### `complete_reminder` (~62 tokens)

Complete Reminder

Marks a reminder complete in Apple Reminders (Reminders.app). Requires confirm=true. For Microsoft To Do use todo_complete_task instead.

Input parameters:

- `confirm` (boolean): Must be true to complete
- `reminder_id` (string, required): Reminder ID from list_reminders

### `configure_clients` (~179 tokens)

Configure Clients

Adds Local MCP to the config of installed MCP-capable AI clients on this Mac (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Zed) so they can use LMCP's tools — no manual JSON editing. Read-only PREVIEW unless confirm:true. Optionally pass a single `client` id to configure just that one. Returns which clients it set up, which already had Local MCP, which aren't installed, and the restart step for each. Pair with list_missing_permissions for fully agent-driven setup.

Input parameters:

- `client` (string): Optional: configure only this client — one of claude, claude-code, cursor, windsurf, vscode, zed. Omit to do all installed ones.
- `confirm` (boolean): Must be true to write the config. Without it, returns a preview of what would change.

### `connect_m365_account` (~43 tokens)

Connect Microsoft 365 Account

Connect your Microsoft 365 account. Call once to get a login code, then call again after you've authenticated at microsoft.com/devicelogin to confirm the connection.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `connect_todoist` (~64 tokens)

Connect Todoist

Connect Todoist using your personal API token (Todoist → Settings → Integrations → Developer → copy the API token). Stored locally, never sent to Claude's servers.

Input parameters:

- `api_token` (string, required): Your Todoist API token (a 40-char hex string)

Output parameters:

- `message` (string)
- `ok` (boolean)

### `create_calendar_event` (~473 tokens)

Create Calendar Event

Creates an event in the Mac's Calendar app (Calendar.app). Requires title, start_date, end_date. Optionally invite attendees by email (CalDAV/Exchange calendars only), or make it a repeating event with `recurrence` (daily/weekly/monthly/yearly). For Microsoft 365 use m365_create_event instead.

Input parameters:

- `attendees` (array): List of email addresses to invite (optional, CalDAV/Exchange only)
- `calendar` (string): Calendar name to match (optional, alternative to calendar_id)
- `calendar_id` (string): Calendar UUID from list_calendar_names (optional, defaults to default calendar)
- `confirm` (boolean): Must be true to create the event
- `end_date` (string, required): ISO 8601 date or datetime, same timezone rules as start_date. For an all-day event pass a bare date (end is inclusive: same date as start = a one-day all-day event; a later date spans through that da…
- `location` (string): Location (optional)
- `notes` (string): Event notes (optional)
- `recurrence` (string): Make it a repeating event: 'daily', 'weekly', 'monthly', or 'yearly' (optional; omit for a one-time event).
- `recurrence_count` (integer): Total number of occurrences (optional). Mutually exclusive with recurrence_until; if neither is given the event repeats indefinitely.
- `recurrence_interval` (integer): Repeat every N periods (optional, default 1 — e.g. recurrence='weekly' + recurrence_interval=2 = every 2 weeks).
- `recurrence_until` (string): ISO 8601 date the repetition stops on (optional; takes precedence over recurrence_count).
- `start_date` (string, required): ISO 8601 date or datetime. With a time (2026-06-27T09:00:00) the event is timed; a time with NO timezone is read in the Mac's LOCAL zone, append Z or an offset (2026-06-27T09:00:00Z, or +02:00) to pi…
- `title` (string, required): Event title

Output parameters:

- `attendees_note` (string)
- `attendees_requested` (array)
- `created` (boolean)
- `end` (string)
- `id` (string)
- `recurrence` (string): Present when the event repeats (human-readable summary)
- `start` (string)
- `title` (string)

### `create_draft` (~245 tokens)

Create Draft

Saves an email to the Mail.app Drafts folder for the user to review and send manually — never sends. Composes a new draft (pass `to`/`subject`/`body`), or a reply draft (pass `reply_to_message_id` plus `body`). On a multi-account Mac, pass `account` (an account name from list_email_accounts) or `from` (a sender address) to place the draft in that account's Drafts; otherwise it lands in the default account. Attach files by passing `attachments` (comma-separated absolute file paths, e.g. a PDF quote) — they are attached to the saved draft. Use this for the cautious user who wants AI-composed mail but insists on sending it themselves.

Input parameters:

- `account` (string)
- `attachments` (string)
- `bcc` (string)
- `body` (string)
- `cc` (string)
- `from` (string)
- `html_body` (string)
- `reply_all` (boolean)
- `reply_to_message_id` (string)
- `subject` (string)
- `to` (string)

Output parameters:

- `account` (string)
- `attachments` (array)
- `attachments_failed` (array)
- `from` (string)
- `kind` (string)
- `reply_to_message_id` (string)
- `saved_draft` (boolean)
- `subject` (string)
- `to` (string)

### `create_email_folder` (~53 tokens)

Create Email Folder

Creates a new mailbox folder in Mail.app.

Input parameters:

- `account` (string): Account name (optional, uses default)
- `confirm` (boolean): Must be true to create
- `name` (string, required): Folder name

Output parameters:

- `created` (boolean)
- `name` (string)

### `create_note` (~83 tokens)

Create Note

Creates a new note in Apple Notes. The body accepts Markdown (headings, bold/italic, bullet/numbered lists, links, inline code) — it's converted to Apple Notes' native formatting. Requires confirm=true to execute.

Input parameters:

- `body` (string, required)
- `confirm` (boolean)
- `folder` (string)
- `name` (string, required)

Output parameters:

- `created` (boolean)
- `id` (string)
- `name` (string)

### `create_omnifocus_task` (~147 tokens)

Create OmniFocus Task

Creates a new task in OmniFocus. Requires confirm=true to execute.

Input parameters:

- `confirm` (boolean): Must be true to create; called without it, returns a preview.
- `defer_date` (string): Defer/start date, ISO 8601 — the task stays hidden until then.
- `due_date` (string): Due date, ISO 8601 (YYYY-MM-DD or full timestamp).
- `flagged` (boolean): Create the task flagged.
- `name` (string, required): The task title.
- `note` (string): Longer note/body for the task.
- `project` (string): Project to file the task under (name). Omit for the inbox.

Output parameters:

- `created` (boolean)
- `id` (string)
- `name` (string)

### `create_referral_invites` (~132 tokens)

Create Referral Invites

Records referral invites for the colleagues a user chose to invite and returns a unique referral link per person, so the user can later see who installed or activated LMCP. It does not send anything itself — each returned link can be included in an email or message to that person. `lang` records the language the invite is written in (e.g. "es", "en").

Input parameters:

- `lang` (string): ISO language of the invite you're writing (the user's conversation language, e.g. 'es', 'en'). Defaults to the Mac's language.
- `recipients` (array, required): The picked recipients.

Output parameters:

- `invites` (array)
- `next` (string)

### `create_reminder` (~94 tokens)

Create Reminder

Creates a reminder in Reminders.app.

Input parameters:

- `confirm` (boolean): Must be true to create
- `due_date` (string): ISO 8601 date (optional)
- `list_name` (string): Reminder list name (optional)
- `notes` (string): Notes (optional)
- `priority` (string): Priority: none | low | medium | high (optional)
- `title` (string, required): Reminder title

### `create_reminder_list` (~52 tokens)

Create Reminder List

Creates a new list in Apple Reminders (Reminders.app). Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to create
- `name` (string, required): Name for the new reminder list

### `daily_brief` (~125 tokens)

Daily Brief

Returns a single morning briefing combining today's calendar events, overdue and due-today reminders, unread inbox email count + subjects, and — when a location is provided — today's weather. Perfect for starting each day: one call gives you everything on your plate.

Input parameters:

- `include_emails` (boolean): Include unread email summary from Mail.app (default true, skipped gracefully if Mail is not running)
- `location` (string): Optional city name or 'lat,lon' to include today's weather in the brief (e.g. 'London', 'San Francisco'). Omitted if not provided.

Output parameters:

- `date` (string): Today's date (YYYY-MM-DD).
- `emails` (object|null): Unread email summary (unread_count + recent_unread), or {skipped} / {error}, or null when not requested.
- `events` (array): Today's calendar events.
- `note` (string): Onboarding enrichment shown when nothing is scheduled.
- `reminders` (array): Reminders due today or overdue.
- `weather` (object): Today's weather (current conditions + forecast), only present when a location was provided.

### `delete_calendar_event` (~136 tokens)

Delete Calendar Event

Deletes an event from the Mac's Calendar app (Calendar.app) by ID. Requires confirm=true. For a repeating event, pass span='future' to delete the whole series (this and all following occurrences); the default deletes only the single occurrence. For Microsoft 365 use m365_delete_event instead.

Input parameters:

- `confirm` (boolean): Must be true to delete
- `event_id` (string, required): Event identifier from list_calendar_events
- `span` (string): For a recurring event: 'this' (default — only this occurrence) or 'future' (this and all following occurrences). Ignored for non-recurring events.

### `delete_reminder` (~65 tokens)

Delete Reminder

Permanently deletes a reminder in Apple Reminders (Reminders.app) by ID. Get the reminder_id from list_reminders. Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to delete
- `reminder_id` (string, required): Reminder identifier from list_reminders

### `delete_reminder_folder` (~93 tokens)

Delete Reminder Folder

Deletes an Apple Reminders list AND all reminders inside it — cannot be undone. Pass the list name (or list_id from get_reminder_folders). Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to delete
- `list_id` (string): List identifier from get_reminder_folders (alternative to name)
- `name` (string): List name to delete (or pass list_id)

### `disconnect_m365_account` (~21 tokens)

Disconnect Microsoft 365 Account

Disconnect your Microsoft 365 account and remove stored tokens.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `disconnect_todoist` (~20 tokens)

Disconnect Todoist

Disconnect Todoist and remove the stored API token.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `excel_create` (~74 tokens)

Excel Create

Creates a new Excel spreadsheet (.xlsx file) with headers and optional data rows.

Input parameters:

- `confirm` (boolean): Must be true to create
- `headers` (array, required): Column headers
- `path` (string, required): Output path for the .xlsx file
- `rows` (array): Array of row arrays with data (optional)

Output parameters:

- `created` (boolean): True when the file was created
- `headers` (array): Column headers written
- `path` (string): Path of the created .xlsx file
- `rows` (integer): Number of data rows written

### `excel_read` (~113 tokens)

Excel Read

Reads data from an Excel spreadsheet (.xlsx file). Returns the first row as `headers` and the remaining data rows as `rows` — mirroring excel_create's `headers`/`rows` params, so a read→create round-trip needs no manual row-0 handling.

Input parameters:

- `max_rows` (integer): Max rows to return (default 100)
- `path` (string, required): Absolute path to the .xlsx file
- `sheet_name` (string): Sheet name to read (optional, reads first sheet)

Output parameters:

- `count` (integer): Number of DATA rows returned (excludes the header row)
- `headers` (array): The first row, as column headers — mirrors excel_create's `headers` param
- `rows` (array): Data rows AFTER the header row (each an array of cell-value strings) — feeds straight into excel_create's `rows`
- `sheet` (string): Name of the sheet that was read
- `sheets` (array): All sheet names in the workbook

### `excel_write_cell` (~95 tokens)

Excel Write Cell

Writes a value to a specific cell in an Excel file.

Input parameters:

- `column` (integer, required): Column number (1-based)
- `confirm` (boolean): Must be true to modify
- `path` (string, required): Path to the .xlsx file
- `row` (integer, required): Row number (1-based)
- `sheet_name` (string): Sheet name (default: first sheet)
- `value` (string, required): Value to write

Output parameters:

- `col` (integer): Column that was written (1-based)
- `row` (integer): Row that was written (1-based)
- `value` (string): Value written to the cell
- `written` (boolean): True when the cell was written

### `file_list` (~96 tokens)

File List

Lists files and folders in a local directory. Defaults to the user's home directory. Returns name, path, type (file/directory), size, and modification date for each item. Sorted: directories first, then files, both alphabetically.

Input parameters:

- `path` (string): Absolute path to the directory. Defaults to the home directory (~) if omitted.
- `show_hidden` (boolean): Include hidden files (starting with '.'). Default false.

Output parameters:

- `count` (integer)
- `dirs` (integer)
- `files` (integer)
- `items` (array)
- `path` (string)

### `file_read` (~241 tokens)

File Read

Reads a plain text file from the local filesystem by its absolute path — the primary, default tool for reading a local text file (use this unless the file is a PDF, Word, Excel, or PowerPoint document, which have their own readers). The path must be inside an allowed folder — the same allowlist as file_write (the home directory by default; extend via Settings → Advanced → Allowed folders). A path outside the allowlist returns an actionable 'access denied' naming the allowed folders. Supports .txt, .md, .csv, .json, .xml, .log, .yaml, .toml and common code file types; auto-detects UTF-8 with Latin-1/Windows-1252 fallback. For files in OneDrive use onedrive_read_file, in Google Drive gdrive_read_file; for PDFs pdf_read, Word word_read, Excel excel_read.

Input parameters:

- `max_bytes` (integer): Maximum bytes to read (default 1 MB, max 10 MB)
- `offset` (integer): Start reading at this byte offset (default 0)
- `path` (string, required): Absolute path to the file

Output parameters:

- `bytes` (integer): Total file size in bytes
- `content` (string): Decoded file text content
- `encoding` (string): Encoding used to decode (utf8 | cp1252 | latin1)
- `offset` (integer): Byte offset the read started at
- `path` (string): Resolved absolute path of the file
- `truncated` (boolean): True if more content remains beyond what was returned

### `file_search` (~134 tokens)

File Search

Searches for files and folders by name (case-insensitive, partial match) starting from a root directory. Defaults to the home directory. Returns matching items with path, type, and size.

Input parameters:

- `file_type` (string): Filter by extension, e.g. 'pdf', 'docx', 'xlsx'. Omit for all types.
- `max_results` (integer): Maximum number of results to return. Default 50, max 200.
- `query` (string, required): Filename pattern to search for (partial, case-insensitive)
- `root` (string): Root directory to search from. Defaults to home directory (~).

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)
- `root` (string)

### `file_write` (~192 tokens)

File Write

Writes text to a local file — create, overwrite, or append. For .txt/.md/.csv/.json/.log and any plain-text or code file. (For Word use word_create, Excel excel_create, PowerPoint ppt_create.) The path must be inside an allowed folder — the same allowlist as file_read (home directory by default; extend via Advanced Settings → Allowed folders). Overwriting an existing file requires confirm=true (the first call returns a preview instead); append=true adds to the end and never needs confirm. Missing parent folders are created.

Input parameters:

- `append` (boolean): Append to the end instead of overwriting (default false)
- `confirm` (boolean): Required (true) to OVERWRITE an existing file. Not needed to create a new file or to append.
- `content` (string, required): Text content to write
- `path` (string, required): Absolute path to the file to write

### `finder_list` (~100 tokens)

Finder List

Lists files and folders in a directory (Spotlight-free). The path must be inside an allowed folder — the same allowlist as file_read/file_write (the home directory by default; extend via Settings → Advanced → Allowed folders). A path outside the allowlist returns an actionable 'access denied' naming the allowed folders.

Input parameters:

- `limit` (integer): Max items (default 100)
- `path` (string): Absolute path to list (default: ~)

Output parameters:

- `count` (integer): Items returned in this response.
- `items` (array)
- `note` (string)
- `path` (string)
- `total` (integer): Total items when the listing was truncated.
- `truncated` (boolean)

### `finder_search` (~110 tokens)

Finder Search

Searches for files by name within the allowlist (uses mdfind/Spotlight). Scoped to the allowed folders — the home directory by default (extend via Settings → Advanced → Allowed folders); pass path= to search a specific allowed folder. A path outside the allowlist returns an actionable 'access denied'.

Input parameters:

- `limit` (integer): Max results (default 50)
- `path` (string): Limit search to this directory (optional)
- `query` (string, required): Filename or content to search for

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)

### `gdrive_file_info` (~50 tokens)

Gdrive File Info

Metadata for a file/folder in the synced Google Drive: size, dates, type. Cheaper than listing the whole directory.

Input parameters:

- `path` (string, required): Absolute path to the file or folder

Output parameters:

- `created` (string)
- `modified` (string)
- `name` (string)
- `path` (string)
- `size` (integer)
- `size_human` (string)
- `type` (string)

### `gdrive_list_files` (~96 tokens)

Gdrive List Files

Lists files and folders in a Google Drive path (the locally-synced folder). Use gdrive_root first for valid roots — 'My Drive' and 'Shared drives' live inside each mount. Returns up to `limit` entries (default 1000).

Input parameters:

- `limit` (integer): Max entries (default 1000, max 5000)
- `path` (string, required): Absolute path to the Google Drive folder

Output parameters:

- `count` (integer)
- `items` (array)
- `note` (string)
- `total` (integer)
- `truncated` (boolean)

### `gdrive_read_file` (~175 tokens)

Gdrive Read File

Reads a text file from the synced Google Drive folder (.txt, .md, .csv, .json, code files...). Note: native Google Docs/Sheets/Slides sync as .gdoc/.gsheet pointers, not real files — export them from Drive or read Office/PDF copies instead. Auto-detects UTF-8 with Latin-1/CP1252 fallback. For files outside Google Drive, use file_read.

Input parameters:

- `encoding` (string): 'auto' (default), 'utf8', 'latin1', 'cp1252', 'ascii', 'utf16'
- `max_bytes` (integer): Max bytes (default 1MB, cap 10MB)
- `offset` (integer): Start byte offset (default 0)
- `path` (string, required): Absolute path to the file

Output parameters:

- `bytes` (integer): Total file size in bytes
- `bytes_read` (integer): Number of bytes read in this slice
- `content` (string): Decoded file text content
- `encoding` (string): Encoding used to decode (utf8 | cp1252 | latin1 | ascii | utf16)
- `offset` (integer): Byte offset the read started at
- `path` (string): Absolute path of the file
- `truncated` (boolean): True if more content remains beyond what was returned

### `gdrive_root` (~61 tokens)

Gdrive Root

Lists the Google Drive folders synced on this Mac (My Drive, Shared drives, per-account mounts). Start here to get valid paths for the other gdrive_* tools. Reads the folder Google Drive for Desktop already syncs — no Google API, no OAuth.

Output parameters:

- `roots` (array)

### `gdrive_search_files` (~80 tokens)

Gdrive Search Files

Searches the synced Google Drive folder for files by name (recursive). Returns up to max_results matches (default 50).

Input parameters:

- `max_results` (integer): Maximum results (default 50)
- `query` (string, required): Filename pattern to search for
- `root` (string): Restrict to this Drive path (optional - defaults to all mounts)

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)

### `gdrive_write_file` (~82 tokens)

Gdrive Write File

Writes or overwrites a text file in the synced Google Drive folder — it uploads automatically via the official client. First call returns a preview; pass confirm=true to write.

Input parameters:

- `confirm` (boolean): Must be true to actually write
- `content` (string, required): Text content to write
- `path` (string, required): Absolute path under a Google Drive mount

Output parameters:

- `bytes` (integer)
- `overwrote` (boolean)
- `path` (string)
- `written` (boolean)

### `get_audit_log` (~113 tokens)

Get Audit Log

Returns recent LMCP tool call history from the local audit log. Each entry shows timestamp, tool name, call source (local/cloud), success status, and duration. Useful for GDPR Article 30 compliance reporting and debugging.

Input parameters:

- `limit` (integer): Number of recent entries to return (default 50, max 200)
- `ok` (boolean): Filter to successes (true) or failures (false) only (optional)
- `tool` (string): Filter to entries for a specific tool name (optional)

Output parameters:

- `count` (integer)
- `entries` (array)

### `get_config` (~19 tokens)

Get Config

Returns the current LMCP configuration (api_key masked).

### `get_contact` (~121 tokens)

Get Contact

Gets a contact from the Mac's Contacts app (Contacts.app) by name or ID. Pass `name` to look up directly by name (no need to search_contacts first — if several people match it returns a compact list to choose from), or `contact_id` for an exact lookup. For Microsoft 365 use m365_get_contact instead.

Input parameters:

- `contact_id` (string): Exact identifier from list_contacts/search_contacts. Provide this OR name.
- `name` (string): Full or partial contact name — the one-step path. Provide this OR contact_id.

### `get_datetime` (~73 tokens)

Get Datetime

Get the current date and time of the machine where LMCP runs — with timezone and UTC offset. Call this whenever you need the real 'now' on the user's computer: before creating calendar events or reminders, resolving relative dates like 'today'/'tomorrow'/'next Friday', or timestamping. Takes no arguments.

Output parameters:

- `epoch_seconds` (integer): Unix epoch seconds.
- `human` (string): Human-readable local date/time.
- `iso_local` (string): Current time in ISO 8601 with the machine's local UTC offset.
- `iso_utc` (string): Current time in ISO 8601, UTC.
- `timezone` (string): IANA timezone identifier.
- `utc_offset` (string): UTC offset like +02:00.
- `weekday` (string)

### `get_m365_person` (~95 tokens)

Get Microsoft 365 Person

Get detailed information about a specific person in your Microsoft 365 directory by their user ID or email address. Use 'me' to get the currently authenticated user's profile.

Input parameters:

- `id` (string, required): User ID (GUID), email address (UPN), or 'me' for the authenticated user, e.g. 'sarah@contoso.com', 'a1b2c3d4-...', or 'me'

Output parameters:

- `department` (string)
- `email` (string)
- `id` (string)
- `mobile` (string)
- `name` (string)
- `office` (string)
- `phones` (array)
- `title` (string)
- `upn` (string)

### `get_reminder_folders` (~41 tokens)

Get Reminder Folders

Lists the lists (folders) in Apple Reminders (Reminders.app) on this Mac. For Microsoft To Do use todo_get_folders instead.

Output parameters:

- `count` (integer)
- `lists` (array)

### `get_weather` (~132 tokens)

Get Weather

Gets the current weather and a short daily forecast for a location. Pass a city name ('London', 'San Francisco', 'Tokyo,JP') or 'lat,lon' coordinates. Uses Open-Meteo — no API key required. Location must be provided (there is no device-location access).

Input parameters:

- `days` (integer): Number of forecast days, 1-7 (default 3)
- `location` (string, required): City name (e.g. 'London', 'Buenos Aires', 'Tokyo,JP') or 'lat,lon' coordinates (e.g. '40.71,-74.01')

### `list_accounts` (~79 tokens)

List Accounts

Lists Mail.app email accounts WITH each account's email addresses and type (imap/pop/iCloud). Slower — queries Mail directly. For just the account NAMES (to pass to list_emails(account=...)), prefer list_email_accounts: it's faster (cached, no Mail lock). Use list_accounts only when you specifically need the addresses or account type.

Output parameters:

- `accounts` (array)
- `count` (integer)

### `list_calendar_events` (~223 tokens)

List Calendar Events

Lists events from the Mac's Calendar app (Calendar.app, local/iCloud calendars) in a date range. Defaults to today + 7 days. For a Microsoft 365 calendar use m365_list_events instead.

Input parameters:

- `calendar` (string): Filter by calendar name — partial, case-insensitive (optional). To pick one of several same-titled calendars, qualify it as "Account/Calendar" (e.g. "Exchange/Calendario") using the source from list_…
- `calendar_id` (string): Filter by a single calendar UUID from list_calendar_names (optional).
- `calendar_ids` (array): Filter by multiple calendar UUIDs (optional).
- `end_date` (string): ISO 8601 date (YYYY-MM-DD). Defaults to start_date + 7 days.
- `limit` (integer): Max number of events to return (most recent first within the range). Optional; defaults to all in range.
- `start_date` (string): ISO 8601 date (YYYY-MM-DD). Defaults to today.

Output parameters:

- `count` (integer)
- `end_date` (string)
- `events` (array)
- `start_date` (string)

### `list_calendar_names` (~39 tokens)

List Calendar Names

Lists the calendars in the Mac's Calendar app (Calendar.app, local/iCloud). For Microsoft 365 calendars use the m365 calendar tools instead.

Output parameters:

- `calendars` (array)
- `count` (integer)

### `list_contacts` (~52 tokens)

List Contacts

Lists contacts from the macOS Contacts app. Optionally filter by group.

Input parameters:

- `group_name` (string): Filter by group name (optional)
- `limit` (integer): Max contacts to return (default 100)

Output parameters:

- `contacts` (array)
- `count` (integer)

### `list_displays` (~56 tokens)

List Displays

Lists connected displays with bounds (global space, top-left origin, points), backing scale_factor, and which is main. display_id is stable for the session so a scripted run can target the same display across steps. No permission required.

### `list_email_accounts` (~80 tokens)

List Email Accounts

Lists all Mail.app account NAMES (fast — cached, no Mail lock). This is the preferred way to get account names: call it first to discover them, then use list_emails(account=name) to fetch messages from a specific account. If you also need each account's email addresses or type (imap/pop/iCloud), use list_accounts instead.

Output parameters:

- `accounts` (array): Mail.app accounts, by name.
- `count` (integer): Number of accounts.
- `tip` (string)

### `list_email_folders` (~176 tokens)

List Email Folders

Lists the full folder (mailbox) tree for Apple Mail (Mail.app) accounts, including nested subfolders. Use this to discover the exact folder names that move_email(target_mailbox=...) and list_emails(mailbox=...) expect. Outlook.com, Exchange, Gmail, iCloud and IMAP accounts added to Mail.app are all included. For a Graph-only Microsoft 365 mailbox not added to Mail.app, use m365_list_emails instead.

Pass account=<name> (from list_email_accounts) to enumerate one account fully; without it, every account is walked which can be slow on macOS 15+. Message counts are off by default (slow on IMAP) — pass include_counts=true to add unread/total per folder.

Input parameters:

- `account` (string)
- `include_counts` (boolean)

Output parameters:

- `account_count` (integer)
- `accounts` (array)
- `folder_count` (integer)
- `next_actions` (array)
- `truncated` (boolean)

### `list_emails` (~223 tokens)

List Emails

Use this when the user wants to see or triage their inbox on this Mac (Apple Mail — any account added to Mail.app: iCloud, Gmail, IMAP, Exchange). Lists email headers (subject, sender, date, unread); call read_email(message_id) for the full body. For a Microsoft 365 mailbox NOT added to Mail.app, use m365_list_emails.

IMPORTANT: On machines with 3+ accounts, always pass account=<name> (from list_email_accounts) to avoid timeouts. Without account, all accounts are scanned which can be slow on macOS 15+.

Supports pagination: use offset to page through results (e.g. offset=20 for page 2 with limit=20). The limit parameter is capped at 50 per call (default 20); to read more, page with offset rather than requesting a larger limit.

Input parameters:

- `account` (string)
- `limit` (integer)
- `mailbox` (string)
- `offset` (integer)
- `unread_only` (boolean)

Output parameters:

- `count` (integer)
- `messages` (array)
- `next_actions` (array)
- `offset` (integer)

### `list_m365_people_insights` (~65 tokens)

List Microsoft 365 People Insights

List the people most relevant to you in Microsoft 365 — based on your communication patterns, collaboration history, and org chart. Useful for meeting prep and contact enrichment.

Input parameters:

- `limit` (integer): Number of people to return (default 20, max 50)

Output parameters:

- `count` (integer)
- `people` (array)

### `list_message_chats` (~32 tokens)

List Message Chats

Lists recent iMessage/Messages.app conversations.

Input parameters:

- `limit` (integer): Max conversations (default 30)

Output parameters:

- `chats` (array)
- `count` (integer)
- `note` (string)

### `list_missing_permissions` (~116 tokens)

List Missing Permissions

Returns the macOS privacy (TCC) permissions Local MCP needs that are NOT granted yet, each with a one-click `open_url` that opens the exact System Settings → Privacy & Security pane. Read-only and passive (never prompts). Use it during setup or before a workflow to tell the user precisely which "Allow" clicks remain (Calendar, Contacts, Reminders, Mail Automation, Full Disk Access, Screen Recording, Accessibility) instead of failing mid-task. `all_granted: true` means nothing is left to do.

Output parameters:

- `all_granted` (boolean)
- `granted` (array)
- `missing` (array)
- `summary` (string)

### `list_notes` (~35 tokens)

List Notes

Lists notes from Apple Notes app. Optionally filter by folder.

Input parameters:

- `folder` (string)
- `limit` (integer)

Output parameters:

- `count` (integer)
- `next_actions` (array)
- `notes` (array)
- `total` (integer)

### `list_omnifocus_folders` (~63 tokens)

List OmniFocus Folders

Lists folders in OmniFocus. Folders group related projects (e.g. "Work", "Personal"). Use list_omnifocus_projects to see the projects inside them.

Input parameters:

- `limit` (integer): Max folders to return (default 100).

Output parameters:

- `count` (integer)
- `folders` (array)

### `list_omnifocus_projects` (~50 tokens)

List OmniFocus Projects

Lists projects in OmniFocus.

Input parameters:

- `include_completed` (boolean): Include completed/dropped projects (default excludes them).
- `limit` (integer): Max projects to return (default 100).

Output parameters:

- `count` (integer)
- `projects` (array)

### `list_omnifocus_tags` (~20 tokens)

List OmniFocus Tags

Lists all tags defined in OmniFocus.

Output parameters:

- `count` (integer)
- `tags` (array)

### `list_omnifocus_tasks` (~130 tokens)

List OmniFocus Tasks

Lists tasks from OmniFocus. Filter by project, tag, inbox, due today, or flagged status.

Input parameters:

- `due_today` (boolean): Only tasks due today or overdue.
- `flagged` (boolean): Only flagged tasks.
- `inbox` (boolean): Only unfiled inbox tasks.
- `include_completed` (boolean): Include completed tasks (default excludes them).
- `limit` (integer): Max tasks to return (default 50).
- `project` (string): Only tasks in this project (name, case-insensitive).
- `tag` (string): Only tasks carrying this tag.

Output parameters:

- `count` (integer)
- `tasks` (array)

### `list_referral_candidates` (~83 tokens)

List Referral Candidates

Returns the user's emailable contacts plus an invite template, for recommending LMCP to a colleague. A user would invoke this when they want to invite or recommend someone. Returns a list of candidate contacts and a message template; create_referral_invites then generates each chosen person's unique invite link.

Input parameters:

- `limit` (integer): Max contacts to return (default 60)

Output parameters:

- `candidates` (array)
- `count` (integer)
- `how_to` (string)
- `lang` (string)
- `template_body` (string)
- `template_subject` (string)

### `list_reminders` (~103 tokens)

List Reminders

Lists reminders from Apple Reminders (Reminders.app) on this Mac. Optionally filter by completion status or list name. For Microsoft To Do use todo_list_tasks instead.

Input parameters:

- `completed` (boolean): true=completed, false=incomplete (default), omit=all
- `limit` (integer): Max number of reminders to return (earliest due first). Optional; defaults to all.
- `list_name` (string): Filter by reminder list name (optional)

Output parameters:

- `count` (integer): Number returned in this response.
- `reminders` (array)
- `total` (integer): Total matching before the limit.

### `list_safari_bookmarks` (~55 tokens)

List Safari Bookmarks

Lists Safari bookmarks (title + URL) from the Mac's Safari (reads ~/Library/Safari/Bookmarks.plist — needs Full Disk Access).

Input parameters:

- `limit` (integer): Max bookmarks to return (default 100)

Output parameters:

- `bookmarks` (array)
- `count` (integer)
- `note` (string): Present when bookmarks can't be read (e.g. Full Disk Access needed).

### `list_windows` (~121 tokens)

List Windows

Lists on-screen windows of any app with window_id, owning app bundle id + name, title, bounds (global space, top-left, points), display_id, and is_focused. Window TITLES require Screen Recording permission — without it this returns an explicit permission_required error rather than a title-less result. Optional app_bundle_id filter. window_id is stable within the session for later targeting.

Input parameters:

- `app_bundle_id` (string): Only return windows owned by this app bundle id.
- `on_screen_only` (boolean): Only on-screen windows (default true).

### `lmcp_install_upgrade` (~112 tokens)

LMCP Install Upgrade

Checks for and installs LMCP updates. Installing downloads the update and RESTARTS LMCP (the AI client briefly reconnects), so it requires confirm=true. Pass check_only=true to only report whether an update is available, with no download or restart.

Input parameters:

- `check_only` (boolean): If true, only report availability — no download, no install, no restart.
- `confirm` (boolean): Must be true to actually install (which restarts LMCP). Without it, returns availability + a preview.

### `lmcp_state` (~301 tokens)

LMCP State

Returns a structured snapshot of the LMCP environment: server/tray/teams-proxy versions, detected AI client, cloud relay state, TCC permission states (Calendar/Reminders/Contacts), and a compact summary of which services (Mail/Calendar/Contacts/Teams/OneDrive/Reminders/Notes) are reachable. Fast (<500ms), passive — never prompts the user, never opens app windows, never touches the network. Call this when you need to verify the environment is healthy before attempting a tool, or to understand what's installed and accessible. If `services.scan_pending` is true, the background service scan hasn't finished yet (just after startup) and the per-service running/accounts values are placeholders — do NOT treat them as a real outage; just call the tool you need. Otherwise `services.scanned_seconds_ago` tells you how many seconds ago that scan ran (cadence ~60s): the per-service values are a snapshot, NOT a live probe. A `false`/`0`/`not available` for a service is advisory only — it can be stale (e.g. the user connected WhatsApp or opened Mail seconds ago) — so never use this tool as a preflight gate to skip or cancel a task; the actual tool call is the source of truth, just attempt it. For reporting failures, use `report_problem` instead — it captures this same snapshot plus logs and submits to the team.

Output parameters:

- `ai_client` (string)
- `arch` (string)
- `build_sha` (string): Git short SHA of the build (F-041). 'unknown' if unstamped.
- `built_at` (string): UTC build timestamp (F-041). 'unknown' if unstamped.
- `cloud_data_enabled` (boolean)
- `cloud_token_set` (boolean)
- `last_activity` (object)
- `license_status` (string): trial | active | expired
- `machine_id` (string)
- `os_version` (string)
- `services` (object): Per-domain reachability summary (mail, calendar, contacts, teams, onedrive, slack, …); shape varies by domain. May include `scan_pending: true` right after startup, meaning the per-service running/ac…
- `tcc` (object): TCC permission states, e.g. granted | denied | authorized.
- `tray_version` (string)
- `tunnel_connected` (boolean)
- `update` (object)
- `version` (string): Serving (running) server version.

### `lmcp_upgrade_diagnostics` (~86 tokens)

LMCP Upgrade Diagnostics

Returns the self-update health state: current version, last N update attempts (with errors), writability of the update cache, and any stale LMCP binaries found at alternate paths. Call this when auto-update seems stuck or when you need to explain to a user why they're on an old version.

Input parameters:

- `limit` (integer): Max recent attempts to return (default 10)

Output parameters:

- `binaries_found` (array): LMCP binaries found at known alternate paths.
- `cache_dir` (string)
- `cache_writable` (boolean): Whether the update cache dir is writable (#1 silent-failure cause).
- `consecutive_failures` (integer)
- `current_version` (string)
- `last_success_at` (string): ISO 8601 timestamp of last successful update, empty if none.
- `recent_attempts` (array): Recent update attempts, newest first.
- `recent_attempts_count` (integer)
- `running_from` (string): Real path of the currently running binary.

### `lmcp_welcome` (~116 tokens)

LMCP Welcome

Onboarding overview for a newly installed LMCP connection. Returns a live snapshot of what LMCP can currently see on this Mac — today's calendar events, due reminders, unread email counts, and total contacts — along with a short list of the domains LMCP reaches (Mail, Calendar, Contacts, Files, and more) and example first requests. Useful right after install, when a user wants to see what LMCP can do. Available only until the first other tool call runs, after which it is removed from the tool list.

Output parameters:

- `automations` (array): Example multi-step prompts the user can try immediately.
- `capabilities` (array): Domains grouped by the job the user wants done, each with one example ask (the discoverability map).
- `domains` (array): Every domain LMCP reaches, with a short capability blurb.
- `snapshot` (object)

### `m365_create_event` (~186 tokens)

Microsoft 365 Create Event

Create a calendar event in your Microsoft 365 / Outlook calendar.

Input parameters:

- `attendees` (string): Comma-separated email addresses to invite (optional)
- `body` (string): Event description (optional)
- `calendar` (string): Calendar name to create the event in — partial, case-insensitive match (optional). Omit to use the primary calendar.
- `end` (string, required): End time in ISO 8601, e.g. '2026-05-20T11:00:00'
- `location` (string): Location (optional)
- `start` (string, required): Start time in ISO 8601, e.g. '2026-05-20T10:00:00'
- `subject` (string, required): Event title
- `timezone` (string): IANA timezone, e.g. 'America/New_York' (default: UTC)

Output parameters:

- `id` (string)
- `message` (string)
- `ok` (boolean)

### `m365_delete_event` (~55 tokens)

Microsoft 365 Delete Event

Delete a calendar event from your Microsoft 365 / Outlook calendar by its ID.

Input parameters:

- `confirm` (boolean, required): Set to true to confirm deletion (required)
- `id` (string, required): Event ID from m365_list_events

Output parameters:

- `message` (string)
- `ok` (boolean)

### `m365_get_contact` (~56 tokens)

Microsoft 365 Get Contact

Get full details of a specific Microsoft 365 contact by ID. Get the ID from m365_list_contacts or m365_search_contacts.

Input parameters:

- `id` (string, required): Contact ID from m365_list_contacts or m365_search_contacts

Output parameters:

- `business_address` (string)
- `company` (string)
- `emails` (array)
- `given_name` (string)
- `home_address` (string)
- `id` (string)
- `mobile` (string)
- `name` (string)
- `notes` (string)
- `phones` (array)
- `surname` (string)
- `title` (string)

### `m365_list_contacts` (~41 tokens)

Microsoft 365 List Contacts

List contacts from your Microsoft 365 / Outlook address book.

Input parameters:

- `limit` (integer): Max contacts to return (default 50, max 100)

Output parameters:

- `contacts` (array)
- `count` (integer)
- `query` (string)

### `m365_list_emails` (~130 tokens)

Microsoft 365 List Emails

Use this when the user wants their Microsoft 365 / Outlook / Exchange inbox via the cloud — requires a connected M365 account (connect_m365_account). Returns subject, sender, date, and preview. For mail already in the Mac's Mail.app (including an Exchange account added there), use list_emails.

Input parameters:

- `folder` (string): Folder name: inbox (default), sentitems, drafts, deleteditems
- `limit` (integer): Number of emails to return (default 20, max 50)
- `unread_only` (boolean): If true, return only unread emails

Output parameters:

- `count` (integer)
- `emails` (array)

### `m365_list_events` (~92 tokens)

Microsoft 365 List Events

List upcoming calendar events from your Microsoft 365 / Outlook calendar.

Input parameters:

- `calendar` (string): Calendar name to filter by — partial, case-insensitive match (optional). Omit to use the primary calendar.
- `days` (integer): Number of days ahead to look (default 7, max 30)
- `limit` (integer): Max events to return (default 20, max 50)

Output parameters:

- `calendar` (string)
- `count` (integer)
- `days` (integer)
- `events` (array)

### `m365_read_email` (~89 tokens)

Microsoft 365 Read Email

Use this when the user wants the full content of a Microsoft 365 email (message ID from m365_list_emails/m365_search_emails). Requires a connected M365 account. For a message found via list_emails/search_emails (Apple Mail), use read_email.

Input parameters:

- `id` (string, required): The email message ID from m365_list_emails or m365_search_emails

Output parameters:

- `body` (string)
- `cc` (array)
- `date` (string)
- `from` (string)
- `from_address` (string)
- `id` (string)
- `is_read` (boolean)
- `subject` (string)
- `to` (array)

### `m365_reply_email` (~139 tokens)

Microsoft 365 Reply Email

Use this when the user wants to reply to a Microsoft 365 email (message ID from m365_list_emails). Requires a connected M365 account. Shows a preview first — set confirm=true to actually send. For replying to a message found in Apple Mail, use reply_email.

Input parameters:

- `confirm` (boolean): Set to true to actually send (default: shows preview only)
- `id` (string, required): Message ID to reply to (from m365_list_emails or m365_read_email)
- `message` (string, required): Your reply text
- `reply_all` (boolean): If true, reply to all recipients (default: false)

Output parameters:

- `message` (string)
- `ok` (boolean)

### `m365_search_contacts` (~43 tokens)

Microsoft 365 Search Contacts

Search contacts in your Microsoft 365 address book by name, email, or company.

Input parameters:

- `query` (string, required): Search term — name, email, or company

Output parameters:

- `contacts` (array)
- `count` (integer)
- `query` (string)

### `m365_search_emails` (~111 tokens)

Microsoft 365 Search Emails

Use this when the user wants to find emails in their Microsoft 365 / Outlook mailbox via the cloud — requires a connected M365 account. Searches by keyword, sender, or subject. For accounts added to the Mac's Mail.app, use search_emails.

Input parameters:

- `limit` (integer): Max results (default 20, max 50)
- `query` (string, required): Search query, e.g. 'budget Q2', 'from:alice@contoso.com', 'subject:invoice'

Output parameters:

- `count` (integer)
- `emails` (array)
- `query` (string)

### `m365_send_email` (~137 tokens)

Microsoft 365 Send Email

Use this when the user wants to send from their Microsoft 365 / Outlook account via the cloud — requires a connected M365 account. Shows a preview first — set confirm=true to actually send. For sending from an account configured in the Mac's Mail.app, use send_email.

Input parameters:

- `body` (string, required): Email body (plain text)
- `cc` (string): CC recipients (optional, comma-separated)
- `confirm` (boolean): Set to true to actually send (default: shows preview only)
- `subject` (string, required): Email subject
- `to` (string, required): Recipient email address. For multiple, separate with commas.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `media_probe` (~68 tokens)

Media Probe

Reports duration_ms, width, height, fps, whether it has audio, and file size for a video/audio file. Call it before editing to reason about the footage (compute trim ranges, pick a reframe crop). No permission required.

Input parameters:

- `path` (string, required): Path to the media file.

### `move_email` (~136 tokens)

Move Email

Moves an email to another mailbox (nested target folders are found by name). Pass account= (returned by list_emails/search_emails) so the message lookup targets one account instead of scanning all of them — without it, multi-account Macs are slow and can time out on bulk moves. If you know the folder the message is in, also pass mailbox= (the `mailbox` field from the listing) so the lookup searches it first.

Input parameters:

- `account` (string)
- `confirm` (boolean)
- `mailbox` (string)
- `message_id` (string, required)
- `target_mailbox` (string, required)

Output parameters:

- `message_id` (string)
- `moved` (boolean)
- `to` (string)
- `warning` (string)

### `nordvpn_diagnose` (~38 tokens)

NordVPN Diagnose

Run a diagnostic check on NordVPN: installation, login state, connection status, kill switch, and supported protocols. Useful for troubleshooting.

Output parameters:

- `account` (string)
- `auto_connect` (boolean): True if auto-connect / connect on demand is on
- `connected` (boolean): True if the VPN is connected
- `installed` (boolean): True if NordVPN is installed
- `kill_switch` (boolean): True if the kill switch is enabled
- `last_location` (string)
- `logged_in` (boolean): True if a NordVPN account is logged in
- `protocols` (array): Supported VPN protocols
- `report` (string): Full formatted text report
- `running` (boolean): True if the NordVPN app is running
- `subscription` (string)
- `version` (string)

### `nordvpn_servers` (~137 tokens)

NordVPN Servers

Get recommended NordVPN servers by country or specialty. Uses NordVPN public API (no account needed). Returns server name, hostname, country, city, load %, and supported technologies.

Input parameters:

- `country` (string): Country name or 2-letter code (e.g. 'US', 'United States', 'JP'). Omit for auto-recommendation.
- `limit` (integer): Number of servers to return (1-10). Default: 5.
- `type` (string): Server type filter: 'standard', 'p2p', 'double_vpn', 'onion', 'dedicated_ip'. Default: standard.

Output parameters:

- `count` (integer)
- `note` (string)
- `servers` (array)

### `nordvpn_status` (~36 tokens)

NordVPN Status

Check NordVPN connection status: connected/disconnected, auto-connect, snooze, and last known location. Does NOT open NordVPN.

Output parameters:

- `app_running` (boolean)
- `auto_connect` (boolean)
- `connected` (boolean)
- `installed` (boolean)
- `last_location` (string)
- `snoozed_until` (string)
- `version` (string)

### `notion_list_databases` (~36 tokens)

Notion List Databases

Lists Notion databases cached on this Mac with their schema (column names and types). Use notion_read_database to get the rows.

Output parameters:

- `count` (integer)
- `databases` (array)
- `note` (string)

### `notion_list_pages` (~81 tokens)

Notion List Pages

Lists Notion pages cached on this Mac (titles, last edited, hierarchy), newest first. Reads the Notion desktop app's local cache — no Notion API, no integration token. Note: only pages visited in Notion (or marked Available offline) are cached.

Input parameters:

- `limit` (integer): Max pages (default 50, max 500)

Output parameters:

- `count` (integer)
- `note` (string)
- `pages` (array)

### `notion_list_workspaces` (~28 tokens)

Notion List Workspaces

Lists the Notion workspaces cached on this Mac with their members (names and emails).

Output parameters:

- `known_users` (array)
- `workspaces` (array)

### `notion_open_page` (~73 tokens)

Notion Open Page

Opens a Notion page in the desktop app (deep link). Accepts a page id or title. Useful to let the user view or edit a page, or to pull an uncached page into the local cache.

Input parameters:

- `page` (string, required): Page id (UUID) or title (partial, case-insensitive)

Output parameters:

- `opened` (boolean)
- `page_id` (string)
- `title` (string)

### `notion_read_database` (~82 tokens)

Notion Read Database

Reads the cached rows of a Notion database with their properties mapped through the schema. Accepts the database id or name (partial match). Only locally-cached rows are returned.

Input parameters:

- `database` (string, required): Database id (UUID) or name (partial, case-insensitive)
- `limit` (integer): Max rows (default 50, max 500)

Output parameters:

- `count` (integer)
- `note` (string)
- `rows` (array)

### `notion_read_page` (~122 tokens)

Notion Read Page

Reads a Notion page from the local cache and returns its content as markdown (headings, lists, to-dos, code, files, subpage links). Accepts a page id or a title (partial match). If parts of the page aren't cached yet, says so — open the page in Notion or mark it Available offline for full content.

Input parameters:

- `max_blocks` (integer): Max blocks to render (default 300, max 1000)
- `page` (string, required): Page id (UUID) or title (partial, case-insensitive)

Output parameters:

- `blocks_rendered` (integer)
- `content_markdown` (string)
- `id` (string)
- `last_edited` (string)
- `note` (string)
- `title` (string)
- `uncached_blocks` (integer)

### `notion_search` (~83 tokens)

Notion Search

Searches cached Notion content (page titles and block text) for a phrase, case-insensitive. Returns matching blocks with the page they belong to. Only locally-cached content is searched — pages never opened in Notion won't match.

Input parameters:

- `limit` (integer): Max results (default 20, max 100)
- `query` (string, required): Text to search for

Output parameters:

- `count` (integer)
- `note` (string)
- `query` (string)
- `results` (array)

### `onedrive_delete_file` (~47 tokens)

OneDrive Delete File

Deletes a file or empty folder from OneDrive.

Input parameters:

- `confirm` (boolean): Must be true to delete
- `path` (string, required): Absolute path to the file or folder

Output parameters:

- `deleted` (boolean)
- `path` (string)

### `onedrive_file_info` (~58 tokens)

OneDrive File Info

Returns metadata for a file or folder: size, modification date, type, and extension. Faster than listing the parent directory when you only need info about one item.

Input parameters:

- `path` (string, required): Absolute path to the file or folder

Output parameters:

- `created` (string)
- `extension` (string)
- `modified` (string)
- `name` (string)
- `path` (string)
- `size` (integer)
- `size_human` (string)
- `type` (string): file | directory

### `onedrive_list_files` (~118 tokens)

OneDrive List Files

Lists files and folders in a OneDrive path. Use onedrive_root to find valid paths. Returns up to `limit` entries (default 1000, max 5000); large folders are truncated with a note — narrow the path for more specific results.

Input parameters:

- `limit` (integer): Max entries to return (default 1000, max 5000). Folders with more entries are truncated; the response sets truncated=true and reports the total.
- `path` (string, required): Absolute path to the OneDrive folder

Output parameters:

- `count` (integer): Entries returned in this response.
- `items` (array)
- `note` (string)
- `total` (integer): Total entries in the folder.
- `truncated` (boolean): True when total exceeds the limit.

### `onedrive_move_file` (~53 tokens)

OneDrive Move File

Moves or renames a file/folder within OneDrive.

Input parameters:

- `confirm` (boolean): Must be true to move
- `destination` (string, required): Destination path
- `source` (string, required): Source path

Output parameters:

- `from` (string)
- `moved` (boolean)
- `to` (string)

### `onedrive_read_file` (~188 tokens)

OneDrive Read File

Reads a text file from your OneDrive synced folder. Supports .txt, .md, .csv, .json, .xml, .log and several code file types. Auto-detects UTF-8, falls back to Latin-1/Windows-1252 for legacy files (common in Latin American banking .TXT padrones). For files elsewhere on this Mac, use file_read.

Input parameters:

- `encoding` (string): Force a specific encoding: 'auto' (default), 'utf8', 'latin1', 'cp1252', 'ascii', 'utf16'
- `max_bytes` (integer): Maximum bytes to read (default 1048576 = 1 MB, capped at 10485760 = 10 MB)
- `offset` (integer): Start reading at byte offset (default 0)
- `path` (string, required): Absolute path to the file

Output parameters:

- `bytes` (integer): Total file size in bytes
- `bytes_read` (integer): Number of bytes read in this slice
- `content` (string): Decoded file text content
- `encoding` (string): Encoding used to decode (utf8 | cp1252 | latin1 | ascii | utf16)
- `offset` (integer): Byte offset the read started at
- `path` (string): Absolute path of the file
- `truncated` (boolean): True if more content remains beyond what was returned

### `onedrive_root` (~20 tokens)

OneDrive Root

Lists all mounted OneDrive directories on this Mac.

Output parameters:

- `roots` (array)

### `onedrive_search_files` (~87 tokens)

OneDrive Search Files

Searches for files by name in a OneDrive directory (recursive). Returns up to max_results matches (default 50); raise max_results or narrow the root for more.

Input parameters:

- `max_results` (integer): Maximum results (default 50)
- `query` (string, required): Filename pattern to search for
- `root` (string): Root OneDrive path to search in (optional)

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)

### `onedrive_set_scope` (~134 tokens)

OneDrive Set Scope

Restricts LMCP's OneDrive access to a specific folder. Once set, all OneDrive tools (read, write, list, search, delete, move) only work inside the allowed folder. Pass an empty folder to remove the restriction. Changes take effect immediately.

Input parameters:

- `confirm` (boolean): Must be true to apply
- `folder` (string): Allowed folder path relative to the root (e.g. '/000-Claude Personal Agent'). Empty string removes the scope.
- `root_name` (string, required): OneDrive root name (from onedrive_root, e.g. 'OneDrive-WPPCloud')

Output parameters:

- `access` (string)
- `allowed_folder` (string)
- `effect` (string)
- `root` (string)
- `scope_removed` (boolean)
- `scope_set` (boolean)

### `onedrive_write_file` (~59 tokens)

OneDrive Write File

Writes text content to a file in OneDrive.

Input parameters:

- `confirm` (boolean): Must be true to write
- `content` (string, required): Text content to write
- `path` (string, required): Absolute path to the file in OneDrive

Output parameters:

- `bytes` (integer)
- `path` (string)
- `written` (boolean)

### `outlook_diagnose` (~48 tokens)

Outlook Diagnose

Checks which email accounts are configured in Microsoft Outlook and compares them with Mail.app. If Outlook has accounts not in Mail.app, guides the user to add them so all email tools work seamlessly.

Output parameters:

- `installed` (boolean): True if Microsoft Outlook is installed
- `mail_app_accounts` (array)
- `missing_from_mail_app` (array): Outlook account emails not present in Mail.app
- `note` (string): Plain-language guidance
- `outlook_accounts` (array)
- `report` (string): Full formatted text report

### `pdf_read` (~49 tokens)

PDF Read

Reads and extracts text from a PDF document (.pdf file).

Input parameters:

- `max_pages` (integer): Max pages to extract (default: all)
- `path` (string, required): Absolute path to the PDF file

Output parameters:

- `chars` (integer): Number of characters in the extracted text
- `pages` (integer): Total number of pages in the PDF
- `text` (string): Extracted text content

### `permissions_status` (~79 tokens)

Permissions Status

Reports the TCC permission state (screen recording, accessibility, microphone) this app needs to capture the screen and drive other apps' UI. Call it before a capture/automation run and surface the grant hints instead of failing mid-sequence. Screen Recording / Accessibility are granted in System Settings (not a JIT dialog); the URLs open the exact pane.

### `ppt_create` (~145 tokens)

PowerPoint Create

Creates a PowerPoint presentation (.pptx) at `path` from an array of slides, each {title, bullets:[…]}. Requires confirm=true — called without it, returns a preview of the deck instead of writing the file. The path must be somewhere Local MCP can write; Desktop/Documents/Downloads may need a one-time Files-and-Folders grant (System Settings → Privacy & Security → Files and Folders). Returns {created, path, slides}.

Input parameters:

- `confirm` (boolean): Must be true to create
- `path` (string, required): Output path for the .pptx file
- `slides` (array, required): Array of {title, bullets:[]} slide objects

Output parameters:

- `created` (boolean): True when the file was created
- `path` (string): Path of the created .pptx file
- `slides` (integer): Number of slides created

### `ppt_read` (~37 tokens)

PowerPoint Read

Reads slide text content from a PowerPoint presentation (.pptx file).

Input parameters:

- `path` (string, required): Absolute path to the .pptx file

Output parameters:

- `count` (integer): Number of slides
- `slides` (array): Per-slide structured content ({slide, title, bullets[]}), mirroring ppt_create

### `read_email` (~351 tokens)

Read Email

Use this when the user wants the full content of an email that lives in the Mac's Apple Mail (message ID from list_emails/search_emails). For a Microsoft 365 message ID from m365_list_emails, use m365_read_email. Pass account= (and mailbox= if known, both from list_emails/search_emails) so the lookup targets one account instead of scanning all of them. Call sequentially, not in parallel — concurrent calls serialize behind Mail.app's JXA lock and later calls will time out.

Performance: body fetch is the primary latency source (avg 20s on slow IMAP). Pass include_body=false to skip it and get metadata-only (fast). Pass max_body_chars=N to cap the body at N chars after HTML stripping (default 30000; 0=unlimited). Response includes body_fetch_ms when fetch took >2s, body_omitted=true when skipped, body_truncated_at=N when cut.

When a body isn't cached on this Mac, read_email returns metadata with body_omitted=true and body_omit_reason="not_downloaded" (iCloud/IMAP optimized storage) rather than making Mail fetch it (that can be slow and tie Mail up). If the user wants it anyway, retry with force_download=true to have Mail pull the body over IMAP now and return it (waits up to ~60s). Off by default; ignored while Mail is in a cooldown.

Input parameters:

- `account` (string)
- `force_download` (boolean)
- `include_body` (boolean)
- `mailbox` (string)
- `max_body_chars` (integer)
- `message_id` (string, required)

Output parameters:

- `account` (string)
- `body` (string)
- `body_fetch_ms` (integer)
- `body_omitted` (boolean)
- `body_truncated_at` (integer)
- `cc` (string)
- `date` (string)
- `from` (string)
- `id` (string)
- `mailbox` (string)
- `subject` (string)
- `to` (string)
- `unread` (boolean)

### `read_messages` (~66 tokens)

Read Messages

Reads messages from an iMessage conversation by chat ID or contact name.

Input parameters:

- `chat_id` (string): Chat identifier from list_message_chats
- `contact_name` (string): Contact name substring (alternative to chat_id)
- `limit` (integer): Max messages (default 50)

Output parameters:

- `chat_id` (string)
- `count` (integer)
- `messages` (array)
- `note` (string)

### `read_note` (~36 tokens)

Read Note

Reads the full content of a note by name or ID.

Input parameters:

- `note_id` (string)
- `note_name` (string)

Output parameters:

- `body` (string)
- `folder` (string)
- `id` (string)
- `modified` (string)
- `name` (string)

### `recipe_delete` (~125 tokens)

Recipe Delete

Use this when the user wants to remove one of THEIR saved recipes/skills (the manifests under ~/.local/share/local-mcp/recipes). Destructive with a preview gate: the first call (without confirm) shows what would be deleted; call again with confirm=true to actually delete. Bundled starter recipes can't be deleted. To modify a recipe instead, recipe_save with the same name overwrites it (upsert).

Input parameters:

- `confirm` (boolean): Must be true to actually delete. Without it, returns a preview.
- `name` (string, required): Recipe name (from recipe_list).

Output parameters:

- `deleted` (boolean)
- `name` (string)
- `status` (string): 'preview' when confirm was not set.

### `recipe_discover` (~111 tokens)

Recipe Discover

Browses installable community SKILLS — ready-made LMCP workflows other people published (a morning brief, inbox triage, a weekly report). A user would browse them to find a ready-made workflow for a repeatable multi-app task instead of building it from scratch. Returns a list of {id, title, category, description, steps, votes}; install one with recipe_install(id).

Input parameters:

- `category` (string): Optional filter hint shown to the user; the catalog is small so all skills are returned.

### `recipe_export` (~108 tokens)

Recipe Export

Exports a saved SKILL (recipe) as a single portable token the user can send to someone else — paste it in a message, email, or doc. The recipient installs it with recipe_import and runs it with recipe_run. A user would export a skill to share it with a teammate (a handy brief, a report, a workflow). Returns {name, skill_token} plus the readable manifest.

Input parameters:

- `name` (string, required): Name of the saved skill to export (see recipe_list).

### `recipe_get` (~31 tokens)

Recipe Get

Returns the full manifest of a recipe by name. recipe_not_found if unknown.

Input parameters:

- `name` (string, required)

Output parameters:

- `description` (string)
- `name` (string)
- `params` (array)
- `steps` (array)

### `recipe_import` (~123 tokens)

Recipe Import

Installs a SKILL someone shared with you — pass the skill_token from their recipe_export (or a raw recipe manifest JSON). Saves it to this Mac so recipe_run can use it. Safe: importing only stores the skill; when it's later run, any state-changing step (send/write/delete) previews first and needs confirmation. If a skill with the same name already exists, the import is saved under a non-colliding name. Returns {name, imported}.

Input parameters:

- `skill` (string, required): A skill_token from recipe_export, or a raw recipe manifest JSON string.

### `recipe_install` (~101 tokens)

Recipe Install

Installs a community SKILL by id (from recipe_discover) onto this Mac so recipe_run can use it. Safe: installing only stores the skill; when it's later run, any state-changing step (send/write/delete) previews first and needs confirmation. If a skill with the same name already exists, it's saved under a non-colliding name. Returns {name, installed}.

Input parameters:

- `id` (integer, required): The community skill id from recipe_discover.

### `recipe_list` (~82 tokens)

Recipe List

Lists the user's reusable SKILLS — saved recipes (an ordered sequence of LMCP tool calls with parameters), plus bundled ones — each runnable with recipe_run. Skills turn a repeated LMCP workflow into one reusable command. A user would list them to find an existing skill for a task rather than rebuilding it from scratch. Returns each skill's name, description, and steps.

Output parameters:

- `recipes` (array)

### `recipe_run` (~133 tokens)

Recipe Run

Executes a recipe end to end: binds params, runs each step's tool in order via the registry, persists the run (see recipe_runs), and returns each step's result plus any markers_path. Recipes with state-changing steps (write/send/delete) PREVIEW first — call again with confirm:true to execute; read-only recipes run immediately. A step that errors stops the run and is reported.

Input parameters:

- `confirm` (boolean): Set true to execute a recipe that has state-changing steps; read-only recipes ignore it.
- `name` (string, required)
- `params` (object): Param overrides (merged over the recipe defaults).

### `recipe_runs` (~128 tokens)

Recipe Runs

Shows the history of past recipe runs and their results (recorded by recipe_run), so you can reuse, compare, or debug an automation. Pass `name` for one recipe's runs, or omit for a compact history across all recipes. Pass `run_id` (with `name`) to get that run in full detail. Newest first.

Input parameters:

- `limit` (integer): Max runs to return (default 20).
- `name` (string): Recipe name; omit for runs across all recipes.
- `run_id` (string): Return this one run in full detail (requires name).

Output parameters:

- `count` (integer)
- `recipe` (string|null): Recipe name when scoped, null for the all-recipes history.
- `runs` (array)

### `recipe_save` (~325 tokens)

Recipe Save

Saves a reusable SKILL — a named recipe (an ordered sequence of LMCP tool calls with parameters) — to this Mac so the user can re-run it anytime with recipe_run. A user would save one to turn a multi-step LMCP workflow they repeat (a morning brief, inbox triage, a weekly report, a data pull) into a single reusable command. Saved skills can be shared with other people via recipe_export. The manifest must have a name and a non-empty steps array. Returns {name}.

Input parameters:

- `manifest` (object, required): The recipe manifest. Shape: {"name": string (required), "description": string, "params": [{"name": string, "type": "string"|"int"|"bool", "default": any}], "steps": [ ... ] (required, non-empty), "ou…

### `record_marker` (~124 tokens)

Record Marker

Drops a named marker into the active recording's timeline. t_ms is elapsed ms since recording start. Provide bounds (global points, top-left) to zoom toward an element, or omit for full-frame. note becomes a caption source. Returns no_active_session if nothing is recording.

Input parameters:

- `bounds` (object): Optional {x,y,w,h} global points to zoom toward.
- `name` (string, required): Marker name, e.g. open_tray, act2_calendar_create.
- `note` (string): Free text → caption source.
- `session_id` (string)

### `rename_reminder_folder` (~103 tokens)

Rename Reminder Folder

Renames an existing Apple Reminders list. Pass the current list name (or list_id from get_reminder_folders) and new_name. Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to apply
- `list_id` (string): List identifier from get_reminder_folders (alternative to name)
- `name` (string): Current list name (or pass list_id)
- `new_name` (string, required): New name for the list

### `reply_email` (~138 tokens)

Reply Email

Use this when the user wants to reply to an email that lives in the Mac's Apple Mail (message ID from list_emails/search_emails). Supports plain text or HTML body. For a Microsoft 365 message ID from m365_list_emails, use m365_reply_email. Pass `account` (from list_emails/search_emails results) to skip scanning other accounts and avoid timeouts on multi-account Macs.

Input parameters:

- `account` (string)
- `body` (string)
- `confirm` (boolean)
- `html_body` (string)
- `message_id` (string, required)
- `reply_all` (boolean)

Output parameters:

- `message_id` (string)
- `replied` (boolean)

### `report_friction` (~171 tokens)

Report Friction

Send an ANONYMOUS, content-free signal when an LMCP tool fails, returns nothing useful, the user seems frustrated, or you could not accomplish what they asked. Helps the LMCP team find and fix the roughest spots. Send ONLY the category + the tool name — NEVER the user's request, message/email content, account names, or any personal data. No confirmation needed: this is anonymous (categories only) and respects the user's opt-out.

Input parameters:

- `attempt_count` (integer): How many times this was attempted (optional).
- `error_category` (string): Category of what went wrong (optional).
- `friction_type` (string, required): What kind of friction you observed.
- `tool_attempted` (string): Name of the LMCP tool involved (e.g. list_emails). Optional.

### `report_problem` (~279 tokens)

Report Problem

Sends a problem report, feature request, or integration request to the LMCP team — for when a user wants to report a bug, ask for a new capability, or request support for an app LMCP doesn't cover yet. Without confirm=true it returns a preview of the anonymous payload that would be sent (version, OS, and permission status only — no personal data); with confirm=true it submits and returns a case_id. type='problem' (default) reports a bug, type='feature' requests a new capability, type='integration' requests an unsupported app.

Input parameters:

- `confirm` (boolean): Must be true to submit the report. Without it, shows a preview.
- `description` (string): Required for type=feature or integration: what the user wants.
- `error_message` (string): For type=problem: verbatim error string from the failed tool.
- `expected` (string): What you or the user expected to happen.
- `report_type` (string): 'problem' (default) | 'feature' | 'integration'
- `symptom` (string): Required for type=problem: what is broken, in your own words.
- `tool_attempted` (string): For type=problem: name of the LMCP tool that failed.
- `user_request` (string): What the user originally asked the AI to do.

### `request_feature` (~144 tokens)

Request Feature

Submits a feature request to the LMCP team — a new capability, a tool that doesn't exist yet, or an app/integration the user wishes LMCP supported. A user would invoke this when they want to ask the team for something LMCP can't do yet. Without confirm=true it returns a preview; with confirm=true it submits. The request includes the machine ID and (if set) the account email so the team can follow up — it is not anonymous.

Input parameters:

- `confirm` (boolean): Must be true to submit. Without it, shows a preview.
- `feature` (string, required): What the user wants LMCP to do — a capability, tool, or integration.

### `run_diagnostics` (~108 tokens)

Run Diagnostics

Runs a fast health check of all LMCP integrations on this machine. Shows what works, what doesn't, and how to fix it. Optionally submits a report to the LMCP team.

Input parameters:

- `focus` (string): Integration to focus on: calendar, mail, contacts, reminders, omnifocus, outlook, notes, finder, onedrive. Leave empty to check all.
- `submit` (boolean): Send the diagnostic report to the LMCP team for analysis (default: false)

Output parameters:

- `integrations` (array)
- `ok_count` (integer): Number of integrations working
- `problem_count` (integer): Number of integrations with errors or missing permissions
- `report` (string): Full formatted text report
- `submitted` (boolean): True when the report was sent to the LMCP team
- `summary` (string): Plain-language summary of overall health
- `warn_count` (integer): Number of integrations with warnings / not running

### `safari_click` (~149 tokens)

Safari Click

Clicks the first element matching a CSS selector in the current Safari tab. Returns the tag name and visible text of the clicked element so you can confirm the right thing was hit. Pass `wait_for_navigation: true` to wait up to 3 seconds for the page to load after the click (useful when clicking links or buttons that trigger navigation).

Input parameters:

- `nth` (integer): Which match to click if there are several (0-based, default 0)
- `selector` (string, required): CSS selector (e.g. 'button.primary', '#save', '[data-testid=login]')
- `wait_for_navigation` (boolean): Wait up to 3s for page load after click (default false)

### `safari_fill_form` (~37 tokens)

Safari Fill Form

Fills multiple form fields in one shot. Pass fields as a JSON object mapping CSS selector to value.

Input parameters:

- `fields` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `safari_go_back` (~29 tokens)

Safari Go Back

Navigates the current Safari tab back to the previous page.

Input parameters:

- `window_index` (integer)

Output parameters:

- `from` (string)
- `to` (string)
- `went_back` (boolean)

### `safari_history` (~116 tokens)

Safari History

Lists or searches the user's Safari browsing history (local SQLite, read-only — no page is opened). Optional `query` matches the URL or page title (case-insensitive substring). Returns url, title, visit_count and last_visit (ISO), newest first. Requires Full Disk Access. For Chrome use chrome_history.

Input parameters:

- `limit` (integer): Max entries to return (default 50, max 200).
- `query` (string): Substring to match against the URL or page title. Omit to list the most recent history.

### `safari_list_tabs` (~28 tokens)

Safari List Tabs

Lists every open tab across all Safari windows with title, URL, and whether it is active.

Output parameters:

- `count` (integer)
- `tabs` (array)

### `safari_navigate` (~50 tokens)

Safari Navigate

Navigates Safari to a URL. Pass new_tab=true to open in a new tab.

Input parameters:

- `new_tab` (boolean)
- `url` (string, required)
- `window_index` (integer)

Output parameters:

- `navigated` (boolean)
- `new_tab` (boolean)
- `url` (string)

### `safari_query_selector_all` (~43 tokens)

Safari Query Selector All

Runs document.querySelectorAll in the current Safari tab and returns a compact summary of each match.

Input parameters:

- `limit` (integer)
- `selector` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `safari_read_tab` (~236 tokens)

Safari Read Tab

Reads the rendered text content of a Safari tab. Identify the tab either by `url_match` (substring match against URL; first hit wins) or by `window_index` + `tab_index` (from safari_list_tabs). Text is capped at `max_bytes` (default 100 KB). Pass `include_html: true` to also get the raw HTML source. Pass `include_links: true` to extract all links with their href and text (useful for following navigation in SPAs like dashboards).

Input parameters:

- `include_html` (boolean): Also return the HTML source (default false)
- `include_links` (boolean): Extract all links with href + visible text (default false). Great for navigating SPAs.
- `max_bytes` (integer): Max bytes of text (and html) to return (default 102400)
- `tab_index` (integer): Tab index from safari_list_tabs (default current tab of that window)
- `url_match` (string): Substring to match against the tab URL. Takes precedence over indices.
- `window_index` (integer): Window index from safari_list_tabs (default 0)

Output parameters:

- `html` (string)
- `html_bytes` (integer)
- `html_truncated` (boolean)
- `link_count` (integer)
- `links` (array)
- `links_error` (string)
- `text` (string)
- `text_bytes` (integer)
- `title` (string)
- `truncated` (boolean)
- `url` (string)

### `safari_search_tabs` (~102 tokens)

Safari Search Tabs

Searches the rendered text of every open Safari tab for a substring. Returns each matching tab with the surrounding snippet. Useful for 'do I have a tab open with X?' questions across dozens of tabs.

Input parameters:

- `context` (integer): Characters of context around each match (default 120)
- `max_tabs` (integer): Max tabs to scan (default 30). Higher = slower.
- `query` (string, required): Substring to search for (case-insensitive)

Output parameters:

- `hits` (array)
- `query` (string)
- `scanned` (integer)

### `safari_setup_check` (~43 tokens)

Safari Setup Check

Reports whether Safari is ready for interactive tools (safari_click, safari_type, safari_evaluate_js). Returns setup instructions if JavaScript from Apple Events is not enabled.

Output parameters:

- `instructions` (string)
- `ready_for_js_tools` (boolean)
- `tabs_open` (integer)

### `safari_type` (~47 tokens)

Safari Type

Sets the value of an input/textarea matching a CSS selector and fires input/change events.

Input parameters:

- `clear` (boolean)
- `selector` (string, required)
- `value` (string, required)

Output parameters:

- `ok` (boolean)
- `result` (object)

### `safari_wait_for` (~152 tokens)

Safari Wait For

Polls the current Safari tab until a CSS `selector` appears, or until `text_match` appears (anywhere on the page, or inside `selector` if you also pass one). Pass at least one of `selector`/`text_match`. Useful after safari_click to wait for the next page or a modal to render.

Input parameters:

- `selector` (string): CSS selector to wait for (optional if text_match is given)
- `text_match` (string): Text to wait for. With `selector`, must appear inside that element; without it, anywhere on the page.
- `timeout_ms` (integer): Max time to wait (default 10000 = 10s, max 30000)

### `save_attachment` (~93 tokens)

Save Attachment

Saves an attachment from an email to disk. Pass account= (and mailbox= if known, both from list_emails/search_emails) so the lookup targets one account instead of scanning all of them.

Input parameters:

- `account` (string)
- `attachment_name` (string, required)
- `confirm` (boolean)
- `destination` (string)
- `mailbox` (string)
- `message_id` (string, required)

Output parameters:

- `attempts` (integer)
- `destination` (string)
- `name` (string)
- `saved` (boolean)

### `screen_record_start` (~132 tokens)

Screen Record Start

Begins a screen recording (ScreenCaptureKit) of a display, window, or region. Single active session in v1 — a second start returns already_recording. Returns a session_id used by record_marker and screen_record_stop. Requires Screen Recording permission; without it returns an explicit permission_required error, never a silent no-op.

Input parameters:

- `fps` (integer): Frames per second (default 60).
- `output_path` (string): Where to write the .mov (default: temp file, returned by stop).
- `show_cursor` (boolean): Default true.
- `target` (object, required): What to capture.

### `screen_record_status` (~39 tokens)

Screen Record Status

Reports whether a recording is active, with the session_id, elapsed_ms, output path, and marker_count.

Input parameters:

- `session_id` (string)

### `screen_record_stop` (~75 tokens)

Screen Record Stop

Stops the active recording, finalizes the .mov, and writes the marker timeline JSON (§6) next to it. Returns the video path, duration, resolution, marker_count and markers_path. Returns no_active_session if nothing is recording.

Input parameters:

- `session_id` (string): Optional; the single active session is used if omitted.

### `screenshot_capture` (~68 tokens)

Screenshot Capture

Captures a single frame of a display, window, or region to a PNG. Requires Screen Recording permission; without it returns an explicit permission_required, never a blank image.

Input parameters:

- `output_path` (string): Where to write the PNG (default: temp file).
- `target` (object, required)

### `search_contacts` (~78 tokens)

Search Contacts

Searches the Mac's Contacts app (Contacts.app, local/iCloud) by name, email, or phone number. For a Microsoft 365 directory use m365_search_contacts or search_m365_directory instead.

Input parameters:

- `limit` (integer): Max results (default 50)
- `query` (string, required): Name, email, or phone to search for

Output parameters:

- `contacts` (array)
- `count` (integer)
- `query` (string)

### `search_emails` (~118 tokens)

Search Emails

Use this when the user wants to find specific emails on this Mac (Apple Mail — any account added to Mail.app). Searches by keyword, sender, or date. For a Microsoft 365 mailbox NOT added to Mail.app, use m365_search_emails. On machines with 3+ accounts, pass account=<name> (from list_email_accounts) to search a specific account and avoid timeouts.

Input parameters:

- `account` (string)
- `limit` (integer)
- `mailbox` (string)
- `query` (string, required)

Output parameters:

- `count` (integer)
- `next_actions` (array)
- `query` (string)
- `results` (array)

### `search_m365_directory` (~82 tokens)

Search Microsoft 365 Directory

Search your organization's Microsoft 365 directory for users by name or email. Returns matching users with their title, department, and contact info.

Input parameters:

- `limit` (integer): Max results (default 10, max 25)
- `query` (string, required): Name or email to search for, e.g. 'Sarah' or 'sarah@contoso.com'

Output parameters:

- `count` (integer)
- `query` (string)
- `users` (array)

### `search_messages` (~162 tokens)

Search Messages

Searches iMessage conversations by content, sender name, or date range.

Input parameters:

- `from_sender` (string): Substring of sender name/handle to filter by (optional)
- `limit` (integer): Max results (default 30)
- `query` (string): Text to search for in message content (optional if from_sender is set)
- `since` (string): ISO8601 date — only return messages on or after this date (optional, e.g. '2026-04-10' or '2026-04-10T00:00:00Z')
- `until` (string): ISO8601 date — only return messages on or before this date (optional). Combine with 'since' to search a date range with no text query.

Output parameters:

- `count` (integer)
- `from_sender` (string)
- `query` (string)
- `results` (array)
- `since` (string)
- `until` (string)

### `search_notes` (~30 tokens)

Search Notes

Searches Apple Notes by title or content.

Input parameters:

- `limit` (integer)
- `query` (string, required)

Output parameters:

- `count` (integer)
- `next_actions` (array)
- `query` (string)
- `results` (array)

### `search_omnifocus_tasks` (~56 tokens)

Search OmniFocus Tasks

Searches OmniFocus tasks by name or note content.

Input parameters:

- `limit` (integer): Max matches to return (default 30).
- `query` (string, required): Text to match against task names and notes (case-insensitive).

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)

### `send_email` (~165 tokens)

Send Email

Use this when the user wants to send an email from an account configured in the Mac's Apple Mail. Composes and sends via Mail.app; supports plain text or HTML body. For sending from a Microsoft 365 account NOT added to Mail.app, use m365_send_email. Pass `from` to send from a specific configured Mail.app account instead of the default sender. Pass `attachments` as a comma-separated list of absolute file paths to attach files.

Input parameters:

- `attachments` (string)
- `bcc` (string)
- `body` (string)
- `cc` (string)
- `confirm` (boolean)
- `from` (string)
- `html_body` (string)
- `subject` (string, required)
- `to` (string, required)

Output parameters:

- `from` (string)
- `sent` (boolean)
- `subject` (string)
- `to` (string)

### `send_message` (~158 tokens)

Send Message

Sends an iMessage via the Mac's Messages.app to a recipient handle (phone number with country code, e.g. +14155551234, or an Apple ID email). This is a write operation: the first call (without confirm) returns a preview; call again with confirm=true to actually send. Direct (1:1) iMessage only — sending into an existing group chat isn't supported yet. Requires Messages.app signed in to iMessage + Automation permission.

Input parameters:

- `confirm` (boolean): Set true to actually send. Without it, returns a preview only.
- `text` (string, required): Message body to send.
- `to` (string, required): Recipient handle: phone number with country code (+14155551234) or Apple ID email.

Output parameters:

- `error` (string)
- `note` (string)
- `preview` (boolean)
- `sent` (boolean)
- `service` (string)
- `text` (string)
- `to` (string)

### `signal_compose_guidance` (~164 tokens)

Signal Compose Guidance

Composes a Signal message and returns step-by-step guidance for the user to send it themselves. This tool does NOT send: Signal Desktop exposes no local send API and LMCP reads its database read-only, so it cannot transmit Signal messages. Call it when the user wants to message someone on Signal — it drafts the text and tells them how to deliver it. First call (confirm=false or omitted) returns a preview; confirm=true returns the send-it-yourself steps. chat_id should come from a previous signal_list_chats call — never fabricate IDs.

Input parameters:

- `chat_id` (string, required): Chat ID from signal_list_chats
- `confirm` (boolean): Set true for send guidance. Default: preview only.
- `text` (string, required): Plain-text message body

### `signal_connect` (~89 tokens)

Signal Connect

Connect Signal to Local MCP. Reports whether Signal Desktop is installed and signed in, and tells you exactly what to do next — install Signal, or open it and link your phone. (Signal links inside its own desktop app, so the QR is shown there, not here.) Once you're signed in, signal_list_chats / signal_read_messages work. If Signal is already connected, it just reports that.

### `signal_list_chats` (~75 tokens)

Signal List Chats

Lists Signal conversations (chats) with last-active timestamps. Reads from the local Signal Desktop database — no network access required. Returns chat IDs, contact names, and type (direct or group). Use the chat_id in subsequent signal_read_messages calls.

Input parameters:

- `limit` (integer): Max chats to return (default 50)

Output parameters:

- `chats` (array): Signal conversations
- `count` (integer): Number of chats returned

### `signal_read_messages` (~84 tokens)

Signal Read Messages

Reads messages from a specific Signal chat. The chat_id must come from a previous signal_list_chats call. Returns messages in chronological order with sender phone numbers and body text. Only messages cached locally by Signal Desktop are available.

Input parameters:

- `chat_id` (string, required): Chat ID from signal_list_chats
- `limit` (integer): Max messages to return (default 50)

Output parameters:

- `count` (integer): Number of messages returned
- `messages` (array): Messages from the chat, chronological

### `signal_search_messages` (~89 tokens)

Signal Search Messages

Full-text search across locally-cached Signal messages. Only messages Signal Desktop has stored on disk are searched — no network access required. Optionally restrict search to a specific chat_id.

Input parameters:

- `chat_id` (string): Optional chat ID to restrict search
- `limit` (integer): Max results to return (default 50)
- `query` (string, required): Search text (case-insensitive substring match)

Output parameters:

- `count` (integer): Number of results returned
- `results` (array): Matching messages

### `slack_list_channels` (~106 tokens)

Slack List Channels

Lists channels in a Slack workspace, including public channels, private channels, and direct messages (DMs). Reads from the local IndexedDB cache — only channels that Slack Desktop has synced to disk are returned. Pass workspace_id from slack_list_workspaces to filter to a specific workspace.

Input parameters:

- `limit` (integer): Max channels to return (default 200)
- `workspace_id` (string): Workspace ID from slack_list_workspaces (optional — omit to list channels across all workspaces)

Output parameters:

- `channels` (array): Channels and DMs synced to the local cache
- `count` (integer): Number of channels returned

### `slack_list_workspaces` (~50 tokens)

Slack List Workspaces

Lists the Slack workspaces (teams) the user has connected in Slack Desktop. Reads from the local IndexedDB cache — no token needed. Only workspaces that have been synced to disk are returned.

Output parameters:

- `count` (integer): Number of workspaces returned
- `workspaces` (array): Connected Slack workspaces synced to the local cache

### `slack_read_channel_messages` (~88 tokens)

Slack Read Channel Messages

Reads recent messages from a Slack channel or DM. Reads from the local IndexedDB cache — only messages that Slack Desktop has synced to disk are available (typically the last few hundred messages for active channels). channel_id must come from slack_list_channels.

Input parameters:

- `channel_id` (string, required): Channel ID from slack_list_channels
- `limit` (integer): Max messages to return (default 50)

Output parameters:

- `count` (integer): Number of messages returned
- `messages` (array): Recent messages from the channel, oldest first

### `slack_search_messages` (~98 tokens)

Slack Search Messages

Searches Slack messages across locally-cached channels using full-text substring matching. Only messages that Slack Desktop has synced to disk are searched — this is not the Slack cloud search API. Optionally restrict search to a specific channel_id.

Input parameters:

- `channel_id` (string): Optional channel ID to restrict search
- `limit` (integer): Max results to return (default 50)
- `query` (string, required): Search text (case-insensitive substring match)

Output parameters:

- `count` (integer): Number of results returned
- `results` (array): Matching messages, most recent first

### `stocks_get_chart` (~121 tokens)

Stocks Get Chart

Gets historical price data for a stock symbol. Range: 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max.

Input parameters:

- `interval` (string): Data interval (default: 1d). Intraday (1m–90m) needs a short range.
- `range` (string): Time range (default: 1mo)
- `symbol` (string, required): Ticker symbol, e.g. AAPL

Output parameters:

- `candles` (array)
- `change_pct` (number)
- `currency` (string)
- `current_price` (number)
- `data_points` (integer)
- `interval` (string)
- `name` (string)
- `range` (string)
- `symbol` (string)

### `stocks_get_quote` (~79 tokens)

Stocks Get Quote

Gets current stock price and market data for one or more symbols (e.g. AAPL, MSFT, BTC-USD). Uses Yahoo Finance — no API key required.

Input parameters:

- `symbols` (string, required): Ticker symbols, comma-separated ('AAPL,MSFT,GOOGL') or a JSON array (['AAPL','MSFT'])

### `stocks_search_symbol` (~53 tokens)

Stocks Search Symbol

Searches for a stock ticker symbol by company name. Returns matching symbols and exchanges.

Input parameters:

- `limit` (integer): Max results (default 10)
- `query` (string, required): Company name or partial ticker to search for

Output parameters:

- `count` (integer)
- `query` (string)
- `results` (array)

### `survey_respond` (~108 tokens)

Survey Respond

Submits the user's answers to the short in-product survey Local MCP just showed them. Call this ONLY when the user has actually answered the survey's questions — pass their answers as an object keyed by each question's `id` (single/scale = one value, multiple = an array of values). Do not invent answers; if the user hasn't answered, don't call this.

Input parameters:

- `answers` (object, required): Answers keyed by question id. Single/scale = a value; multiple = an array.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `survey_skip` (~44 tokens)

Survey Skip

Skips the short in-product survey Local MCP showed the user, for now — use this when the user doesn't want to answer right now. They won't be asked again this session.

Output parameters:

- `message` (string)
- `ok` (boolean)

### `teams_call_history` (~170 tokens)

Teams Call History

Reads Microsoft Teams call & meeting history from the Mac's local Teams cache — no Graph API, no token, no admin consent (the same local store the Teams Calls tab renders). Each call includes direction (incoming/outgoing/missed), participants (names + ids), start / answered / end times, duration, call type (1:1/group/meeting) and a stable call id. Optional since/until (YYYY-MM-DD) narrow the range — e.g. a daily collector pulls the previous day's calls.

Input parameters:

- `limit` (integer): Max calls to return (default 50), newest first
- `since` (string): Only calls on/after this date, YYYY-MM-DD (optional)
- `until` (string): Only calls on/before this date, YYYY-MM-DD (optional)

Output parameters:

- `calls` (array)
- `count` (integer)
- `error` (string)

### `teams_list_channels` (~79 tokens)

Teams List Channels

Lists channels in a Microsoft Teams workspace. Returns channels that are cached in the local Teams client. If the result is empty, the channels have not been loaded into the local cache yet — ask the user to open Microsoft Teams and browse to the team's channels, then try again.

Input parameters:

- `team_id` (string, required): Team ID from teams_list_teams

Output parameters:

- `channels` (array)
- `count` (integer)
- `error` (string)

### `teams_list_chats` (~36 tokens)

Teams List Chats

Lists Microsoft Teams chats (direct messages and group chats).

Input parameters:

- `limit` (integer): Max chats to return (default 50)

Output parameters:

- `chats` (array)
- `count` (integer)
- `error` (string)

### `teams_list_teams` (~21 tokens)

Teams List Teams

Lists all Microsoft Teams workspaces the user belongs to.

Output parameters:

- `count` (integer)
- `error` (string)
- `teams` (array)

### `teams_read_channel_messages` (~55 tokens)

Teams Read Channel Messages

Reads messages from a Microsoft Teams channel.

Input parameters:

- `channel_id` (string, required): Channel ID from teams_list_channels
- `limit` (integer): Max messages (default 50)
- `team_id` (string, required): Team ID

Output parameters:

- `count` (integer)
- `error` (string)
- `messages` (array)

### `teams_read_chat_messages` (~49 tokens)

Teams Read Chat Messages

Reads messages from a Teams chat or direct message thread.

Input parameters:

- `chat_id` (string, required): Chat ID from teams_list_chats
- `limit` (integer): Max messages (default 50)

Output parameters:

- `count` (integer)
- `error` (string)
- `messages` (array)

### `teams_search_messages` (~170 tokens)

Teams Search Messages

Searches your Microsoft Teams chat and direct-message history (all conversations at once) by text, sender name, and/or date range. Use this to find where something was discussed in Teams without knowing which chat. Returns matching messages with the chat they came from. Provide at least one of query, from_sender, since, until.

Input parameters:

- `from_sender` (string): Filter to a sender by display-name substring (optional)
- `limit` (integer): Max results, newest first (default 50, max 200)
- `query` (string): Text to find in message bodies (case-insensitive substring)
- `since` (string): Only messages on/after this date, YYYY-MM-DD (optional)
- `until` (string): Only messages on/before this date, YYYY-MM-DD (optional)

Output parameters:

- `count` (integer)
- `error` (string)
- `messages` (array)

### `teams_send_channel_message` (~122 tokens)

Teams Send Channel Message

Sends a text message to a Microsoft Teams channel via Graph API. Requires connect_m365_account with Chat.ReadWrite / ChannelMessage.Send permissions. team_id and channel_id must come from teams_list_teams / teams_list_channels. First call returns a preview; set confirm=true to send.

Input parameters:

- `channel_id` (string, required): Channel ID from teams_list_channels
- `confirm` (boolean): Set true to send; false returns preview
- `team_id` (string, required): Team ID from teams_list_teams
- `text` (string, required): Plain-text message body

### `teams_send_message` (~191 tokens)

Teams Send Message

Sends a text message to a Microsoft Teams chat or channel.
Requires Microsoft Teams to be running and signed in (token is read fresh from Teams' local cookies on each call). The chat_id MUST come from a previous teams_list_chats call — never fabricate ids. This is a write operation: the first call returns a preview, the second call (with confirm=true) actually sends.

Input parameters:

- `chat_id` (string, required): Thread id from teams_list_chats (e.g. '19:<uuid>_<uuid>@unq.gbl.spaces' for 1:1, '19:<uuid>@thread.tacv2' for group)
- `confirm` (boolean): Must be true to actually send. Without it, returns a preview without making any network call.
- `text` (string, required): Plain-text message body. Max 28000 chars. No formatting / mentions / attachments in v1.

### `todo_complete_task` (~82 tokens)

To Do Complete Task

Marks a Microsoft To Do task as complete (via Reminders sync).

Input parameters:

- `confirm` (boolean): Must be true to complete
- `list` (string): List name to narrow search by title (optional)
- `task_id` (string): Task ID from todo_list_tasks
- `title` (string): Task title (partial match, alternative to task_id)

### `todo_create_task` (~125 tokens)

To Do Create Task

Creates a task in Microsoft To Do (via Reminders sync). Task appears in To Do automatically once synced.

Input parameters:

- `confirm` (boolean): Must be true to create
- `due_date` (string): Due date (YYYY-MM-DD, optional)
- `list` (string): List name (from todo_get_folders). Defaults to first available list.
- `notes` (string): Task notes (optional)
- `priority` (integer): Priority: 1=high, 5=medium, 9=low (optional)
- `title` (string, required): Task title

### `todo_get_folders` (~38 tokens)

To Do Get Folders

Lists Microsoft To Do task lists. Requires Microsoft account in Reminders sync (System Settings → Internet Accounts → Microsoft Exchange → enable Reminders).

Output parameters:

- `count` (integer)
- `lists` (array)
- `note` (string)

### `todo_list_tasks` (~82 tokens)

To Do List Tasks

Lists tasks from a Microsoft To Do list (or any Reminders list). Syncs via macOS Reminders.

Input parameters:

- `include_completed` (boolean): Include completed tasks (default false)
- `limit` (integer): Max tasks to return (default 50)
- `list` (string): List name (from todo_get_folders). Leave empty to show all.

Output parameters:

- `count` (integer)
- `tasks` (array)

### `todoist_complete_task` (~44 tokens)

Todoist Complete Task

Mark a Todoist task complete (closes it). Pass the task_id from todoist_list_tasks.

Input parameters:

- `task_id` (string, required): The task id to complete

Output parameters:

- `message` (string)
- `ok` (boolean)

### `todoist_create_task` (~136 tokens)

Todoist Create Task

Create a Todoist task. Optionally set a project, a natural-language due date (due_string, e.g. 'tomorrow 5pm', 'every monday'), and priority (1=normal … 4=urgent).

Input parameters:

- `content` (string, required): The task text
- `due_string` (string): Natural-language due date, e.g. 'tomorrow 5pm', 'next monday'
- `priority` (integer): 1 (normal) to 4 (urgent). Todoist UI p1 = 4.
- `project_id` (string): Project to add it to (default: Inbox)

Output parameters:

- `content` (string)
- `due` (string)
- `id` (string)
- `priority` (integer)
- `project_id` (string)
- `url` (string)

### `todoist_list_projects` (~36 tokens)

Todoist List Projects

List your Todoist projects (id + name). Use a project's id to scope todoist_list_tasks or todoist_create_task.

Output parameters:

- `count` (integer)
- `projects` (array)

### `todoist_list_tasks` (~113 tokens)

Todoist List Tasks

List active (incomplete) Todoist tasks. Optionally scope to a project_id, or pass a Todoist filter (e.g. 'today', 'overdue', '#Work & p1').

Input parameters:

- `filter` (string): A Todoist filter query, e.g. 'today', 'overdue', 'p1'
- `limit` (integer): Max results (default 50, max 200)
- `project_id` (string): Only tasks in this project (from todoist_list_projects)

Output parameters:

- `count` (integer)
- `tasks` (array)

### `ui_click` (~121 tokens)

Ui Click

Clicks an element (by element_ref, at its center) or a screen coordinate (by coords). button left|right, count 2 = double-click. Returns {clicked, at:{x,y}}. Requires Accessibility permission.

Input parameters:

- `button` (string): Default left.
- `by` (string): Default element if element_ref given, else coords.
- `coords` (object): {x,y} in global screen points.
- `count` (integer): 1 (default) or 2 for double-click.
- `element_ref` (string)

### `ui_find_element` (~196 tokens)

Ui Find Element

GUI automation — control a native app's interface. Finds an element (button, field, menu…) in an app's accessibility tree by role and/or label. Scope with app_bundle_id or window_id. Returns an opaque element_ref (usable by ui_click / ui_get_element this session) plus role, label, bounds, enabled, focused. found=false when the app is reachable but no element matches; app_not_found is an explicit error. Requires Accessibility permission.

Input parameters:

- `app_bundle_id` (string): Scope the search to this app.
- `index` (integer): Which match to return if several (default 0).
- `label` (string): AX title/description to match.
- `match` (string): Default contains.
- `role` (string): AX role, e.g. AXButton, AXMenuItem, AXTextField.
- `window_id` (string): Alternatively scope by a window_id from list_windows.

### `ui_get_element` (~60 tokens)

Ui Get Element

Re-resolves a previously returned element_ref (its bounds/state may have changed). Returns role, label, bounds, enabled, focused, value. stale_element if the handle is unknown or the element no longer exists.

Input parameters:

- `element_ref` (string, required)

### `ui_keystroke` (~89 tokens)

Ui Keystroke

Sends a key combination, e.g. "cmd+shift+5", "return", "cmd+,", "escape". Modifiers: cmd, shift, alt/option, ctrl, fn. The last token is the key. unknown_key if the key isn't recognized. Requires Accessibility permission.

Input parameters:

- `keys` (string, required): e.g. cmd+shift+5, return, cmd+,

### `ui_menu_bar_click` (~121 tokens)

Ui Menu Bar Click

Clicks a status-bar (menu bar extra / NSStatusItem) item and optionally follows a nested menu path. Best-effort via the app's AX extras menu bar; apps that render fully custom (non-AX) menus may not be reachable (fall back to ui_click at known coords). Requires Accessibility permission.

Input parameters:

- `app_bundle_id` (string): Owner of the status item.
- `label` (string): Status item / menu item title.
- `path` (array): Nested menu path, e.g. ["App","Settings…"].

### `ui_read_tree` (~223 tokens)

Ui Read Tree

Returns a COMPACT accessibility tree of a running native app's labeled + interactive elements (buttons, links, text fields, checkboxes, menus…) — the native equivalent of web_read's a11y mode. Use it to DISCOVER what to act on in an unfamiliar app when you don't already know an element's role/label (ui_find_element needs one up front). Each interactive node carries a `ref` you can pass straight to ui_click. Pass app_bundle_id of a running app (e.g. com.apple.finder — see list_windows); the tree is pruned to signal-bearing nodes and bounded by max_depth (default 12) and a node budget, so very large windows return partial.

Input parameters:

- `app_bundle_id` (string): Bundle id of a RUNNING app (e.g. com.apple.finder)
- `max_depth` (integer): Max tree depth to descend (default 12, max 20)
- `window_id` (string): Alternative to app_bundle_id: a window id from list_windows (targets that window's app)

### `ui_type` (~63 tokens)

Ui Type

Types text into the focused control (or focuses element_ref first, then types). Sends real key events so validation/handlers fire. Requires Accessibility permission.

Input parameters:

- `element_ref` (string): Optional; focus this element first.
- `text` (string, required): The text to type.

### `ui_wait_for_element` (~139 tokens)

Ui Wait For Element

Deterministic synchronization — replaces all sleeps. Polls for an element until it reaches state (present|enabled|focused|absent) or times out. A timeout is an EXPLICIT error, never a false success. Returns {satisfied, waited_ms, element_ref?, bounds?}.

Input parameters:

- `app_bundle_id` (string)
- `label` (string)
- `match` (string)
- `poll_ms` (integer): Default 150.
- `role` (string)
- `state` (string): Default present.
- `timeout_ms` (integer): Default 5000.
- `window_id` (string)

### `update_calendar_event` (~213 tokens)

Update Calendar Event

Updates an existing event in the Mac's Calendar app (Calendar.app) by ID. Pass only the fields you want to change — unspecified fields are left as-is. Get the event_id from list_calendar_events. For Microsoft 365 use the m365 calendar tools instead.

Input parameters:

- `confirm` (boolean): Must be true to apply changes
- `end_date` (string): New end datetime ISO 8601 (optional). Same timezone rules as start_date.
- `event_id` (string, required): Event identifier from list_calendar_events
- `location` (string): New location — pass empty string to clear (optional)
- `notes` (string): New notes — pass empty string to clear (optional)
- `span` (string): For recurring events: 'this' (default) or 'future'
- `start_date` (string): New start datetime ISO 8601 (optional). No timezone = Mac's local time; append Z/offset to pin the zone.
- `title` (string): New title (optional)

Output parameters:

- `all_day` (boolean)
- `attendees` (array)
- `attendees_total` (integer)
- `calendar` (string)
- `calendar_id` (string)
- `end` (string)
- `id` (string)
- `location` (string)
- `notes` (string)
- `start` (string)
- `title` (string)
- `updated` (boolean)

### `update_note` (~87 tokens)

Update Note

Updates an existing note in Apple Notes. Change the title and/or body (the body accepts Markdown, converted to Apple Notes' native formatting). Find note_id with list_notes or search_notes. Requires confirm=true.

Input parameters:

- `body` (string)
- `confirm` (boolean)
- `note_id` (string)
- `note_name` (string)
- `title` (string)

Output parameters:

- `id` (string)
- `name` (string)
- `updated` (boolean)

### `update_reminder` (~168 tokens)

Update Reminder

Updates an existing reminder in Reminders.app. Change the title, due date, notes, priority, or move it to another list (list_name). Get reminder_id from list_reminders. Requires confirm=true.

Input parameters:

- `confirm` (boolean): Must be true to apply changes
- `due_date` (string): New ISO 8601 due date. Pass empty string to clear (optional)
- `list_name` (string): Move the reminder to this list (a name from get_reminder_folders) (optional)
- `notes` (string): New notes text (optional)
- `priority` (string): Priority: none | low | medium | high (optional)
- `reminder_id` (string, required): Reminder identifier from list_reminders
- `title` (string): New title (optional)

### `video_blur_region` (~140 tokens)

Video Blur Region

Pixelates/blurs one or more rectangles over the video — the tool for redacting PII (an email pane, a name) before publishing a screen recording. Rects are in source pixels, top-left origin: [{x,y,w,h, start_ms?, end_ms?}] — omit the times to cover the whole clip. Great with a marker timeline's `bounds`. Returns the output path.

Input parameters:

- `input` (string, required)
- `output` (string): Default: <input>_blurred.mov
- `regions` (array, required): [{x,y,w,h, start_ms?, end_ms?}] in source pixels (top-left).

### `video_concat` (~81 tokens)

Video Concat

Stitches multiple videos end-to-end, in order, into one NEW file (e.g. assemble separate acts). All inputs should share a resolution for a clean result. Returns the output path + duration.

Input parameters:

- `inputs` (array, required): Ordered list of video file paths.
- `output` (string): Output path (default: <first-input>_joined.mov).

### `video_export_gif` (~163 tokens)

Video Export Gif

Exports a video (or a [start_ms,end_ms] slice of it) to an optimized looping GIF — for README/social. fps (default 12) and width (default 640, height auto) control size. Returns the output path, frame count, and size.

Input parameters:

- `end_ms` (integer): Slice end (default: end of video).
- `fps` (integer): Frames per second in the GIF (default 12).
- `input` (string, required): Path to the source video.
- `output` (string): Output path (default: <input>.gif).
- `start_ms` (integer): Slice start (default 0).
- `width` (integer): Output width in px, height scales to keep aspect (default 640).

### `video_reframe` (~184 tokens)

Video Reframe

Crops a video to a target aspect ratio (e.g. "9:16" vertical, "1:1" square, "4:5") around a focus point — for social clips. Takes the LARGEST crop of that aspect that fits, centered on `focus` (x,y in source pixels, top-left origin; default = center) and clamped to the frame. Audio passes through. Returns the output path + new dimensions.

Input parameters:

- `aspect` (string, required): Target aspect "W:H", e.g. 9:16, 1:1, 4:5, 16:9.
- `focus` (object): {x,y} center of interest in source pixels (top-left). Default: frame center.
- `input` (string, required)
- `output` (string): Default: <input>_<aspect>.mov

### `video_trim` (~130 tokens)

Video Trim

Trims a video to one or more time ranges (milliseconds), concatenated in order into a NEW file — e.g. keep [{start_ms:0,end_ms:6000},{start_ms:126000,end_ms:223000}] to drop a dead segment. Audio is carried along. Returns the output path + duration. Never overwrites the input in place.

Input parameters:

- `input` (string, required): Path to the source video.
- `output` (string): Output path (default: <input>_trimmed.mov).
- `ranges` (array, required): Ordered [{start_ms, end_ms}] to keep.

### `web_click` (~112 tokens)

Web Click

Clicks an element on the current page. `target` is a CSS selector or visible text (resolved fresh each call). Clicks that SUBMIT a form preview first — call again with confirm:true to execute; plain links/buttons click directly. Returns the resulting URL/title.

Input parameters:

- `confirm` (boolean): Required (true) to perform a click that submits a form.
- `session` (string): Session name (default 'default').
- `target` (string, required): CSS selector or visible text of the element to click.

### `web_extract` (~86 tokens)

Web Extract

Scrapes structured data from the current page. Pass `selectors` = an object mapping field names to CSS selectors (e.g. {"title":"h1","price":".price"}); returns each field's first-match text/href, null when absent.

Input parameters:

- `selectors` (object, required): Field name → CSS selector map.
- `session` (string): Session name (default 'default').

Output parameters:

- `data` (object): One key per requested field; first-match text/href, null when absent.
- `title` (string)
- `url` (string)

### `web_find` (~104 tokens)

Web Find

Finds elements on the current page of a web session so you can decide what to click or type into. `query` is a CSS selector OR visible text to match. Returns up to 30 matches with tag/text/name/type/href — never a silent empty.

Input parameters:

- `query` (string, required): A CSS selector (e.g. 'input[name=q]') or visible text (e.g. 'Sign in').
- `session` (string): Session name (default 'default').

Output parameters:

- `count` (integer): Number of matching elements (max 30 returned).
- `matches` (array): Matched elements with tag/text/name/type/href.
- `query` (string)

### `web_login` (~144 tokens)

Web Login

Opens a real browser window on the Mac for the user to sign into a website themselves (you never handle their password). After they log in, the session is saved on this Mac and reused by web_navigate/web_read/web_screenshot — they won't need to log in again. Use a stable `session` name per site (e.g. 'linkedin'). NOTE: automating sites like Instagram/LinkedIn may violate their terms — the user accepts that risk.

Input parameters:

- `session` (string): A stable name for this login profile, e.g. 'linkedin'.
- `url` (string, required): The site's login URL to open, e.g. https://www.linkedin.com/login

### `web_navigate` (~92 tokens)

Web Navigate

Navigates a web session to a URL (using its saved login if any) and returns the resulting URL + page title. Opens the session if it doesn't exist. Read the page with web_read.

Input parameters:

- `session` (string): Session name (default 'default').
- `timeout_seconds` (integer): Max seconds to wait for load (default 25).
- `url` (string, required): URL to open (https).

Output parameters:

- `loaded` (boolean)
- `ok` (boolean): True when the page finished loading.
- `title` (string): The resulting page title.
- `url` (string): The URL after navigation (redirects followed).

### `web_read` (~106 tokens)

Web Read

Reads the current page of a web session so you can reason over it. mode='text' (visible text, default), 'a11y' (compact accessible tree of links/buttons/fields — best for deciding what to click), or 'html' (raw DOM). Returns an explicit no_session error if the session isn't open — never a silent empty.

Input parameters:

- `mode` (string): What to return (default text).
- `session` (string): Session name (default 'default').

Output parameters:

- `content` (string): The page content in the requested mode.
- `mode` (string): The mode that was read (text/a11y/html).
- `title` (string)
- `url` (string)

### `web_screenshot` (~69 tokens)

Web Screenshot

Captures a PNG screenshot of the current page of a web session (returned inline so web AIs can see it). Useful to ground what the page looks like before acting.

Input parameters:

- `session` (string): Session name (a named login profile, e.g. 'linkedin'). Defaults to 'default'.

Output parameters:

- `bytes` (integer): PNG size in bytes (the image itself is an inline content block).
- `url` (string): URL of the page that was captured.

### `web_session_close` (~69 tokens)

Web Session Close

Closes a web-automation session's window and frees it. The saved login stays on disk, so web_login/web_navigate can reopen it later without signing in again.

Input parameters:

- `session` (string): Session name (a named login profile, e.g. 'linkedin'). Defaults to 'default'.

### `web_session_list` (~98 tokens)

Web Session List

Lists your web-automation login profiles: every SAVED login (persisted on disk, so web_login/web_navigate can reopen it without signing in again) plus which are currently OPEN. Each entry has `saved` (a persisted profile exists) and `open` (its window is live now, with url + title). Use it to check whether a login a recipe needs already exists before running it, instead of opening it and failing.

Output parameters:

- `sessions` (array)

### `web_show` (~131 tokens)

Web Show

Brings a web session's browser window to the FRONT so the USER can take over directly — solve a CAPTCHA, complete 2FA, or make a choice the AI shouldn't. Local MCP never solves CAPTCHAs itself; this hands control to the user. Pair with web_screenshot first to show them what's on the page. After they finish, tell the agent to continue — the session keeps its state.

Input parameters:

- `reason` (string): Short reason shown to the user, e.g. 'a CAPTCHA appeared' or 'confirm which account'.
- `session` (string): Session name (default 'default').

### `web_type` (~99 tokens)

Web Type

Types text into a form field (input/textarea) on the current page. `target` is a CSS selector or the field's visible label/placeholder. Does NOT submit — use web_click on the submit button afterwards (that step is gated).

Input parameters:

- `session` (string): Session name (default 'default').
- `target` (string, required): CSS selector or visible label/placeholder of the field.
- `text` (string, required): The text to type.

### `web_wait_for` (~123 tokens)

Web Wait For

Waits (polls, not a fixed sleep) until a JavaScript condition is truthy on the page, or times out. Use for SPA pages that hydrate after load, e.g. condition "document.querySelector('input[name=password]')". Returns met:true/false.

Input parameters:

- `condition` (string, required): JS expression evaluated on the page; waits until it's truthy. e.g. document.querySelector('.feed')
- `session` (string): Session name (default 'default').
- `timeout_seconds` (integer): Max seconds to wait (default 15).

Output parameters:

- `message` (string): Present when the condition was not met.
- `met` (boolean): True if the condition became truthy before the timeout.
- `ok` (boolean)

### `window_focus` (~49 tokens)

Window Focus

Brings a window (by window_id from list_windows) to the front and activates its app. window_not_found if it can't be resolved. Requires Accessibility permission.

Input parameters:

- `window_id` (string, required)

### `window_set_frame` (~75 tokens)

Window Set Frame

Pins a window (by window_id) to fixed bounds {x,y,w,h} in global points, so every take is framed identically across runs. Returns the actual post-constraint bounds. Requires Accessibility permission.

Input parameters:

- `bounds` (object, required): {x,y,w,h} global points.
- `window_id` (string, required)

### `word_append` (~138 tokens)

Word Append

Appends text to the end of an existing Word (.docx) document at `path`, preserving the document's existing content and formatting. Requires confirm=true — called without it, returns a preview instead of modifying the file. Same file-access rules as word_create (Desktop/Documents/Downloads may need a Files-and-Folders grant). Returns {appended, chars_appended, path}. To create a new document use word_create; to read one use word_read.

Input parameters:

- `confirm` (boolean): Must be true to modify
- `content` (string, required): Text to append
- `path` (string, required): Path to the existing .docx file

### `word_create` (~185 tokens)

Word Create

Creates a new Word (.docx) document at `path` with the given text content (and an optional title rendered as the heading). Requires confirm=true — called without it, returns a preview of what will be written instead of creating the file. The path must be somewhere Local MCP can write; Desktop/Documents/Downloads may need a one-time Files-and-Folders grant (System Settings → Privacy & Security → Files and Folders). Returns {created, path}. For a OneDrive or Google Drive path use onedrive_write_file / gdrive_write_file; to append to an existing doc use word_append, to read one word_read.

Input parameters:

- `confirm` (boolean): Must be true to create
- `content` (string, required): Document text content
- `path` (string, required): Output path for the .docx file
- `title` (string): Document title (optional)

Output parameters:

- `created` (boolean): True when the document was created
- `path` (string): Path of the created .docx file

### `word_read` (~35 tokens)

Word Read

Reads text content from a Word document (.docx file).

Input parameters:

- `path` (string, required): Absolute path to the .docx file

Output parameters:

- `chars` (integer): Number of characters in the extracted text
- `text` (string): Extracted text content

### `zoom_list_recordings` (~90 tokens)

Zoom List Recordings

Lists Zoom meeting recordings saved locally on this Mac (~/Documents/Zoom), newest first: meeting name, date, and which artifacts exist (transcript, captions, saved chat, audio, video). Local recordings only — no Zoom API, no admin approval. Use zoom_read_transcript to read the text of a meeting.

Input parameters:

- `limit` (integer): Max recordings to return (default 20)

Output parameters:

- `count` (integer)
- `recordings` (array)
- `total` (integer)

### `zoom_read_transcript` (~130 tokens)

Zoom Read Transcript

Reads the text artifacts of a local Zoom recording: the transcript/captions (.vtt or closed_caption.txt, cleaned to readable 'Speaker: text' lines) and the saved in-meeting chat. Pass the recording name or path from zoom_list_recordings. Perfect for 'summarize my last meeting' or 'what did we agree on in the kickoff call'.

Input parameters:

- `include` (string): 'all' (default), 'transcript' or 'chat'
- `recording` (string, required): Recording folder name (or full path) from zoom_list_recordings. Partial name match works.

Output parameters:

- `chat` (string|null)
- `chat_source` (string)
- `note` (string)
- `path` (string)
- `recording` (string)
- `transcript` (string|null)
- `transcript_source` (string)

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp#diagnostics

## Score history

- 2026-08-03: 64
- 2026-08-02: 63
- 2026-08-01: 63
- 2026-07-31: 62
- 2026-07-30: 66
- 2026-07-29: 60
- 2026-07-28: 59
- 2026-07-27: 63
- 2026-07-26: 62

## Links

- Remote endpoint: https://local-mcp.com/mcp
- Repository: https://github.com/lanchuske/local-mcp-releases
- Changelog RSS feed: https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp/changelog.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp/changelog.json
- HTML version of this page: https://verifymcp.io/servers/com-local-mcp-local-mcp/local-mcp
