skim-pdf-mcp
A local MCP bridge that lets an AI client read what you're viewing in Skim — and, per this schema, optionally recolor or delete your red/gray underlines.
List open documents (
get_open_documents): every PDF open in Skim, frontmost first, with name, path, page count and current page.Get full reading context (
get_reading_context): front document identity, current page, live text selection (explicitly empty if none), all pending red underlines with quoted text/page/color/comment, the page's text layer, and a PNG of the page with the selection outlined blue and pending underlines red — the image being ground truth for math. Options:page(1-based override),document_path,include_image=false.List pending underlines (
get_pending_underlines): all red (pending) underlines in reading order with their quoted text.Mark underlines processed (
mark_processed): recolor pending red underlines gray by id — the only non-read-only action here; skips highlights and other colors, deletes nothing, and should only run after reader approval.Clear processed underlines (
clear_processed): permanently delete gray underlines by id — destructive; only gray underlines are deleted, red underlines, highlights and other markup are skipped, and explicit approval is required.All tools accept an optional
document_path(fromget_open_documents) and default to the front document; the read tools are marked read-only, idempotent and non-open-world.
Note: this schema diverges from the README, which describes an anchored-notes workflow with only three strictly read-only tools (get_open_documents, get_reading_context, get_anchored_notes) and no document modification.
Bridges Skim PDF reader to ChatGPT via OpenAI's Secure MCP Tunnel, enabling ChatGPT to retrieve open documents, current page, live selection, pending underlines, page text, and rendered page images, and to mark or clear processed underlines.
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., "@skim-pdf-mcpExplain the red-underlined passages in my Skim PDF."
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.
skim-pdf-mcp
A local MCP server that bridges the Skim PDF reader to ChatGPT. You read a PDF in Skim, pin anchored notes where you have questions, and ChatGPT — which renders math well — gets the page, your selection and all your notes in one call.
Anchored-note-driven. You leave questions for ChatGPT as anchored notes (any icon) in Skim. Invoking the server makes ChatGPT read every anchored note in the document and address them all. Highlights, underlines and every other kind of markup are ignored.
One context call.
get_reading_contextreturns the front document, current page, live selection, all anchored notes with their text and the page text beside them, the page's text layer, and a PNG of the page with the selection and notes outlined. The image is the ground truth for math; the text layer can garble formulas.Strictly read-only. The server never modifies your document and keeps no record of what it has returned. There is nothing to mark as handled.
Private by design. The server runs on your Mac. ChatGPT reaches it through OpenAI's Secure MCP Tunnel: an outbound-only connection, no open ports, no public URL.
Python (FastMCP + PyMuPDF), MIT licensed. Supports stdio and streamable HTTP.
Requirements
macOS with Skim installed (talks to it through AppleScript; the optional
skimnotescommand-line tool from the Skim project is used as a read-only fallback for reading notes).Python 3.10+.
For ChatGPT: a ChatGPT workspace that allows custom MCP servers and an OpenAI Platform organization with a tunnel (see Connect to ChatGPT).
Related MCP server: chatgpt-sol-local-bridge
Install
git clone <this repository> skim-pdf-mcp
cd skim-pdf-mcp
uv tool install . # or: pipx install .
skim-pdf-mcp --versionuv tool install puts the command at ~/.local/bin/skim-pdf-mcp. Use that absolute path wherever a launcher needs the command (which skim-pdf-mcp).
Allow Automation
The first time the server talks to Skim, macOS asks whether the app that launched it (Terminal, tunnel-client, your shell…) may control Skim. Approve it, or enable it under System Settings → Privacy & Security → Automation. Until then calls time out after 25 seconds with an error that says so.
Run it
Transport | Command | Use when |
stdio (default) |
| A launcher such as |
streamable HTTP |
| You want a long-running server at |
HTTP flags: --host (default 127.0.0.1), --port (default 8765), --path (default /mcp). Logging flags: --log-file, --log-level (see Logs). The HTTP server has no authentication; keep it bound to loopback and never expose it on a network interface. Any local process could call it and read your notes and pages (the tools never modify anything).
Connect to ChatGPT with Secure MCP Tunnel
You need, once per workspace/organization (details in OpenAI's Secure MCP Tunnel guide):
A tunnel created in Platform tunnel settings, associated with the ChatGPT workspace you will use. Creating it needs Tunnels Read + Manage; running the client and selecting the tunnel need Read + Use. Note the
tunnel_id.A runtime API key for
tunnel-client.Permission to add custom MCP servers in your ChatGPT workspace (ask the workspace admin if the option is missing).
Install and configure tunnel-client on the Mac that runs Skim:
brew install openai/tools/tunnel-client
export CONTROL_PLANE_API_KEY="sk-..." # keep out of shell history/config files
tunnel-client init \
--sample sample_mcp_stdio_local \
--profile skim \
--tunnel-id tunnel_0123456789abcdef0123456789abcdef \
--mcp-command "$HOME/.local/bin/skim-pdf-mcp"
tunnel-client doctor --profile skim --explain
tunnel-client run --profile skimtunnel-client launches skim-pdf-mcp over stdio and relays requests. Prefer the HTTP transport? Start skim-pdf-mcp --transport http yourself and use --mcp-server-url http://127.0.0.1:8765/mcp instead of --mcp-command.
Leave tunnel-client run running: ChatGPT discovery and tool calls both depend on it. Its local admin UI (/ui, /healthz, /readyz) shows whether it is connected. Run tunnel-client help quickstart for the current flags of your installed version.
Then in ChatGPT:
Open ChatGPT Plugins, click +, choose Add custom MCP server.
Under Connection, choose Tunnel and select your tunnel (or paste its
tunnel_id). If it is not listed, the tunnel is not associated with this workspace.Configure authentication as your workspace requires, review the risk warning, choose I understand and want to continue, then Create as a plugin.
In a chat, enable the plugin and invoke it (
@skim-mcp-pdf). ChatGPT reads your anchored notes and answers them all; no further instructions are needed.
Tunnel limits that shape this server: a serialized response may be at most 10 MiB and a request has a 120 second deadline. Page images are therefore capped at 6 MiB of PNG (≈8 MiB once base64-encoded) and re-rendered smaller if a page exceeds that.
The anchored-note workflow
Read in Skim. Wherever you want ChatGPT's help, add an anchored note (Skim's note tool, the icon that pins to a spot on the page) and type your question or comment in it. Any icon works: comment, key, note, help, new paragraph, paragraph, insert.
Optionally select text for a one-off question — the live selection is included in the context.
In ChatGPT, invoke the server (
@skim-mcp-pdf) with no further instructions needed. The server's instructions tell ChatGPT to callget_reading_contextright away and to address every anchored note in the whole document, in reading order, not just the ones on the current page. It sees the page image with your selection outlined in blue and the anchored notes on that page outlined in orange, plus each note's text, page, icon, position, and the page text level with it.That is all. The server never changes your document and never records which notes it has returned: asking again returns the same notes, so there is nothing to mark as handled. Delete or edit a note in Skim when you are done with it.
Only anchored notes are read. Highlights, underlines, text boxes and every other kind of markup are ignored.
Changes you make in Skim are visible immediately (AppleScript reads the open document); you do not need to save.
Tool reference
Every tool is read-only (readOnlyHint: true). All accept an optional document_path (a path listed by get_open_documents) and default to the front document. Paths that Skim does not have open are rejected; the server never reads arbitrary files.
Tool | Purpose |
| PDFs open in Skim, frontmost first: |
| Start here. Front-document identity, current/rendered page, live selection, every anchored note of the document, page text, page PNG. |
| Every anchored note of a document in reading order. |
get_reading_context
page (1-based) overrides which page is rendered; by default it is the selection's page, else the current page. include_image=false skips rendering. The result has two parts: a JSON text block (also available as structured content) and one image/png block.
{
"document": {"name": "paper.pdf", "path": "/Users/me/paper.pdf", "page_count": 12, "current_page": 4},
"page": 4,
"selection": {"present": true, "text": "E = mc^2", "page": 4,
"bounds": {"x0": 100.0, "y0": 500.0, "x1": 180.0, "y1": 516.0}},
"note_count": 2,
"notes": [
{"id": "0x1f3…", "page": 2, "icon": "help", "text": "why does this follow?",
"quoted_text": "the spectral gap is positive",
"nearby_text": "the spectral gap is positive and therefore …",
"bounds": {"x0": 540.0, "y0": 600.0, "x1": 556.0, "y1": 616.0}},
{"id": "0x2a9…", "page": 4, "icon": "note", "text": "check the sign here",
"quoted_text": "", "nearby_text": "…",
"bounds": {"x0": 40.0, "y0": 300.0, "x1": 56.0, "y1": 316.0}}
],
"page_text": "…text layer of page 4…",
"page_text_truncated": false,
"image": {"page": 4, "width": 1275, "height": 1650, "dpi": 150, "legend": "Blue outline = live selection; …"},
"warnings": []
}With nothing selected,
selectionis explicitly{"present": false, "text": "", "page": null, "bounds": null}. The selection outline is only drawn when the selection is on the rendered page.Rectangles are in PDF points with the origin at the page's bottom-left (y grows upward). A note's rectangle is its icon; its centre is the anchor.
noteshas every anchored note of the whole document, sorted by page, then top to bottom, then left to right. There is no cap.textis what you typed in the note's body (the question or comment).quoted_textis Skim's own "text" field for the note, which Skim fills with the passage that was selected when the note was added (empty if none); it is context, not your question.nearby_textis the page text on the lines level with the icon (within 24 points above and below its centre), which is usually the passage the note sits beside; check the image for the exact spot.page_textis capped at 8000 characters. Only the notes on the rendered page are outlined on the image.Problems that do not prevent a useful answer (selection unreadable, image failed, nearby text unavailable) appear in
warningsinstead of failing the call.
get_anchored_notes
Returns {"document": …, "count": n, "notes": […], "warnings": […]} with the same note objects as above.
Logs
The server keeps its own log, so errors are not lost when it is launched by tunnel-client and nobody is watching a terminal.
Location:
~/Library/Logs/skim-pdf-mcp/server.logon macOS ($XDG_STATE_HOME/skim-pdf-mcp/server.log, default~/.local/state/…, elsewhere). The directory is created as needed. Override with--log-file PATHor theSKIM_PDF_MCP_LOG_FILEenvironment variable (the flag wins). The file rotates at 1 MB and keeps 3 backups (server.log.1…server.log.3). If the file cannot be written, the server warns on stderr and keeps running.Tail it:
tail -f ~/Library/Logs/skim-pdf-mcp/server.log, or open it in Console.app.Verbosity: INFO by default.
--log-level DEBUG(orSKIM_PDF_MCP_LOG_LEVEL=DEBUG) also logs everyosascript/skimnotescall with its duration and output size. For a server started by a launcher, setSKIM_PDF_MCP_LOG_LEVEL=DEBUGin the launcher's environment (or give it a command that includes--log-level DEBUG), then restart it.What is logged: startup (version, transport, Python and OS version, log path); one line per tool call (tool name, arguments,
outcome=ok|error|crash, duration); and every failure with its full traceback and the complete stderr of the failedosascriptorskimnotescommand (command line shown without the script text). Arguments are limited to document paths and page numbers.What is never logged: document text, note text, nearby text, selection text, page text, page images, or any note contents, nor the stdout of
osascript/skimnotes(which carries note text).stdout and stderr: nothing is ever written to stdout, because the stdio transport uses it for the MCP protocol. Warnings and errors are also echoed to stderr.
Troubleshooting
Symptom | Cause / fix |
| macOS is waiting for Automation approval, or Skim is busy with a modal dialog. Approve in System Settings → Privacy & Security → Automation. |
| Open a saved PDF in Skim. Unsaved/untitled documents have no file path and are skipped. If a saved PDF is open and you still see this, run |
| Skim named an open document but refused to report its path. The healthy documents are still listed; the skipped one is reported in |
A note is missing from the result | Only anchored notes are returned (the note icons you add with Skim's anchored-note tool, any icon). Highlights, underlines, text boxes, circles, boxes and lines are not. Unsaved notes are included when AppleScript works; with the |
| AppleScript failed for notes, so notes were read from the last-saved copy via |
Tool calls fail from ChatGPT, work locally |
|
ChatGPT shows | The full error is in the server log: |
Development
uv venv && uv pip install -e ".[dev]"
.venv/bin/pytest # no Skim or macOS required
.venv/bin/ruff check . && .venv/bin/ruff format --check .
.venv/bin/mypy
.venv/bin/pytest --live-skim # optional: smoke test against a running Skim; read-onlySee AGENTS.md for the code layout and conventions.
Verification status
The unit suite covers the AppleScript output parsing, the skimnotes plist parsing, the service logic, real PyMuPDF text extraction and rendering (including rotated pages and the image budget), the MCP tool surface over an in-memory client, and the log file. Both transports start and list the same three tools. The AppleScript snippets compile with osacompile against Skim's scripting dictionary.
Run against a live Skim (Skim 1.7.17 on macOS, one saved PDF open, Automation approved): the document-listing, selection and note-listing snippets on the front document, read-only. The note listing returned a real anchored note (note icon, page 5, its text and position) through the installed CLI over stdio; other icons have only been checked through the unit tests. An early live run found that the snippets resolved paths with POSIX path of (file of d), which raises AppleScript error -1700 inside Skim, so the listing was empty and path-addressed tools could not find any document; they now use Skim's path of document, and pytest --live-skim cross-checks the listing against osascript -e 'tell application "Skim" to get name of every document'. The live tests also check that a full read leaves every note of the document unchanged. Not yet run live: an end-to-end ChatGPT session through a real tunnel with the anchored-note tools.
License
MIT — see LICENSE.
Available Tools
5 toolsclear_processedClear processed underlinesADestructiveIdempotent
PERMANENTLY DELETE processed (gray) underlines by id. MODIFIES the Skim document.
Only run after the reader has explicitly approved clearing these ids. Deletes exactly the ids passed, and only if they are gray underlines: pending (red) underlines, highlights and other markup are skipped.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Ids of gray underlines to delete. | |
| document_path | No | Path of an open document. Default: the front document. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, so safety is covered; the description adds genuinely new behavior beyond them — permanent, irreversible deletion and a filtering rule that silently skips non-gray ids. It does not describe how skipped ids are reported back, keeping it at 4.
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 short paragraphs with zero filler; the destructive nature and the precondition are both front-loaded before the detail.
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 destructive, low-parameter mutation with full schema coverage, complete annotations, and an output schema, the description supplies everything else an agent needs: irreversibility, the approval precondition, and the id-filtering rule.
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 coverage is 100%, so the baseline is 3, but the description adds real meaning: the ids are validated against gray-underline status and are the exact set deleted. It also clarifies document scope ('the Skim document') beyond the schema's default-to-front-document note.
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?
States a specific verb (PERMANENTLY DELETE) and resource (processed/gray underlines) with an explicit scope qualifier (by id) and notes it MODIFIES the Skim document. This cleanly separates it from read-only siblings like get_pending_underlines and the non-destructive mark_processed.
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?
Gives a clear precondition ('Only run after the reader has explicitly approved clearing these ids') and a when-not rule ('pending (red) underlines, highlights and other markup are skipped'). It does not explicitly route to a sibling alternative, so it falls just short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_open_documentsList open Skim documentsARead-onlyIdempotent
List the PDFs open in Skim, frontmost first: name, path, page count, current page.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds real behavioral context beyond that: the ordering guarantee ('frontmost first') and the exact fields returned including 'current page'.
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?
One sentence, front-loaded with the action and resource, and the field enumeration is a compact literal list. No wasted words.
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?
A zero-parameter read tool with a full output schema and complete annotations needs very little prose; the description covers what is listed, the ordering, and the source application. An agent has everything required to call it 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 tool takes zero parameters, so the baseline is 4. Nothing in the description needs to explain inputs, and the listed fields describe the output rather than a parameter.
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?
Specific verb and resource ('List the PDFs open in Skim') with the result set enumerated, so the agent knows exactly what comes back. It does not differentiate itself from the sibling get_reading_context, which is the one tool an agent could plausibly confuse it with.
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?
Usage is implied by the resource description (call this when you need the currently open PDFs), but the description gives no explicit when-to-use, prerequisites, or alternative such as get_reading_context for reading state.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_pending_underlinesGet pending underlinesARead-onlyIdempotent
List pending (red) underlines of a document in reading order, with quoted text.
| Name | Required | Description | Default |
|---|---|---|---|
| document_path | No | Path of an open document. Default: the front document. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful return-shape context (reading order, quoted text) but says nothing about pagination, scope limits, or what 'pending' means operationally.
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?
A single front-loaded sentence naming the resource, ordering, and payload; every clause earns its place and there is no filler.
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 one-optional-parameter read-only tool with a declared output schema and full annotation coverage, the definition supplies enough to invoke it correctly. The only gap is disambiguation from get_reading_context, which the description leaves unaddressed.
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 document_path and its 'default: the front document' behavior are fully documented in the schema. The description only alludes to 'a document' and adds no syntax or fallback detail beyond what the schema already provides, so baseline 3 applies.
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 states a specific verb and resource ('List pending (red) underlines of a document') plus two extra qualifiers: reading order and quoted text. This clearly separates it from the mutating siblings mark_processed and clear_processed, though it never explicitly contrasts itself with get_reading_context.
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?
Usage is only implied: 'pending' suggests items not yet processed, which loosely links to the mark_processed/clear_processed siblings. There is no explicit statement of when to call this versus get_reading_context, nor any prerequisite or exclusion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reading_contextGet reading contextARead-onlyIdempotent
Everything about what the reader is looking at, in one call.
Returns the front document (name, path), its current page, the live text selection (explicitly empty when nothing is selected), pending red underlines with their quoted text, page, color and comment, the page's text layer, and a PNG of the page with the selection (blue, only when it is on the rendered page) and the pending underlines on that page (red) outlined. The image is the ground truth for math; the text layer may garble formulas. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page to render. Default: the selection's page, else the current page. | |
| document_path | No | Path of an open document (see get_open_documents). Default: front. | |
| include_image | No | Set false to skip rendering the page PNG. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and idempotentHint=true, so safety is covered. The description adds real value beyond that: the selection is 'explicitly empty when nothing is selected', the image is 'ground truth for math' while 'the text layer may garble formulas', and the blue/red outline semantics are disclosed.
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?
Front-loaded with a one-line summary, then a structured enumeration of return contents. Dense but every clause carries information; no filler sentences. Slightly long but justified by the breadth of returned data.
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?
With no output schema, the description carries the full burden of describing return values, and it does so thoroughly: document name/path, page, selection, underlines with quoted text/page/color/comment, text layer, and PNG outlining. An agent knows exactly what to expect from a call.
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 all three parameters (page, document_path, include_image) are already documented with defaults, including the fallback logic for page and document_path. The description adds nothing parameter-specific, so the baseline 3 applies.
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?
States a specific resource (reading context) and enumerates its contents: front document, current page, selection, pending underlines, text layer, and PNG. This implicitly separates it from the narrow siblings like get_pending_underlines and get_open_documents, though it never names them directly.
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 opening phrase 'Everything about what the reader is looking at, in one call' implies this is the comprehensive aggregator, which hints at when to prefer it over the narrower siblings. However, it never explicitly states when to use this vs. get_pending_underlines or get_open_documents, nor any exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_processedMark underlines processedAIdempotent
Recolor pending (red) underlines gray by id. MODIFIES the reader's Skim document.
Only run after the reader has approved. Highlights and underlines of other colors are skipped, never changed. Nothing is deleted.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Underline ids from get_pending_underlines or get_reading_context. | |
| document_path | No | Path of an open document. Default: the front document. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare it is a non-readonly, idempotent, non-destructive write; the description goes further by scoping the mutation (only pending red underlines, other colors skipped, never changed) and explicitly stating nothing is deleted. These details materially reduce the risk of unintended edits beyond what the annotations convey.
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?
Three short sentences, front-loaded with the action and the side-effect warning before the precondition. Every sentence carries distinct information with no padding.
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?
With rich annotations, a full input schema, and an output schema present, the description supplies exactly what structured fields cannot: the approval prerequisite, mutation scope, and non-deletion guarantee. Nothing needed to invoke it safely 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?
Schema coverage is 100%, so both parameters (ids, document_path) are already documented, including the source of ids and the default front-document behavior. The description's "by id" adds no syntax or format detail beyond the schema, so the baseline 3 applies.
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?
States a specific verb and resource: recolor pending (red) underlines to gray by id. It is clearly distinguishable from siblings get_pending_underlines (read) and clear_processed (removal), and the title reinforces rather than restates the operation.
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?
"Only run after the reader has approved" gives an explicit precondition for invocation, which is meaningful for a mutation tool. It does not, however, name sibling alternatives or explain the relationship to clear_processed, leaving some routing inference to the agent.
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.
5 tool updates
v0.1.0- First observed
clear_processed - First observed
get_open_documents - First observed
get_pending_underlines - First observed
get_reading_context - First observed
mark_processed
TDQS
Scored across 5 tools
The mutation tools (mark_processed, clear_processed) and their read counterparts are clearly distinct. However, get_reading_context already bundles the front document, its pending underlines, and page info, which overlaps with get_pending_underlines and get_open_documents, so an agent may be unsure when to call the narrow getters versus the aggregate.
All names use snake_case with a verb-first, noun-object pattern (get_/mark_/clear_ + resource). The verb variation is semantically meaningful (reads vs. state mutations) rather than inconsistent.
Five tools is well-scoped for a Skim reading-support server: two read getters, one aggregate context call, and two annotation-state mutations. Every tool earns its place with no filler.
The pending-red → gray → deleted lifecycle is fully covered, plus document listing and rich reading context. The main gap is the absence of an inverse to mark_processed (no way to revert gray back to red) or any annotation-creation ability, though the core workflow is complete.
Related MCP Connectors
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
MCP connector that lets ChatGPT list, search, and run your Apple Shortcuts via a local Mac agent
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Query your own PDFs and documents from any MCP client. Every answer cites the page it came from.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceGives ChatGPT Web hands on your Mac — files, git, terminal, browser, and windows via MCP, with outbound-only tunnel and delete protection.1MIT
- AlicenseNot gradedqualityDmaintenanceEnables ChatGPT to securely control a local workstation via an MCP tunnel, exposing 44 tools for file/project editing, git, process supervision, browser automation, and Office document handling across macOS, Linux, and Windows.7MIT
- AlicenseAqualityBmaintenanceEnables ChatGPT web to use local tools like file reading, command execution, and patch application through an MCP server over OpenAI Secure MCP Tunnel.62MIT
- AlicenseNot gradedqualityBmaintenanceEnables ChatGPT to read and write local files, view the screen, and operate Windows desktop windows, mouse, and keyboard through MCP stdio and OpenAI's Secure MCP Tunnel.7MIT