DoesItOmarchy
Search Ctrl K

API Reference

Test tools send their diagnostic reports straight in, and everything on DoesItOmarchy is open data: read the catalog and the test results as JSON.

Base URLhttps://doesitomarchy.com/api/v1
FormatJSON. Every endpoint is listed at GET /api/v1.
KeysNone to read. Submitting reports needs a source key.
CORSAny website may read.
CachingReads may be cached for a minute.
LicenceCatalog data is CC BY-SA 4.0: credit DoesItOmarchy and share alike.
GET /api/v1

curl

curl https://doesitomarchy.com/api/v1

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1");
const index = await res.json();

Python

import requests

index = requests.get("https://doesitomarchy.com/api/v1").json()
Response: 200 OK
{
  "name": "DoesItOmarchy API",
  "docs": "https://doesitomarchy.com/api",
  "schema": "doesitomarchy/report/v1",
  "endpoints": {
    "capabilities": "/api/v1/capabilities",
    "config": "/api/v1/configs/{id}",
    "mac": "/api/v1/macs/{identifier}",
    "macs": "/api/v1/macs",
    "match": "POST /api/v1/match",
    "openapi": "/api/v1/openapi.json",
    "report": "/api/v1/reports/{code}",
    "schema": "/api/v1/schema",
    "submit": "POST /api/v1/reports"
  },
  "mcp": "https://doesitomarchy.com/mcp"
}

Building with AI

AI assistants and agents can build on this API too. Give them these, so they don't have to guess:

  • /llms.txt: this API in plain Markdown, written for language models.
  • /api/v1/openapi.json: an OpenAPI 3.1 document with every endpoint, parameter and field. Agents, code generators and OpenAPI-to-MCP tools can import it.
  • /mcp: an MCP server, so an assistant can look Macs up itself.
  • /api/v1/schema: the report format as JSON Schema, to check a report before sending it.

Three things generated code often gets wrong:

  • Verdicts belong to configurations, not to Macs. untested means no report yet, not a failure.
  • Identifiers contain a comma: MacBookPro8,2. A hyphen works too.
  • The catalog changes rarely. Fetch /api/v1/macs once and cache it, rather than requesting every Mac.

Start your assistant with the prompt beside this, then describe what you want.

Prompt for your AI assistant
I'm building a tool on the DoesItOmarchy API, which says how well
Omarchy (Arch Linux + Hyprland) runs on each Intel Mac.

Read these first:
https://doesitomarchy.com/llms.txt
https://doesitomarchy.com/api/v1/openapi.json

Reads need no key. Use real identifiers such as MacBookPro8,2.
Fetch /api/v1/macs once and cache it; don't request every Mac.

What I want to build: 

Connect an AI assistant (MCP)

POST https://doesitomarchy.com/mcp

Cheat sheet (PDF, 1 page)

MCP (Model Context Protocol) is a standard way to give an AI assistant tools. Add this server to Claude, ChatGPT, VS Code or any MCP client, and your assistant looks up Macs and their test results itself, instead of guessing.

Tools

search_macsFind Macs by name, year, identifier or the search fields, with their verdicts.
get_macOne Mac's configurations: the verdict, what fails or only partly works (with the evidence and any fix in progress), and known limitations.
identify_macThe Mac and its exact configuration from a command's output or hardware IDs, like Identify my Mac.
what_needs_testingWhat's still untested or due a re-test on a Mac, and how to help.

Connect

  • claude.ai and Claude Desktop: add a custom connector (Settings → Connectors) with the URL above.
  • ChatGPT: turn on developer mode (Settings → Apps & Connectors → Advanced), then create a connector with the URL above and no authentication.
  • Claude Code, VS Code and Cursor: use the samples beside this.

Good to know

  • No key and no sign-in. It's read-only: it can't submit reports or change anything.
  • Answers are short text written for the assistant, with links to the site. For full data, use the endpoints below.
  • It speaks MCP 2026-07-28 and the earlier versions back to 2025-03-26, without sessions. Each IP address may make 300 requests a minute.
  • We record which tool was called, not what was asked (privacy).
Claude Code
claude mcp add --transport http doesitomarchy https://doesitomarchy.com/mcp
VS Code: .vscode/mcp.json
{
  "servers": {
    "doesitomarchy": {
      "type": "http",
      "url": "https://doesitomarchy.com/mcp"
    }
  }
}
Cursor: ~/.cursor/mcp.json
{
  "mcpServers": {
    "doesitomarchy": {
      "url": "https://doesitomarchy.com/mcp"
    }
  }
}
POST /mcp: try a tool
curl -X POST https://doesitomarchy.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: search_macs" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": {
      "name": "search_macs",
      "arguments": {"query": "macbook pro 2015", "limit": 3},
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'
The answer's text
3 Macs match "macbook pro 2015":

