fm-mcp

MCP server · macOS 27 · v0.1.2, public beta

Hand small jobs to the model on your Mac.

fm-mcp lets Claude Code and Codex use the small model built into macOS 27 for the gist of a log, fields from an invoice, or the text in a screenshot. Free, private and offline.

Terminal
$ brew install yrangana/tap/fm-mcp
$ fm-mcp install    # sets up Claude Code and Codex
$ fm-mcp doctor     # checks everything it needs
claude · what went wrong in checkout.log?
A terminal recording of Claude Code asked what went wrong in checkout.log. It loads the fm-delegate skill, calls fm-mcp, checks the summary against the log, and answers with three problems: payment timeouts leaving six orders in PENDING, a full database connection pool, and stock for SKU 88-1043 going negative.
Real time. Claude Code hands the log to the on-device model, checks the draft, and fixes three details it got wrong.
8Ktokens

context, for input and answer together

0.5s

median classify call, over 404 texts

1.4s

to pull 4 fields from an invoice

$0

per call, and it works offline

01

The model is small, so the agent has to know what not to send it.

Each tool description says what the model gets wrong, with numbers, and fm-mcp install gives each agent a short guide on when to delegate.

brew

One binary

Rust, installed with Homebrew. No Python, no cloning. It starts fm serve when needed and stops it after.

fm-mcp install · doctor

Sets itself up

install writes the Claude Code and Codex config, with backups; --uninstall removes it. doctor checks everything.

skill · AGENTS.md

Says what it can't do

Four narrow tools and no open-ended "ask". Each description states its limits.

02

Three real calls

What the agent sent and what came back, from fm-mcp 0.1.1 on 12 October 2026.

Summarise a log

summarise · 5.3 s

A 66-line checkout log (the file). All three problems in 3 of 3 runs, but every run dropped a warning about an unset webhook secret. Check what matters.

summarise
→ {"path": "docs/demo/checkout.log", "focus": "what went wrong"}

The payment service timed out multiple times after 10000ms for orders
51820, 51821, 51822, 51823, 51824, and 51825, with the retry budget
exhausted for 6 orders left in PENDING. The database connection pool
exhausted (max 20, waiting 31) for requests to /api/checkout, occurring
multiple times. The inventory system reported that SKU 88-1043 stock
went negative (-2) after concurrent checkout.

Pull fields out of an invoice

extract · 1.4 s

The model copies $1,280.00 as text and fm-mcp converts it. Asked to reformat numbers itself, the model got them wrong.

extract
→ text:   "Invoice INV-2041 from Northwind Traders, dated 3 March 2026.
           Total due: $1,280.00 by 2 April 2026."
  schema: invoice_number, supplier, total (number), due_date

{"invoice_number": "INV-2041", "supplier": "Northwind Traders",
 "total": 1280.0, "due_date": "2 April 2026"}

Sort a message into your labels

classify · 0.6 s

The answer is always one of your labels. The fourth tool, ocr, reads an image in about 2 s.

classify
→ text:   "I was charged twice for my subscription this month."
  labels: billing, bug, feature request, account access

{"label": "billing"}
03

Four narrow tools

summarise, extract and classify take text or a file path. Full reference: docs/TOOLS.md.

summarise

The gist of a log, document or transcript: a paragraph, or up to 7 bullets.

TakesAbout 30,000 words of prose, or 1,000 lines of a dense log. Long input is split, which is slower and can drop details.

extract

Named fields from one text as JSON, from your JSON Schema. Best with a flat schema.

TakesAbout 3,500 words.

classify

Sorts one text into your labels: one, or several with multi.

TakesAbout 3,500 words.

ocr

Text from a PNG, JPEG, HEIC, TIFF, GIF or BMP image. Not PDF.

TakesOne image.

Good to hand off

  • The gist of a long log or transcript
  • Names, dates, amounts and IDs from a short document
  • Sorting tickets, emails or log lines
  • Text in a screenshot or a receipt photo

