pdfwithlove for AI agents

This site has no HTTP API, no upload endpoint and no server-side job to POST a file to - every tool runs in the visitor's browser, which is the point of the site and also the reason an agent could not use it. WebMCP closes that in the only place the work can happen: inside the page. Open any page here in a browser that bridges WebMCP and 10 tools appear, covering 60 of the 63 operations.

Connecting

Two routes, same ten tools.

// No WebMCP bridge? The same ten tools, from plain JavaScript.
window.__pdfwithlove.describe();            // the ten schemas

await window.__pdfwithlove.call('attach_file', {
  name: 'contract.pdf', base64: '<...>'   // or: call('open_file_picker')
});
await window.__pdfwithlove.call('run', { tool: 'compress' });
await window.__pdfwithlove.call('save_result', {});

A machine-readable copy of every schema below is served at /agents/tools.json, and the site's summary for models is at /llms.txt.

The typical run

list_toolsdescribe_toolopen_file_pickerrunsave_result

open_file_picker is the efficient way to hand over a real file - it opens the chooser and attaches what is selected, with no base64 crossing the boundary. attach_file takes base64 for small inputs you already hold. Results come back through save_result (a download) or get_result (base64, for small outputs).

The 10 tools

pdfwithlove_list_tools

List the PDF and image operations this site can perform, with the id to pass to pdfwithlove_run. Start here. Optionally filter by category or a search term.

  • category string one of Organize, Optimize, Convert, Edit, Security, Image
  • query string - Free-text filter over titles and descriptions.

pdfwithlove_describe_tool

Describe one operation: how many input files it needs, what it accepts, and every option it takes with its default. Call this before pdfwithlove_run rather than guessing option names.

  • tool string required - Tool id from pdfwithlove_list_tools, e.g. "pdf_to_markdown".

pdfwithlove_attach_file

Attach a file for the operations to work on, as base64. Suitable for small inputs; for anything over a megabyte or so use pdfwithlove_open_file_picker instead, which takes the file without moving its bytes through this conversation.

  • name string required - Filename including extension - the extension is how engines recognise the format.
  • base64 string required - File contents, base64. A data: URL is also accepted.

pdfwithlove_open_file_picker

Open the browser file chooser and attach whatever is selected. This is the efficient way to hand over a real file: call it, then satisfy the file chooser with your browser-automation upload command. Resolves once files are chosen.

  • multiple boolean - Allow selecting more than one file. Default true.

pdfwithlove_list_files

List the files currently attached, with the ids that pdfwithlove_run takes. Pass clear:true to drop them all first.

  • clear boolean - Remove every attached file. Default false.

pdfwithlove_inspect_pdf

Read an attached PDF without changing it: page count, page size, metadata, form fields, and whether it has extractable text or is a scan. Worth calling before choosing page ranges or deciding whether OCR is needed.

  • fileId string - Which attached file. Defaults to the first one.

pdfwithlove_run

Run one operation on the attached files, in this browser. Returns a summary plus any text the operation produced; a resulting file is held in memory for pdfwithlove_get_result or pdfwithlove_save_result. Nothing is uploaded - the work happens locally, so a large document can take a while.

  • tool string required - Tool id, e.g. "merge", "compress", "pdf_to_markdown".
  • options object - Operation options. Call pdfwithlove_describe_tool for the exact shape.
  • text string - Text input, for the operations that take typed text instead of a file.
  • fileIds array - Which attached files to use, in order. Defaults to all of them.

pdfwithlove_get_result

Return the last result. Text results come back in full; a file comes back base64, which is expensive - prefer pdfwithlove_save_result unless you genuinely need the bytes in the conversation.

  • maxBytes number - Refuse a file larger than this rather than flooding the context. Default 750000.

pdfwithlove_save_result

Download the last result through the browser, the same way the Download button does. Use this for anything of real size; the file lands wherever the browser saves downloads.

  • filename string - Override the suggested filename. Optional.

pdfwithlove_open_tool

Navigate the visible page to a tool, its directory, or the home page - for when a person is watching, or when you want to finish a job in the on-screen editor. This only changes the view; it runs nothing.

  • tool string required - A tool id, or "all" for the tool directory, or "home".

The 63 operations

Ids to pass as run({ tool }). Call list_tools for this list at runtime and describe_tool for one tool's options rather than guessing option names.

Organize - merge, split, organize_pages, rotate, extract_pages, delete_pages, create_pdf, pages_per_sheet, change_page_size, halve_pages, scan_to_pdf (browser UI only)

Optimize - compress, flatten_pdf, repair_pdf

Convert - pdf_to_pdfa, pdf_to_word, pdf_to_excel, pdf_to_ppt, pdf_to_markdown, word_to_pdf, excel_to_pdf, images_to_pdf, html_to_pdf, pdf_to_image, ocr_pdf, pdf_color_convert, pdf_to_text, text_to_pdf, markdown_to_pdf, extract_images, pdf_to_html, pdf_to_epub, svg_to_pdf

Edit - edit_pdf (browser UI only), sign_pdf (browser UI only), watermark, page_numbers, pdf_metadata, redact_pdf, pdf_forms, compare_pdf, crop_pdf, overlay_pdf, viewer_prefs, bookmark_pdf, annotate_pdf

Security - protect_pdf, unlock_pdf, remove_metadata, password_generator

Image - compress_image, resize_image, crop_image, convert_to_jpg, convert_from_jpg, photo_editor, upscale_image, remove_background, watermark_image, meme_generator, rotate_image, html_to_image, blur_face

What cannot run headlessly

3 of the 63 are direct-manipulation canvases. run refuses them with the reason and a URL rather than an unknown-tool error, so an agent can fall back to driving the page through the DOM.

Before you call anything