apple-docs-mcp
Provides offline search of Apple Developer Documentation from Xcode's local documentation index, returning full text, code examples, availability, and scores, along with framework listing and documentation status tools.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@apple-docs-mcpsearch SwiftData model inheritance"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
apple-docs-mcp
An MCP server and CLI that answer Apple Developer Documentation questions from the documentation corpus Xcode already has on disk — no network, no Xcode process, no approval dialog.
Coding agents recall Apple APIs from training data that lags the installed SDK. This server hands them the real text: complete pages with their code listings, the declaration, and the availability windows.
Measured against Xcode's own DocumentationSearch
Xcode 27 exposes an MCP tool, DocumentationSearch, that answers from the same
corpus through mcpbridge. It is a good tool, and this project uses it — but
only when its semantic ranking is wanted. The two paths measured on this machine
(6 queries, same session, 2026-09-17):
Xcode | this server, | |
Requires | Xcode running, workspace open, per-process approval, 24 h expiry | nothing |
Latency, mean of 6 queries | 264–371 ms across runs | 5.9 ms in the MCP server; 2–17 ms across runs |
Latency, single page fetch | not available | 2 ms |
Corpus reachable | the 20 documents the tool returns | 263,513 pages, searchable |
Text per result | fragment, 46–5,305 chars (median 437) | the whole page (up to 375,908 chars) |
Fields per result | title, uri, contents, score, kind | plus declaration, availability, framework, kind, USR |
Index build | n/a | 263,513 pages in 5.3 s, 128 MB |
Reproduce all of it on your own machine — uv run python tools/benchmark.py. The
offline figures move with the OS page cache, so the script reports the first call
of a fresh process separately from the steady state: 5 ms first call, 17 ms mean
over six queries in a standalone process, 5.9 ms mean over the same six in a
running MCP server that warmed itself at startup.
Evaluated, not asserted
Six queries and an opinion is not evidence, so ranking is measured on two sets —
uv run python tools/evaluate.py, ground truth stated per query:
40 identifier queries, sampled from the corpus, asked twice: as the bare title, and inside a sentence the way a person types it ("I want to use ARAnchor in my app, how does it work"). The expected answer is the page's URI and no judgement is involved.
12 questions ("how do I download a file and show progress"). Answers were judged from the pooled results of both engines, so an equally good page is not marked wrong for not being the one the question writer had in mind.
Recall, with @k meaning the expected page is in the first k results:
Identifier, bare title (n=40) | @1 | @3 | @5 | MRR | mean latency |
this server, | 87.5% | 92.5% | 92.5% | 0.900 | 1.4–5.7 ms |
Xcode | 57.5% | 65.0% | 67.5% | 0.615 | 294 ms |
Identifier inside a sentence (n=40) | @1 | @3 | @5 | MRR | mean latency |
this server, | 90.0% | 92.5% | 92.5% | 0.912 | 5–28 ms |
Xcode | 35.0% | 45.0% | 47.5% | 0.402 | 353 ms |
Questions (n=12) | @1 | @3 | @5 | MRR | mean latency |
this server, | 41.7% | 66.7% | 75.0% | 0.544 | 15 ms |
this server, | 41.7% | 83.3% | 91.7% | 0.600 | 282 ms |
Xcode | 41.7% | 83.3% | 91.7% | 0.614 | 319 ms |
One trade-off is measured rather than hidden: in hybrid, weighting the local
list equally with the bridge raises recall@1 from 41.7% to 58.3% and lowers
recall@5 from 91.7% to 75.0% (MRR 0.646 against 0.600). Bridge-weighted is kept
because the reported metric is recall and because an agent that reads three
results cares more about the answer being inside them.
The script also audits its own ground truth, because the person who wrote the questions also judged the pooled answers. Where the accepted pages came from: 9 found by both engines, 13 only by the bridge's pool, 4 only by this server's — the labelling leans toward the bridge, not toward this project. A further 12 accepted pages were returned by neither engine; nobody judged those, which is the blind spot of pooling and it flatters both engines equally.
Read that honestly, because it cuts both ways:
For a named API — the lookup an agent makes constantly — this server is clearly better and 50–100x faster, with no approval dialog. The bridge scored 65% at @3 on bare names and 45% when the name arrived inside a sentence; the local index scored 92.5% on both.
For a question, the bridge is better than
/offline(91.7% vs 75.0% at @5). Semantic search reaches wording that no term index has seen, and this project does not reproduce Apple's embedding space (measured cosine ≈ 0.01–0.03 for the very same page text), so it does not pretend to.auto— the default — is never worse than the better engine on any of the three sets, because it reads the query and picks. A query that names an API is answered locally in about a millisecond; a question that names no API gets the bridge's semantic order fused in. A framework name is not an API name: it scopes a question rather than being one.
The bridge's own weakness is visible in the data as well: 39 of the 118 documents it returned across an earlier six-query run were under 100 characters, because it ranks short "…: Relationships" stubs highly, and a stub cannot answer a question.
Two things this project does not claim. It does not reproduce Xcode's
semantic ranker offline: the stored page vectors are 512-dimensional but live in
a space NLContextualEmbedding does not reproduce (measured cosine ≈ 0.01–0.03
for the same page text). And it is not a better search engine than Xcode's —
it is the same corpus with different plumbing.
Related MCP server: Apple Docs MCP
Relationship to Xcode's own MCP server
Xcode ships an MCP server (xcrun mcpbridge) that advertises 54 tools. Read
the list, because the comparison is narrower than the counts suggest: exactly
one of the 54 is documentation.
What the 54 cover | Examples |
Building, running, testing, debugging |
|
Devices and previews |
|
Project files and settings |
|
Localisation, crash and performance retrieval |
|
Documentation |
|
So this server is not a replacement for that one and does not try to be: it answers documentation questions, and only the last row of that table overlaps with what it does. If you want the other 53 — build, run, debug, device control — register Xcode's server as well:
claude mcp add xcode-tools -- xcrun mcpbridgeTwo things are worth knowing before you do. That server needs Xcode running with an approved workspace, and it hands the agent write and execute access to your project; this one reads documentation and nothing else. Run both if you want both, and route documentation at whichever answers your kind of question better — the evaluation above is the evidence for that choice.
Install
Requires macOS and Xcode with the Developer Documentation asset installed. uv for the toolchain.
git clone https://github.com/Ahmetshbzz/apple-docs-mcp
cd apple-docs-mcp
uv sync
uv tool install --editable .Register with an MCP client
Claude Code:
claude mcp add apple-docs --scope user -- \
uv run --quiet --project /path/to/apple-docs-mcp apple-docs-mcpCodex (~/.codex/config.toml):
[mcp_servers.apple-docs]
command = "uv"
args = ["run", "--quiet", "--project", "/path/to/apple-docs-mcp", "apple-docs-mcp"]
startup_timeout_sec = 60Tools
Tool | Purpose |
| Ranked pages with declaration, availability, and a snippet. Offline by default; |
| One whole page by URI: prose, code listings, declaration, availability |
| Every framework with its page count (375 frameworks) |
| Corpus, index, and bridge state, with the remedy when something is missing |
| The Swift/Apple-platform engineering playbook and its reference files |
| Build or rebuild the derived index (search builds it automatically on first use) |
The playbook is also served as the swift://playbook resource, the
swift://reference/{topic} resource template, and the swift-playbook prompt.
Modes
Mode | What answers | Measured latency | Xcode needed |
| the index for API names, the bridge for questions | 1.5 ms / 296 ms | only for questions |
| the local corpus index | 2–28 ms | no |
| Xcode's bridge | 185–739 ms | yes, approved |
| both, fused by reciprocal rank | 308–313 ms | no (falls back) |
hybrid labels each hit local, bridge, or hybrid, so a caller can see
where an answer came from. If the bridge fails in hybrid, the offline results
are returned with a bridgeError note instead of an error.
CLI
apple-docs search "SwiftData model inheritance" --limit 5
apple-docs search "AVAudioSession category" --framework AVFAudio --json --full
apple-docs get /documentation/SwiftUI/View
apple-docs frameworks
apple-docs status
apple-docs index --rebuildExit codes are distinct per failure so scripts can branch:
0 success, 1 usage, 2 not approved, 3 Xcode unavailable, 4 timeout,
5 protocol, 6 asset missing, 7 page not found.
Where the text comes from
Xcode's Developer Documentation asset keeps every page as a JSON document in a SQLite database:
/System/Library/AssetsV2/com_apple_MobileAsset_AppleDeveloperDocumentation/
<uuid>.asset/AssetData/documentation-db/index.sqldocuments holds one JSON row per page — uri, title, framework, kind,
content (prose and code listings), symbol (identity and USR), platforms
(availability) — and observations holds precomputed 512-dimensional vectors.
The neighbouring documentation-cache/ is a different thing: an HTTP-style blob
store (cache.db refs into fs/<n> segments) for images and cached payloads.
The server derives a full-text index from documents and leaves the corpus
alone: it is opened read-only and immutable, and never written to.
The derived index
Built once per corpus version, in
~/Library/Caches/apple-docs-mcp/.118 MB for 263,513 pages; 5.3 s to build; atomic swap, so an interrupted build never leaves a half-index. An index from an older schema is deleted on the next build instead of lingering.
Contentless FTS5 — the index holds tokens, and page text is read back from the corpus for the handful of pages a query returns. That is why it is 118 MB and not 500 MB.
Term statistics live in the index (
fts5vocab), which is what lets a query be built from its selective words without touching the corpus.Freshness is decided from the corpus file's size and mtime plus the index schema version.
The Swift playbook
swift_playbook serves an authored engineering playbook — what to do, what never
to do, and which reference file answers an architecture question — while
search_docs supplies signatures and availability, which the playbook
deliberately does not carry. The playbook stays in its own repository and is read
in place; point APPLE_DOCS_SWIFT_SKILL at it. When it is absent, the tool says
where it looked instead of returning nothing.
Configuration
Variable | Default | Meaning |
| the system asset root | Where the documentation asset lives |
|
| Where the derived index lives |
|
| Where the playbook lives |
|
| How to launch Xcode's bridge |
Limitations
Ranking is BM25 over the query's selective terms, with column weights biased toward the URI and title plus an identifier boost. It is not semantic; use
semanticorhybridwhen meaning matters more than terms.The corpus is written by Xcode, so a page can be a section rather than an article ("…: Relationships"). Such sections are short by construction.
Xcode can purge the asset (
…xml.purgedappears next to it). If the corpus disappears, searches fail with a remedy rather than returning nothing.Attribute rows in
index.sqlcarryframework,title, andtypebut no text; the text lives indocuments. The server readsdocuments.
Development
uv run pytest tests/ -q # 148 tests, no Xcode needed
uv run pytest tests/ -m integration # live bridge tests
uv run python tools/benchmark.py # latency, offline against the bridge
uv run python tools/evaluate.py # recall, on judged ground truth
uv run ruff check src tests toolsUnit tests build synthetic corpus and skill fixtures, so they run anywhere. The
integration tests exercise the live bridge and skip themselves when it is
unavailable. The MCP surface itself is tested over a real stdio transport in
tests/test_server_surface.py.
Verified environment
Measured 2026-09-17, not assumed:
Thing | Value |
Xcode | 27.0 (27A266a) |
Swift | 6.4 (swiftlang-6.4.0.34.1) |
Documentation corpus | 263,513 pages, 375 frameworks, ~375 MB of text |
Derived index | 118 MB, built in 5.3 s |
License
MIT — see LICENSE.
Available Tools
3 toolsdoc_statusA
Report whether documentation search is usable right now: asset presence, bridge path, and whether this process is approved to use Xcode's tools.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of disclosing behavior. It does so well by framing the tool as a pure report and listing the three checks it performs, which implicitly signals a non-mutating status operation. It could be more explicit about having no side effects, but 'Report whether' plus the enumerated checks provides strong transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence followed by a compact colon-separated list. Every phrase earns its place, and there is no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter status tool with an output schema available, the description covers what the tool reports, when it is relevant, and which aspects are checked. It could have explicitly instructed the agent to call it before search_docs, but nothing essential for invoking it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4. There is no parameter information needed in the description, and the description does not attempt to add any because none is required.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Report') and a specific subject ('whether documentation search is usable right now'), then enumerates the scope: asset presence, bridge path, and Xcode approval. This clearly distinguishes it from sibling tools like search_docs and list_frameworks, which perform different actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'usable right now' makes it clear this is a readiness/precondition check, so an agent understands to call it when it needs to verify documentation search is available before searching. It does not explicitly name alternatives or exclusion conditions, but the intended timing is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_frameworksA
List every framework in Xcode's local documentation index. Runs offline and needs no Xcode approval.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Since no annotations are provided, the description carries the full burden of behavioral disclosure. It meaningfully discloses that the tool runs offline and needs no Xcode approval, which addresses common permission and environment concerns. It does not describe ordering or other return details, but the output schema exists to cover return structure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler; the first states the core purpose and the second adds two key behavioral constraints. Everything present earns its place, and the description is immediately scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter list operation with an output schema, the description is complete: it names the resource, clarifies it covers every framework, and adds offline/no-approval context. An agent has everything it needs to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so there is nothing for the description to explain about parameter usage. With 100% schema coverage of an empty schema, the baseline of 4 is appropriate, and no further parameter documentation is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and a precise resource ('every framework in Xcode's local documentation index'), making the tool's scope unmistakable. It clearly differentiates this from siblings like search_docs and doc_status by stating it enumerates all frameworks rather than searching or checking status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when this tool is appropriate: when a complete framework list is needed, and it works offline without requiring Xcode approval. It does not explicitly name alternatives or exclusion conditions, but the usage context is clear enough for an agent to select it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_docsA
Search Apple Developer Documentation by meaning, using Xcode's local documentation index. Returns full document text including code examples, with each result's uri and match score. Use this before answering from memory about Swift or Apple platform APIs.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Natural-language or API search query. | |
| timeout | No | Seconds to wait for the bridge before failing. | |
| frameworks | No | Restrict the search to these frameworks, e.g. ['SwiftData']. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses that the search is semantic ('by meaning'), uses a local Xcode index, and returns full document text with code examples, uri, and match score. It does not mention failure modes or side effects, but this is a read-only search operation and the core behavior is well conveyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three concise sentences with no filler. The first sentence states the core purpose, the second describes the return value, and the third gives actionable usage guidance. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, usage context, return contents, and the underlying local-index mechanism. An output schema exists to document the exact return shape, and the schema documents all parameters. It could add a note about prerequisites or when to use list_frameworks first, but it is otherwise complete for a search tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds context about the search approach and return format but does not add meaning to the parameters themselves beyond what the schema already provides for query, timeout, and frameworks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: 'Search Apple Developer Documentation by meaning, using Xcode's local documentation index.' It also explains what is returned (full document text, code examples, uri, match score), making the tool's function unmistakable and distinct from the sibling tools list_frameworks and doc_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'Use this before answering from memory about Swift or Apple platform APIs.' It does not explicitly mention when not to use it or alternatives, but the sibling tools are clearly different in purpose, so no exclusion is necessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
doc_status - First observed
list_frameworks - First observed
search_docs
TDQS
Scored across 3 tools
Each tool has a clearly distinct role: search_docs finds documentation, list_frameworks enumerates available documentation domains, and doc_status reports tooling health. There is no meaningful overlap or ambiguity between them.
search_docs and list_frameworks follow a clear verb_noun snake_case pattern, while doc_status is a noun_noun name rather than a verb-based one. The naming is still consistent in style and readable, but the health-check tool deviates slightly from the verb-led convention.
Three tools is well-scoped for a documentation lookup server: one core search action, one discovery action, and one operational status check. Each tool clearly earns its place without redundancy or bloat.
The server covers the main documentation workflow well: searching docs, listing available frameworks, and verifying tool readiness. A direct fetch-by-URI option is missing, but since search_docs returns full document text, it is not a serious dead end.
Maintenance
Related MCP Connectors
Apple Developer Documentation with Semantic Search, RAG, and AI reranking for MCP clients
Get up-to-date, version-specific documentation and code examples from official sources directly in…
Search and read Rust documentation for the standard library and any crate on crates.io
Search public open-source code, documentation, metadata, vulnerabilities, changelogs, and examples.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceProvides comprehensive access to Apple's development documentation ecosystem including hidden Xcode docs, Swift Evolution proposals, GitHub repositories, and WWDC session notes. Enables developers to search and retrieve advanced Apple development resources not available through public channels.16MIT
- AlicenseAqualityDmaintenanceProvides access to Apple's official developer documentation, frameworks, APIs, and WWDC session transcripts across all Apple platforms. It enables AI assistants to search technical guides, sample code, and platform compatibility information using natural language queries.181,132 npm1,381MIT
- AlicenseNot gradedqualityBmaintenanceProvides access to Apple documentation and WWDC transcripts with semantic, keyword, and hybrid search capabilities, enabling developers to quickly find relevant code examples and technical information.120MIT
- AlicenseAqualityDmaintenanceSearch and retrieve information from Apple's complete developer documentation, including API references, code examples, and Human Interface Guidelines, across all Apple platforms and versions.426 npm8MIT