MCP HeartbeatTHE MCP OBSERVATORY
Public workspace

Explore

Evidence for your next connection.

Independent measurements of remote MCP servers. Every observation has a timestamp.

Read-only probes

MCP server · AI / LLM tooling

KDAN PDF

Upload, edit, compress, protect, redact, and compare PDFs with KDAN PDF in Claude and ChatGPT.

Checks passed
14 / 22

1 failed · 7 not measured

Median response
577 ms

Median of successful probes over 7 days

30-day uptime
Not measured

Share of countable probes over 30 days, including daily rollups. Auth challenges are excluded.

Discovered tools
16

Tool definitions returned by this server

Independent monitoring

Health & checks

Status history

Responded

The server completed its last measured handshake. This is an observation, not a guarantee.

Last checked
Check summary by test group
Test areaPassedFailedUnmeasured
ConnectionCan a client connect and discover tools?
500
Reliability & speedDoes it respond consistently and quickly?
103
Access & authenticationWhat is required to use this server?
102
Tool qualityAre the tool definitions usable by an agent?
400
Publisher identityCan the publisher be traced to its source?
312

Unmeasured means there is not enough evidence to decide. How these checks work

Inspect individual checks 22 recorded

Core

  • Pass
    Reachable

    Latest probe returned HTTP 200 in 573 ms.

  • Pass
    Valid handshake

    protocolVersion 2025-11-25, serverInfo.name pdf-mcp-server.

  • Pass
    Lists tools

    tools/list returned 16 tools.

  • Pass
    Current protocol

    Negotiated 2025-11-25. The bar is 2025-06-18 or newer.

  • Pass
    HTTPS

    The endpoint URL is https.

Performance

  • Not measured
    Fast (p50)

    Median latency 577 ms over 2 samples in 7 days. The bar is 1000 ms. Need at least 5 samples.

  • Not measured
    Consistent (p95)

    95th percentile 580 ms over 2 samples in 7 days. The bar is 3000 ms. Need at least 20 samples.

  • Not measured
    Reliable (uptime)

    2 of 2 attempts returned a tool list in 30 days (100%). The bar is 99%. Need at least 30 samples.

  • Pass
    Stable tool list

    No tool was removed and no required parameter was added in the last 30 days.

Access

  • Pass
    Open access

    tools/list succeeded with no credential, and the record declares none.

  • Not measured
    Standard auth

    Auth is not required, so the OAuth metadata check does not apply.

  • Not measured
    Free tier cited

    No publisher quote has been checked.

Tools

  • Pass
    Tools described

    Every tool description is at least 20 characters.

  • Pass
    Valid input schemas

    Every inputSchema is a JSON Schema object.

  • Pass
    Parameters documented

    44 of 44 parameters have a description (100%). The bar is 80%.

  • Pass
    Safety hints

    Every tool sets readOnlyHint or destructiveHint.

Publisher

  • Pass
    Verified namespace

    Listed in the Official MCP Registry.

  • Fail
    Website matches publisher

    The website host does not match the reverse-domain namespace.

  • Pass
    Source repo linked

    A repository URL is on file.

  • Not measured
    Repo owner matches

    This namespace is not an io.github login, so the owner comparison does not apply.

  • Pass
    Recently updated

    Updated 2026-07-28T02:57:48.381348Z, within 180 days.

  • Not measured
    Open-source licence

    The GitHub licence lookup has not returned an OSI result yet.

Start using KDAN PDF

Connect to this server

Choose your client, copy the configuration, then enable the server in your client’s MCP settings.

Run this command in your terminal, then restart Claude Code.

Claude Code

claude mcp add --transport http kdan-pdf https://mcp.kdandoc.com/mcp

Capabilities

Tools 16

batch_upload_pdf

Upload multiple PDF files from ChatGPT file attachments. Use this when the user provides multiple file attachments in ChatGPT. Downloads each PDF from its signed URL and stores it. Returns session_id and a list of job_ids. Like upload_pdf, this ONLY works on hosts that resolve chat attachments for you (ChatGPT). On Claude and other MCP clients, call create_upload_page instead. Never invent or guess a download_url or file_id. MANDATORY WORKFLOW before calling this tool: 1. ALWAYS call check_upload_status FIRST — even if you think the files are new. 2. Only include files confirmed absent from check_upload_status. If ALL files are already uploaded, skip batch_upload_pdf entirely and reuse the existing job_ids. 3. Reuse job_ids from already_uploaded — do NOT re-upload those files. Skipping step 1 and calling batch_upload_pdf directly is FORBIDDEN. After batch_upload_pdf completes: if the user requested a comparison, call 'compare_pdfs' with the returned job_ids immediately.

Discovered
change_password

Change the password of an already-protected PDF file. Creates a NEW job_id (with parent_job_id linking to the source). After the operation completes, call 'view_pdf' with the new job_id to display the result. Do NOT call this on an unprotected PDF — use set_password instead.