- MacBook Pro (Retina, 15-inch, Mid 2015) [MacBookPro11,4]: 1 configuration (1 Untested). https://doesitomarchy.com/mac/MacBookPro11-4
- MacBook Pro (Retina, 15-inch, Mid 2015) [MacBookPro11,5]: 1 configuration (1 Untested). https://doesitomarchy.com/mac/MacBookPro11-5
- MacBook Pro (Retina, 13-inch, Early 2015) [MacBookPro12,1]: 1 configuration (1 Untested). https://doesitomarchy.com/mac/MacBookPro12-1

Use get_mac with an identifier for the details.

Quickstart

There are two ways in. Both end at a configuration, where the verdicts are.

You know the Mac: GET /macs gives identifiers; GET /macs/{identifier} gives its configurations. You have the hardware: POST /match gives the configuration. Both lead to GET /configs/{id}: verdict, criteria, ports and reports. You know the Mac You have the hardware GET /macs every Mac identifier GET /macs/{identifier} all its configurations POST /match product name, board, PCI IDs config ID config ID GET /configs/{id} verdict · criteria · ports · reports
  • You know the Mac: list the Macs, then get one by its identifier. It comes with all its configurations.
  • You have the hardware: send its IDs to Identify a Mac and get the configuration back.
Quickstart

curl

# 1. Every Mac, with its configuration IDs
curl https://doesitomarchy.com/api/v1/macs

# 2. One Mac, with all its configurations
curl https://doesitomarchy.com/api/v1/macs/MacBookPro8,2

# 3. One configuration: verdict, criteria, ports, reports
curl https://doesitomarchy.com/api/v1/configs/macbookpro8-2-15-early-2011-a

JavaScript

const api = "https://doesitomarchy.com/api/v1";
const get = (path) => fetch(api + path).then((res) => res.json());

const mac = await get("/macs/MacBookPro8,2");
for (const c of mac.configurations) {
  console.log(c.distinction, c.verdict, `${c.tested} of ${c.applicable} tested`);
}

Python

import requests

API = "https://doesitomarchy.com/api/v1"

mac = requests.get(f"{API}/macs/MacBookPro8,2").json()
for c in mac["configurations"]:
    print(c["distinction"], c["verdict"], f"{c['tested']} of {c['applicable']} tested")

Errors

Errors come with an HTTP status and a JSON body. error says what went wrong. For reports, problems lists every problem, not just the first.

400The request or the report isn't valid.
401No source key, or an unknown one.
403The key was revoked, or the report names a different source.
404No such Mac, configuration, report or endpoint.
413The report is over 1 MiB.
415A report format we don't read.
422No configuration in the catalog fits the report's hardware.
429Too many reports this hour. Wait Retry-After seconds.
Response: 400 Bad Request
{
  "error": "invalid report",
  "problems": [
    "tested_at: \"2026-10-01 18:30\" must be a timestamp with a time zone, e.g. 2026-09-30T14:05:00Z or 2026-09-30T10:05:00-04:00",
    "items.wifi: unknown capability (see /api/v1/capabilities, or send it as an extra)"
  ]
}

Becoming a source

A source is a test tool that submits reports. To get a source key, register your tool: its name, a link to its source code, a contact email and what it tests. A maintainer reviews the request, and you collect the key once from a private link.

  • Keys look like doi_ followed by 32 characters. We keep only a hash, so a lost key is replaced, not recovered.
  • Keep the key in your tool's build or server, not in a public repository. If it leaks, ask for a new key link; the old key stops working at once.
  • Each source may submit up to 60 reports an hour.

Every report goes through review, as the diagram shows.

1. Your tool runs the tests. 2. It shows the tester the consent notice; they can stop. 3. POST /reports with your key; the reply is 201 with a code, state pending. 4. A maintainer reviews it; GET /reports/{code} shows its state. 5. Accepted: the results show on the site. Or rejected, with a reason. 1 Your tool runs the tests on Omarchy, on a real Mac 2 It shows the consent notice the tester can stop here 3 POST /reports with your key → 201, a code, state pending 4 A maintainer reviews it GET /reports/{code} shows its state accepted results show on the site rejected with the reason why