Better kept by the agent

  • Code, maths or reasoning
  • Facts the text doesn't contain
  • Anything costly to get wrong that can't be checked
  • A page the agent has already read: nothing to save
04

Where it falls short

The tool descriptions say the same. The notes show how each was measured.

  1. Small context. About 8,000 tokens for input and answer together, and logs use more than twice as many tokens per character as prose.
  2. It makes mistakes. Summaries can drop details. extract can fill a missing field from nearby text: a missing PO number took the supplier's name in 10 of 25 runs. Ask only for fields the text is likely to have.
  3. One request at a time. All sessions share the model, so calls queue. A short extract takes 1–2 s; a long summarise, a minute or more.
  4. Refusals. The model sometimes refuses harmless text. fm-mcp says so, and the agent does the job itself.
  5. Mac only. Apple Silicon and macOS 27 with Apple Intelligence on. No Intel, Windows or Linux.
05

Set up in about 20 seconds

Apple Silicon, macOS 27, Apple Intelligence on, and the fm licence agreed once. fm-mcp doctor checks all of it.

brew install · fm-mcp install · fm-mcp doctor
A terminal recording: brew install yrangana/tap/fm-mcp, then fm-mcp install updating the Claude Code and Codex config, then fm-mcp doctor with every check passing.
A fresh install, in real time: about 19 seconds.

What doctor checks

  • Apple Silicon and macOS 27
  • The fm tool and its licence
  • The on-device model is available
  • A test call to fm serve
  • Claude Code and Codex config, skill and guidance
  • The configured binary is this one
Show the full output on my Mac
✓ Apple Silicon: arm64
✓ macOS 27 or later: 27.0.1
✓ `fm` tool: /usr/bin/fm
✓ `fm` licence: Agreed to license FM1 version 1.0
✓ On-device model: System model available
✓ Socket path: 70 bytes, under the 104-byte limit
✓ Model test: `fm serve` started and answered in 2.0 s
✓ Claude Code config: runs /opt/homebrew/bin/fm-mcp
✓ Codex config: runs /opt/homebrew/bin/fm-mcp
✓ Claude Code skill: ~/.claude/skills/fm-delegate/SKILL.md
✓ Codex guidance: in ~/.codex/AGENTS.md
✓ Configured binary: matches /opt/homebrew/bin/fm-mcp

All checks passed.
06

Other servers for the same model

From each project's README on 12 October 2026. Where a README doesn't say, the cell says so.

Scroll the table sideways →

fm-mcp yihan2099/apple-fm-mcp sophiacave/apple-fm-mcp phimage/mcp-foundation-models
Written inRustPythonPythonSwift
Installbrew install, a shell installer, or cargo installgit clone, uv sync, then Apple's SDK from gitpip installgit clone, then swift build
Needs on your MacNothing elsePython 3.10+Python 3.10+Xcode 26 and Swift 6.2 to build
macOS27 (uses the fm tool)26 or later26 or later26 or later
Agent setupfm-mcp install writes Claude Code and Codex configBy hand (claude mcp add, or JSON)By hand (JSON)By hand (JSON)
Tells the agent when to delegateA Claude Code skill and an AGENTS.md section, installed for youNot in the READMEA "when to use" table in the READMENot in the README
Setup checkfm-mcp doctorA model-availability toolA status toolNot in the README
Input longer than the contextSplit and combined (summarise)Not in the READMENot in the READMENot in the README
Images (OCR)YesNo (text only)Not in the READMENot in the README
Open-ended text generationNo, on purposeYes, plus multi-turn chatYesYes

If something here is out of date, open an issue and I'll fix it.

07 · Privacy

Nothing leaves the Mac

fm-mcp talks to fm serve over a local socket and sends nothing over the network. It reads a file only when the agent passes its path, and writes none. Details in SECURITY.md.

08 · Try it

On your Mac

Terminal
$ brew install yrangana/tap/fm-mcp
$ fm-mcp install