Skip to main content
Glama
README.md
# pdfporter

Files in, PDFs out — and PDFs reshaped. pdfporter is a
client for the conv2pdf conversion service: it takes the
path of a file on your computer, sends it for conversion,
and writes the result back next to the original. Office
documents, spreadsheets, presentations, images, e-books,
PostScript and HTML files become PDFs; PDFs become Word
documents or per-page images; and PDFs can be merged,
split, page-extracted, rotated, numbered, watermarked,
compressed, password-protected and unlocked.

## Configuration

- `CONV2PDF_API_KEY` (required for calls) — created in
  the conv2pdf dashboard, Developer tab. It is sent on
  every request as `Authorization: Bearer <key>`. Without
  a key the server still starts and lists its tools; any
  tool call fails with a message explaining how to set
  the key.
- `CONV2PDF_API_URL` (optional) — API base URL. Defaults
  to `https://api.conv2pdf.com/v1`.
- `CONV2PDF_TIMEOUT_MS` (optional) — per-request timeout
  in milliseconds. Defaults to `120000`, because a
  conversion may legitimately take up to about a minute.

## Tools

- `convert_to_pdf` — convert a file to PDF, chosen from
  its extension: Word, Excel, PowerPoint, OpenDocument,
  Apple Pages, RTF, TXT and CSV files go through
  office-to-pdf; PNG, JPG, WebP, GIF and TIFF images
  through image-to-pdf; HEIC/HEIF photos through
  heic-to-pdf; EPUB through epub-to-pdf; PS/EPS through
  ps-to-pdf; HTML through html-to-pdf. An unknown
  extension fails locally, with no request made.
- `convert_url_to_pdf` — render a public web page to
  PDF from its address. It always takes an
  `output_path`, because there is no input file to write
  next to.
- `convert_heic_to_jpg` — an iPhone HEIC or HEIF photo
  as a JPG.
- `convert_pdf_to_word` — a PDF with real text as an
  editable DOCX document. Pass `password` to open a
  protected PDF.
- `convert_pdf_to_images` — one PNG (default) or JPG
  per page, delivered in a ZIP file.
- `merge_pdfs` — combine 2 to 20 PDFs into one, in the
  order given in `paths`.
- `extract_pdf_pages` — copy the pages listed in
  `ranges` (such as `1-5,7,10-12`) into a new PDF, in
  the order written.
- `split_pdf` — one PDF per page, in a ZIP file.
- `rotate_pdf` — turn every page by `rotation`: 90, 180
  or 270 degrees.
- `add_page_numbers` — number the pages, as "3 / 12"
  (`format: "full"`, the default) or "3"
  (`format: "simple"`), at `position` bottom-center
  (default), bottom-left or bottom-right.
- `add_watermark` — stamp a `text` of up to 50
  characters on every page.
- `compress_pdf` — reduce the size, with `quality`
  low, medium (default) or high, keeping images at 72,
  150 or 300 DPI.
- `protect_pdf` — set a new `password` (6 to 64
  characters), with `prevent_print` and `prevent_copy`
  to forbid printing or copying.
- `unlock_pdf` — remove a password you know (pass it as
  `password`), or the printing and copying restrictions.
- `get_quota` — the plan, usage and limits of the API
  key, returned verbatim.

Most PDF-reading tools accept an optional `password`
that opens a protected PDF; the result has no password.

## How a conversion behaves

1. The input file is read locally and POSTed as
   multipart/form-data to `<base>/convert/<api-tool>`,
   the file in the `file` field plus the tool's fields
   as string fields.
2. On HTTP 429 or 503 the server waits for the delay
   the API gives (the `Retry-After` header, else the
   body's `retry_after` seconds) and retries, at most
   twice and only for delays of 30 seconds or less.
3. The convert response carries a `job_id` and a
   relative `download_url`; the result is downloaded
   from the API origin, written to disk, and the job is
   then deleted from the service on a best-effort basis.

Results are never overwritten. By default the output
sits next to the input: a conversion swaps the
extension (`report.docx` gives `report.pdf`), a PDF
operation adds a suffix (`report.pdf` gives
`report-compressed.pdf` for `compress_pdf`,
`report-images.zip` for `convert_pdf_to_images`, and so
on), and a merge writes `merged.pdf` next to the first
input. When the target name is taken, ` (2)`, ` (3)`,
... is appended before the extension until a free name
is found. With an explicit `output_path`, an existing
file there is refused before anything is sent.

A successful conversion answers with a JSON summary:
`{"job_id": ..., "output_path": ..., "size_bytes": ...}`,
plus the `quota` object copied from the convert
response when the service sends one. `get_quota`
returns the service's response body verbatim.

Errors: a non-2xx reply fails with
`conv2pdf error: <error>` when the body carries an
`error` code, else `conv2pdf error: HTTP <status>`; an
unreachable API fails with `conv2pdf unreachable`.

## CLI

Each tool is a subcommand taking one JSON argument
string:

```bash
npx @reneturcios/pdfporter convert_to_pdf '{"path":"report.docx"}'
npx @reneturcios/pdfporter convert_url_to_pdf '{"url":"https://example.com/article","output_path":"article.pdf"}'
npx @reneturcios/pdfporter convert_heic_to_jpg '{"path":"IMG_0001.heic"}'
npx @reneturcios/pdfporter convert_pdf_to_word '{"path":"report.pdf"}'
npx @reneturcios/pdfporter convert_pdf_to_images '{"path":"report.pdf","format":"jpg"}'
npx @reneturcios/pdfporter merge_pdfs '{"paths":["a.pdf","b.pdf"]}'
npx @reneturcios/pdfporter extract_pdf_pages '{"path":"report.pdf","ranges":"1-5,7,10-12"}'
npx @reneturcios/pdfporter split_pdf '{"path":"report.pdf"}'
npx @reneturcios/pdfporter rotate_pdf '{"path":"scan.pdf","rotation":90}'
npx @reneturcios/pdfporter add_page_numbers '{"path":"report.pdf","position":"bottom-right"}'
npx @reneturcios/pdfporter add_watermark '{"path":"contract.pdf","text":"CONFIDENTIAL"}'
npx @reneturcios/pdfporter compress_pdf '{"path":"big.pdf","quality":"low"}'
npx @reneturcios/pdfporter protect_pdf '{"path":"confidential.pdf","password":"secret123","prevent_print":true}'
npx @reneturcios/pdfporter unlock_pdf '{"path":"protected.pdf","password":"secret123"}'
npx @reneturcios/pdfporter get_quota '{}'
npx @reneturcios/pdfporter --version
```

Exit codes: `0` on success, `1` on a runtime error,
`2` on a usage error.

## MCP server

```bash
npx -y @reneturcios/pdfporter mcp
```

MCP client config:

```json
{
  "mcpServers": {
    "pdfporter": {
      "command": "npx",
      "args": ["-y", "@reneturcios/pdfporter", "mcp"],
      "env": {
        "CONV2PDF_API_KEY": "<your key>"
      }
    }
  }
}
```

## Docker

```bash
docker run -i ghcr.io/cloudcorprecords/pdfporter
docker run --rm ghcr.io/cloudcorprecords/pdfporter get_quota '{}'
```

## Development

```bash
npm install
npm run build   # tsc -> dist/
npm test        # tsx --test test/*.test.ts (fake fetch only, no network)
npm run dev     # run the CLI from source
```

## License

MIT — Copyright (c) 2026 Rene Turcios