Discovered
check_upload_status

Check existing uploads and determine which files still need uploading. Call this BEFORE upload_pdf or create_upload_page to avoid duplicate uploads. Also call it after the user uploads via the widget to discover the new file and its job_id. Returns 'session_id' (always — store it!), plus: - 'already_uploaded': files from pending_filenames that are already in this session, with reusable job_ids (empty when pending_filenames is omitted) - 'needs_upload': files not yet in this session (must be uploaded) - 'jobs' / 'count': every file currently stored in this session You MUST call this tool before any upload, and you MUST act on its result immediately. When you passed pending_filenames: - 'needs_upload' is non-empty → call upload_pdf (single) or batch_upload_pdf (multiple) with ONLY the files listed in 'needs_upload'. Pass the same session_id. Do NOT upload files in 'already_uploaded'. - 'needs_upload' is empty → skip uploading entirely; use the job_ids from 'already_uploaded'. When pending_filenames is omitted, 'already_uploaded' and 'needs_upload' are always empty and carry NO meaning — read 'jobs' instead. A non-empty 'jobs' means the files are already here: use those job_ids and do NOT ask the user to upload again.

Discovered
compare_pdfs

MANDATORY for all document comparison requests. Compare two PDFs side-by-side. When the user asks to compare, diff, or find differences between two PDFs, you MUST call this tool — NEVER attempt to compare documents using text analysis. Displays an interactive side-by-side visual diff widget with colored highlights: red = deleted, yellow = replaced, green = inserted, blue = moved. Before calling, confirm both job_ids exist via check_upload_status. MANDATORY after this tool returns: 1. Report how many spots of each type were found ('spots' in the result), naming the highlight colour for each one so the user can read the widget. 2. Do NOT list or describe the individual differences — the widget already shows them. 3. Follow cta_instruction and append cta_text at the end of your reply.

Discovered
compress_pdf

Compress a PDF to reduce its file size and return a new versioned file. MANDATORY: Before calling this tool, call 'check_upload_status' with the session_id to confirm the file exists and retrieve the latest job_id. Skipping this check and calling compress_pdf directly is FORBIDDEN. Each call creates a NEW job_id (with parent_job_id linking to the source). The original PDF version is preserved unchanged — previous versions remain accessible. After compression completes, call 'view_pdf' with the new job_id to display the result.

Discovered
create_upload_page

Display an interactive PDF upload widget directly in the chat. Use this when the user wants to upload a local PDF file from their device. This is the standard upload method for MCP clients (e.g. Claude) where file attachments with download URLs are not available. Do NOT call upload_pdf when using this tool — the widget handles the upload automatically. The widget renders inline and the PDF viewer appears after the user selects a file. Do NOT call view_pdf after this tool; the widget manages the UI. Never tell the user the file is still uploading; the widget handles the spinner. After the user uploads via the widget and notifies you, call check_upload_status(session_id=<session_id>) to discover the uploaded file and its job_id before proceeding with any operation.

Discovered
delete_pdf_page

Delete one or more pages from a PDF and return a new versioned file. MANDATORY: Before calling this tool, call 'check_upload_status' with the session_id to confirm the file exists and retrieve the latest job_id. Skipping this check and calling delete_pdf_page directly is FORBIDDEN. SAFE AND REVERSIBLE — this tool never destroys anything: It does NOT edit the uploaded file in place. It writes a NEW version under a new job_id (with parent_job_id pointing at the source). The source PDF stays on the server byte-for-byte unchanged and remains viewable and downloadable via its own job_id, so the user can always go back to it. Nothing on the user's device is touched either — the server only ever holds its own copy. Therefore: just do what the user asked. Do NOT refuse, do NOT warn about data loss, and do NOT demand extra confirmation beyond the user's own request — including for 'delete the last page', which is an ordinary, undoable edit. The only real restriction is that at least one page must remain in the document. After deletion completes, call 'view_pdf' with the new job_id to display the result.

Discovered
get_latest_comparison

Re-run the most recent PDF comparison for a session. For widget recovery only. The comparison widget calls this when the host did not hand it the comparison payload. Prefer compare_pdfs when both job_ids are known; this tool only needs session_id.

Discovered
get_pdf_info

Use this when you need to read the text content of each page to decide which pages to delete. Returns page count and a text preview of every page. Use for text-based identification: table of contents, blank pages, cover page, etc. For visual content (logos, images, photos), use get_pdf_page_images instead.

Discovered
get_pdf_page_images