Submit a report

POST /api/v1/reports

Send one report per test run, as JSON or YAML, with your key in the Authorization header.

You don't need the configuration ID. Send the model identifier and the hardware probe, and the server finds the configuration. Include the CPU name (hardware.cpu): it tells apart configurations that share every device. If the probe still fits several, the report is taken anyway, and a maintainer picks the right one.

A new report is pending until a maintainer reviews it. Keep its code to check on it. flags lists anything a maintainer will look at, such as a possible duplicate.

POST /api/v1/reports

curl

curl -X POST https://doesitomarchy.com/api/v1/reports \
  -H "Authorization: Bearer $DOI_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @report.json

JavaScript

import { readFile } from "node:fs/promises";

const res = await fetch("https://doesitomarchy.com/api/v1/reports", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DOI_KEY}`,
    "Content-Type": "application/json",
  },
  body: await readFile("report.json"),
});
const receipt = await res.json();

Python

import os
import requests

with open("report.json", "rb") as f:
    receipt = requests.post(
        "https://doesitomarchy.com/api/v1/reports",
        headers={
            "Authorization": f"Bearer {os.environ['DOI_KEY']}",
            "Content-Type": "application/json",
        },
        data=f,
    ).json()
Response: 201 Created
{
  "code": "3f9a1c07be",
  "state": "pending",
  "config": "macbookpro8-2-15-early-2011-a",
  "candidates": [],
  "flags": [],
  "status_url": "https://doesitomarchy.com/api/v1/reports/3f9a1c07be"
}

The report format

Format doesitomarchy/report/v1, described in full by the schema. Items use the IDs from List criteria; leave out anything you didn't test. Each result says how it was tested: automatic (the tool checked), observed (the tester looked) or fixture (with test equipment, such as a loopback plug).

  • tested_at is when the test ran, with a time zone. The site shows every time in UTC.
  • omarchy.version is /etc/os-release's VERSION_ID, from any channel: a release (4.0.4), a pre-release (4.0.4rc2) or an edge build (4.0.0.r6713.ga85e29a). For a dev install, add "channel": "dev" and the checkout's commit as revision. Stable releases decide verdicts; other channels show as newest_build where they differ.
  • Port criteria can be reported per connector, as ports.usb-a@left-2, using the IDs in the configuration's connectors. A criterion is fully verified only when every connector it applies to has passed.
  • Put the check's output in evidence when something fails or only partly works. It's what helps someone fix it.
  • Don't send serial numbers, network addresses, or user and computer names. They're removed anyway, but it's better never to send them.
report.json
{
  "schema": "doesitomarchy/report/v1",
  "identifier": "MacBookPro8,2",
  "source": { "version": "1.2.0", "profile": "full" },
  "tester": { "handle": "vintage-fan" },
  "tested_at": "2026-10-01T18:30:00-04:00",
  "omarchy": { "version": "4.0.4" },
  "kernel": "6.16.2-arch1-1",
  "hardware": {
    "product_name": "MacBookPro8,2",
    "board_id": "Mac-94245A3940C91C80",
    "cpu": "Intel(R) Core(TM) i7-2635QM CPU @ 2.00GHz",
    "pci": ["8086:0126", "1002:6760", "14e4:4331"]
  },
  "items": {
    "boot.install":     { "status": "supported", "method": "observed" },
    "network.wifi":     { "status": "partial", "method": "automatic",
                          "evidence": "b43: firmware loaded; 5 GHz networks not listed" },
    "power.sleep-wake": { "status": "failed", "method": "observed",
                          "evidence": "resume hangs on a black screen" },
    "audio.headphone":  { "status": "not_tested", "reason": "no-equipment" }
  },
  "consent_notice": "This sends your test results to DoesItOmarchy.com. …"
}
GET /api/v1/schema

curl

curl https://doesitomarchy.com/api/v1/schema

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/schema");
const schema = await res.json();

Python

import requests

schema = requests.get("https://doesitomarchy.com/api/v1/schema").json()

Check a report

GET /api/v1/reports/{code}

Where a report stands: pending, accepted (with its public url), rejected (with the reason) or retracted (with both).

GET /api/v1/reports/{code}

curl

curl https://doesitomarchy.com/api/v1/reports/3f9a1c07be

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/reports/3f9a1c07be");
const report = await res.json();

Python

import requests

report = requests.get("https://doesitomarchy.com/api/v1/reports/3f9a1c07be").json()
Response: 200 OK
{
  "code": "3f9a1c07be",
  "state": "rejected",
  "config": "macbookpro8-2-15-early-2011-a",
  "identifier": "MacBookPro8,2",
  "tested_at": "2026-10-01T22:30:00Z",
  "submitted_at": "2026-10-01T22:41:07Z",
  "reason": "Not a real test run: every item has the same evidence."
}

List Macs

GET /api/v1/macs

Every Intel Mac in the catalog, with its verdicts and its configuration IDs.

Returns, for each Mac

identifierThe model identifier, e.g. MacBookPro8,2.
nameApple's name for it.
line, yearsIts product line and the years it was sold.
verdictsThe distinct verdicts of its configurations.
configsIts configuration IDs.
urlIts page on this site.
GET /api/v1/macs

curl

curl https://doesitomarchy.com/api/v1/macs

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/macs");
const data = await res.json();

Python

import requests

data = requests.get("https://doesitomarchy.com/api/v1/macs").json()
Response: 200 OK
{
  "macs": [
    {
      "identifier": "MacBook1,1",
      "name": "MacBook (13-inch)",
      "line": "macbook",
      "years": "2006",
      "verdicts": ["not-compatible"],
      "configs": ["macbook1-1-mid-2006-a"],
      "url": "https://doesitomarchy.com/mac/MacBook1-1"
    },
    {
      "identifier": "MacBook2,1",
      "name": "MacBook (13-inch, Mid 2007)",
      "line": "macbook",
      "years": "2006–2007",
      "verdicts": ["untested"],
      "configs": ["macbook2-1-late-2006-a", "macbook2-1-mid-2007-a"],
      "url": "https://doesitomarchy.com/mac/MacBook2-1"
    },
    … 119 more
  ]
}

Get a Mac

GET /api/v1/macs/{identifier}

One Mac with all its configurations.

Path parameter

identifierIn the path: a model identifier, e.g. MacBookPro8,2. MacBookPro8-2 works too.

Returns

Everything List Macs does, plus efi (32 or 64), security_chip, hard_blocker (why it can never run Omarchy, if it can't), board_ids, and configurations, each one in full, as Get a configuration returns it.

GET /api/v1/macs/{identifier}

curl

curl https://doesitomarchy.com/api/v1/macs/MacBookPro8,2

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/macs/MacBookPro8,2");
const mac = await res.json();

Python

import requests

mac = requests.get("https://doesitomarchy.com/api/v1/macs/MacBookPro8,2").json()
Response: 200 OK
{
  "identifier": "MacBookPro8,2",
  "name": "MacBook Pro (15-inch, Late 2011)",
  "line": "macbook-pro",
  "years": "2011",
  "verdicts": ["untested"],
  "configs": ["macbookpro8-2-15-early-2011-a", … 3 more],
  "url": "https://doesitomarchy.com/mac/MacBookPro8-2",
  "efi": 64,
  "board_ids": ["Mac-94245A3940C91C80"],
  "configurations": [{…}, … 3 more]
}

Get a configuration

GET /api/v1/configs/{id}

A configuration is one release of a Mac with the parts that set it apart, such as its GPU. Its verdict comes from the methodology.

Path parameter

idIn the path: a configuration ID, from a Mac's configs.

Returns

verdictThe verdict, with counts per verdict, how many criteria are applicable and how many are tested. verified means every applicable criterion passed.
capabilitiesEach applicable criterion's current result: its verdict, the Omarchy version and kernel tested, the method and the latest_report. Port criteria add ports, one result per connector. A failed one may have a fix.
componentsIts parts, with the hardware_ids that identify them (and gles for GPUs).
connectorsIts port layout, with the criteria each connector is tested for. portmap_url is the drawing (SVG).
reportsThe reports behind its results.
GET /api/v1/configs/{id}

curl

curl https://doesitomarchy.com/api/v1/configs/macbookpro8-2-15-early-2011-a

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/configs/macbookpro8-2-15-early-2011-a");
const config = await res.json();

Python

import requests

config = requests.get("https://doesitomarchy.com/api/v1/configs/macbookpro8-2-15-early-2011-a").json()
Response: 200 OK
{
  "id": "macbookpro8-2-15-early-2011-a",
  "identifier": "MacBookPro8,2",
  "label": "Core i7 2.0 GHz · HD 3000 + Radeon HD 6490M",
  "distinction": "HD 6490M · Early 2011",
  "release": "MacBook Pro (15-inch, Early 2011)",
  "order_numbers": ["MC721LL/A"],
  "components": [
    {
      "id": "gpu/intel-hd-3000",
      "kind": "gpu",
      "name": "Intel HD Graphics 3000",
      "hardware_ids": ["pci:8086:0116", "pci:8086:0126"],
      "gles": "3.0"
    },
    {
      "id": "gpu/amd-radeon-hd-6490m",
      "kind": "gpu",
      "name": "AMD Radeon HD 6490M",
      "hardware_ids": ["pci:1002:6760"],
      "gles": "3.1"
    },
    … 11 more
  ],
  "ports": ["Line in (analog)", "Optical digital audio in", … 7 more],
  "connectors": [
    {
      "id": "left-1",
      "side": "left",
      "type": "magsafe",
      "name": "MagSafe power",
      "criteria": []
    },
    {
      "id": "left-2",
      "side": "left",
      "type": "ethernet-gbe",
      "name": "Gigabit Ethernet",
      "criteria": ["network.ethernet"]
    },
    … 7 more
  ],
  "layout_sources": [
    "https://cdsassets.apple.com/live/6GJYWVAV/user/ma1567_macbook_pro_15inch_early2011.pdf"
  ],
  "portmap_url": "https://doesitomarchy.com/portmap/MacBookPro8-2_15-early-2011.svg",
  "features": ["Battery", "Lid (sleep on close)", … 12 more],
  "verdict": "untested",
  "applicable": 37,
  "tested": 0,
  "verified": false,
  "counts": {
    "Supported": 0,
    "Partial": 0,
    "Failed": 0,
    "Unsupported": 0,
    "Untested": 37
  },
  "capabilities": [
    {
      "id": "boot.installer-efi64",
      "verdict": "untested"
    },
    {
      "id": "boot.install",
      "verdict": "untested"
    },
    … 35 more
  ],
  "reports": [],
  "url": "https://doesitomarchy.com/mac/MacBookPro8-2#cfg-macbookpro8-2-15-early-2011-a"
}

List criteria

GET /api/v1/capabilities

The test criteria, including retired ones. Configurations and reports refer to them by id, such as network.wifi. A blocking criterion (Boot) decides whether a configuration fails; the criteria page shows them all.

GET /api/v1/capabilities

curl

curl https://doesitomarchy.com/api/v1/capabilities

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/capabilities");
const data = await res.json();

Python

import requests

data = requests.get("https://doesitomarchy.com/api/v1/capabilities").json()
Response: 200 OK
{
  "capabilities": [
    {
      "id": "boot.installer-efi64",
      "category": "boot",
      "name": "Stock installer boots via 64-bit EFI",
      "description": "The unmodified Omarchy installer ISO boots natively from the Mac's startup manager.",
      "blocking": true
    },
    {
      "id": "boot.installer-efi32",
      "category": "boot",
      "name": "Installer boots via 32-bit EFI workaround",
      "description": "The installer boots on 32-bit EFI firmware using a boot shim (e.g. rEFInd with a 32-bit GRUB). Record the method used in the notes.",
      "blocking": true
    },
    … 44 more
  ]
}

Identify a Mac

POST /api/v1/match

Send what a Mac reports about its hardware, and get back the Mac and its configuration. It's the same matching as Identify my Mac. Send any of these; more IDs give a surer match.

Body

product_nameThe model identifier. Linux: /sys/class/dmi/id/product_name. macOS: sysctl -n hw.model.
board_ide.g. Mac-94245A3940C91C80. Linux: /sys/class/dmi/id/board_name.
cpuThe processor's name. It tells apart configurations that share every device.
pci, usbDevice IDs as vendor:device, e.g. 1002:6760, from lspci -nn and lsusb.

Returns

config is the best configuration, or null when several fit equally. best lists every configuration tied at the top (just one when exact); their order means nothing. candidates ranks them all, with the IDs each matched and its parts not_reported.

From a browser, leave out the Content-Type header, as the JavaScript sample does: the request then needs no CORS preflight.

POST /api/v1/match

curl

curl -X POST https://doesitomarchy.com/api/v1/match \
  -H "Content-Type: application/json" \
  -d '{"product_name": "MacBookPro8,2", "pci": ["1002:6760"]}'

JavaScript

const res = await fetch("https://doesitomarchy.com/api/v1/match", {
  method: "POST",
  body: JSON.stringify({"product_name": "MacBookPro8,2", "pci": ["1002:6760"]}),
});
const match = await res.json();

Python

import requests

match = requests.post(
    "https://doesitomarchy.com/api/v1/match",
    json={"product_name": "MacBookPro8,2", "pci": ["1002:6760"]},
).json()
Response: 200 OK
{
  "identifier": "MacBookPro8,2",
  "by": "product_name",
  "exact": true,
  "config": "macbookpro8-2-15-early-2011-a",
  "best": ["macbookpro8-2-15-early-2011-a"],
  "candidates": [
    {
      "config": "macbookpro8-2-15-early-2011-a",
      "score": -10,
      "matched": ["pci:1002:6760"],
      "not_reported": ["pci:8086:0116/pci:8086:0126", "pci:14e4:4331"],
      "label": "MacBookPro8,2 · HD 6490M · Early 2011 · MacBook Pro (15-inch, Early 2011)",
      "url": "https://doesitomarchy.com/mac/MacBookPro8-2#cfg-macbookpro8-2-15-early-2011-a"
    },
    {
      "config": "macbookpro8-2-15-early-2011-b",
      "score": -30,
      "matched": [],
      "not_reported": ["pci:8086:0116/pci:8086:0126", "pci:1002:6741", … 1 more],
      "label": "MacBookPro8,2 · HD 6630M / 6750M · Early 2011 · MacBook Pro (15-inch, Early 2011)",
      "url": "https://doesitomarchy.com/mac/MacBookPro8-2#cfg-macbookpro8-2-15-early-2011-b"
    },
    … 2 more
  ]
}

Versions and rules

  • Only send results from real test runs on real hardware.
  • Maintainers review every report and can reject or retract it, with a reason that's shown on the site.
  • v1 may gain optional fields, so ignore fields you don't know. A breaking change would get a new version (/v2), and v1 would keep working.
  • Every change is in the API changelog.

API changelog

Changes to the API and the report format, newest first. Changes to the catalog itself are in the catalog changelog.

  1. · MCP answers name the release

    A Mac sold under several names, such as the iMac10,1 (21.5-inch and 27-inch), is now named after the release an MCP answer is about: identify_mac, and get_mac or what_needs_testing with a config. get_mac for the whole Mac adds an "Also sold as" line.

    • search_macs no longer flags a valid value that matches no Mac yet, such as status:supported, as a mistake.
  2. · An MCP server for AI assistants

    AI assistants can now look Macs up themselves through the MCP server at /mcp, with four read-only tools: search_macs, get_mac, identify_mac and what_needs_testing. No key needed. The index lists it as mcp.

    • Streamable HTTP without sessions, speaking MCP 2026-07-28 and the earlier versions back to 2025-03-26. Each IP address may make 300 requests a minute.
  3. · Descriptions for tools and AI agents

    The API is now described in full as an OpenAPI 3.1 document at /api/v1/openapi.json, and in plain Markdown for language models at /llms.txt. The index lists the new document as openapi.

    • Unknown /api paths now answer 404 in the usual error format, {"error": "…"}.
  4. · OpenGL ES levels and known limitations

    GPU components gain gles, the highest OpenGL ES version their Linux driver reaches. Configurations gain known_limitations, notes from research that come before any test.

  5. · Board IDs per release

    Configurations gain board_ids, the board IDs tied to their release by real machines. Identify a Mac uses them: a matching board shows in a candidate's matched as board:….

  6. · Omarchy channels

    Results can come from any Omarchy channel: stable, rc, beta, edge or dev, and each one says its omarchy_channel. Stable releases decide verdicts; where a newer build disagrees, a criterion shows it in newest_build.

    • Report format: a dev install sends "channel": "dev" and the checkout's commit as revision.
  7. · Fix tracking

    A failed criterion can carry fix: its GitHub issue, its state (open, claimed, stale, proposed or fixed), who is on it, and retest once a fix lands after the latest result.

  8. · Port map drawings

    Configurations gain portmap_url, their port layout drawn as an SVG, when there is one.

  9. · Ports per connector

    Configurations gain connectors, their port layout, with the criteria each connector is tested for. Port criteria have a result per connector, in ports.

    • Report format: port criteria can be reported per connector, as ports.usb-a@left-2.
    • A connector's result can be covered_by a group-mate that passed, or flagged possible_hardware_fault.
  10. · The API opens

    Version 1: read the catalog and test results, identify a Mac from its hardware, and submit diagnostic reports with a source key, in the doesitomarchy/report/v1 format.

    ↑↓ moveEnter openTab completeEsc close