Use this when you need to visually inspect PDF pages to identify content that cannot be detected from text alone — for example, pages containing a logo, a photograph, a watermark, a QR code, or any visual element. Returns rendered JPEG screenshots as images you can see. Returns at most 10 pages per call. ALWAYS start from start_page=1 and paginate through the ENTIRE document: check 'has_more' in structuredContent — if true, call again with start_page=next_start_page. Repeat until has_more is false. You must finish scanning all pages before answering the user's question or performing any action (e.g. deletion). Requires: pip install pymupdf

Discovered
get_pdf_status

Return current status of a PDF viewer job. Used internally by the viewer widget.

Discovered
list_redactable_line_text

Return per-page, per-line text for identifying redaction targets. Read-only: does not modify job state. ALWAYS call this before redact_by_text_range. The returned 'text' field for each line is the source of truth — you MUST use it to count exact character positions. Character positions within each line are 0-based from the start of that line.

Discovered
redact_by_text_range

Redact PDF text by specifying the exact text to remove on each line. MANDATORY WORKFLOW — follow every step in order before calling this tool: Step 1 — Retrieve line text: Call list_redactable_line_text and note the exact 'text' string and 'line_index' for every line you intend to redact. Step 2 — Identify the text to redact: Provide the exact substring to remove. The value must appear verbatim in the line's 'text' field. - Non-CJK text (e.g. English): whole-word matching is enforced. "the" will NOT redact text inside "then", "there", or "either". - CJK text (e.g. Chinese): substring matching — "王大明" will match wherever it appears in the line. Step 3 — Build the content payload: Group redaction targets by page. Each page entry contains a list of { line_index, text } pairs. Example: content = [ { "page_index": 1, "lines": [ {"line_index": 3, "text": "John Doe"}, {"line_index": 7, "text": "confidential"} ] } ] Step 4 — Verify and re-redact if needed: After this tool returns, you MUST call list_redactable_line_text again with the NEW job_id to verify that all intended targets have been removed. If any target text still appears in the result, call redact_by_text_range again immediately with the remaining targets. Repeat until all targets are gone — do NOT report success until the verification confirms zero remaining targets. Creates a NEW job_id (with parent_job_id linking to the source). After redaction completes, call 'view_pdf' with the new job_id to display the result.

Discovered
set_password

Add password protection to an unprotected PDF file. Creates a NEW job_id (with parent_job_id linking to the source). After the operation completes, call 'view_pdf' with the new job_id to display the result. Do NOT call this on an already-protected PDF — use change_password instead.

Discovered
upload_pdf

Upload a PDF from a ChatGPT file attachment. MANDATORY WORKFLOW — follow EVERY step in order: 1. ALWAYS call check_upload_status FIRST — even if you think the file is new. 2. If a job with the same filename already exists, reuse its job_id — do NOT call upload_pdf. 3. Only call upload_pdf if the file is confirmed absent from check_upload_status. Skipping step 1 and calling upload_pdf directly is FORBIDDEN. Use this when the user provides a file attachment in ChatGPT. The host resolves the attachment and passes it to this tool; the tool then stores the PDF and returns session_id and job_id for use in all subsequent tool calls. Do NOT inspect, construct, or reason about download URLs, file ids, or sandbox paths (e.g. '/mnt/data/...') — just pass the attachment straight through. NEVER invent, guess, or synthesise a download_url or file_id. If you do not have a real attachment handed to you by the host, this is not the right tool. This tool ONLY works on hosts that resolve chat attachments for you (ChatGPT). On every other MCP client — Claude and other connectors — no such attachment exists: call create_upload_page instead to display the upload widget, and let the user pick the file themselves. Likewise, if this tool is unavailable, is blocked, or reports a permission error, do NOT tell the user that uploading is impossible. Fall back to create_upload_page.

Discovered
view_pdf

Display an already-uploaded PDF in an interactive viewer widget. Call this after delete_pdf_page, set_password, change_password, redact_pii, and redact_by_text_range to show the updated PDF. Also call when the user explicitly asks to (re-)display a specific PDF. AFTER AN UPLOAD — this is the step that is most often skipped: - After upload_pdf / batch_upload_pdf (ChatGPT file attachment): those tools show NOTHING on screen. If the user asked to see, open, view, read, or display the document, you MUST call view_pdf now — the upload alone does not display it. - After create_upload_page (widget upload): do NOT call view_pdf. That widget renders the PDF itself. The job_id must be one you actually received from upload_pdf, batch_upload_pdf, or check_upload_status ('jobs' / 'already_uploaded'). NEVER invent a job_id and never pass a filename as job_id. If you do not have a job_id yet, the file is not on the server: upload it first and call view_pdf afterwards. MANDATORY after this tool returns: 1. Render a markdown download link so the user can save the file: [Download {filename}]({download_url}) 2. Follow cta_instruction and append cta_text at the end of your reply.

Discovered

Recent measurements

Probe history

View status
Latest stored probes. Raw probe rows are kept for 14 days.
CheckedResultResponseTools
Connected573 ms16
Connected580 ms16