trackmcp
Back to directory
baruchiro

paperless-mcp

View on GitHub

An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.

139 stars TypeScriptOthers Updated Aug 30, 2026
aidocument-managementmcpmcp-serverpaperless-ngx

Documentation

Paperless-NGX MCP Server

CodeRabbit Pull Request Reviews

An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.

Quick Start

Install MCP Server

Installation

Add these to your MCP config file:

// STDIO mode (recommended for local or CLI use)

json
"paperless": {
  "command": "npx",
  "args": [
    "-y",
    "@baruchiro/paperless-mcp@latest",
  ],
  "env": {
    "PAPERLESS_URL": "http://your-paperless-instance:8000",
    "PAPERLESS_API_KEY": "your-api-token",
    "PAPERLESS_PUBLIC_URL": "https://your-public-domain.com"
  }
}

// HTTP mode (recommended for Docker or remote use)

json
"paperless": {
  "command": "docker",
  "args": [
    "run",
    "-i",
    "--rm",
    "ghcr.io/baruchiro/paperless-mcp:latest",
  ],
  "env": {
    "PAPERLESS_URL": "http://your-paperless-instance:8000",
    "PAPERLESS_API_KEY": "your-api-token",
    "PAPERLESS_PUBLIC_URL": "https://your-public-domain.com"
  }
}

3. Get your API token:

1. Log into your Paperless-NGX instance

2. Click your username in the top right

3. Select "My Profile"

4. Click the circular arrow button to generate a new token

4. Replace the placeholders in your MCP config:

    Environment Variables

    VariableRequiredDefaultDescription
    `PAPERLESS_URL`YesBase URL of your Paperless-NGX instance
    `PAPERLESS_API_KEY`YesAPI token from your Paperless-NGX profile
    `PAPERLESS_PUBLIC_URL`No`PAPERLESS_URL`Public-facing URL for document links
    `PAPERLESS_API_VERSION`No`9`Paperless-ngx REST API version. `9` works on Paperless-ngx v2.x (recent) and v3.x. Paperless-ngx v3.0.0 dropped support for versions below `9`, so older defaults now return HTTP 406. If you see HTTP 406 errors, set this to a version your server supports.
    `PAPERLESS_MCP_UPLOAD_PATHS`NoColon-separated list of allowed directories for `file_path` uploads. Recommended for security. Example: `/var/uploads:/tmp/scans`

    That's it! Now you can ask Claude to help you manage your Paperless-NGX documents.

    Example Usage

    Here are some things you can ask Claude to do:

    • "Show me all documents tagged as 'Invoice'"
    • "Search for documents containing 'tax return'"
    • "Create a new tag called 'Receipts' with color #FF0000"
    • "Download document #123"
    • "List all correspondents"
    • "Create a new document type called 'Bank Statement'"

    Available Tools

    Document Operations

    list_documents

    Get a paginated list of documents with simple filters. Use this for straightforward listing tasks. For full-text queries, structured custom field filtering, or advanced Paperless filters, use `query_documents`.

    Parameters:

    • page (optional): Page number
    • page_size (optional): Number of documents per page
    • search (optional): Simple Paperless search term
    • correspondent (optional): Correspondent ID
    • document_type (optional): Document type ID
    • tag (optional): Tag ID
    • storage_path (optional): Storage path ID
    • created__date__gte (optional): Created date on or after YYYY-MM-DD
    • created__date__lte (optional): Created date on or before YYYY-MM-DD
    • ordering (optional): Paperless ordering field
    • archive_serial_number (optional): Archive serial number
    • archive_serial_number__isnull (optional): Whether the archive serial number is empty
    • custom_field_query (optional): Raw JSON-encoded Paperless custom field query string
    • custom_fields__icontains (optional): Case-insensitive substring match across custom field values
    typescript
    list_documents({
      page: 1,
      page_size: 25
    })

    query_documents

    Canonical document query tool. Supports full-text querying, simple Paperless search, custom field filters, and documented `/api/documents/` Paperless query parameters.

    Parameters:

    • page (optional): Page number
    • page_size (optional): Number of documents per page
    • ordering (optional): Paperless ordering field
    • query (optional): Full-text query string
    • search (optional): Simple Paperless search term
    • more_like_id (optional): Find documents similar to this document ID
    • correspondent (optional): Correspondent ID
    • document_type (optional): Document type ID
    • tag (optional): Tag ID
    • storage_path (optional): Storage path ID
    • created__date__gte (optional): Created date on or after YYYY-MM-DD
    • created__date__lte (optional): Created date on or before YYYY-MM-DD
    • custom_field_query (optional): Structured Paperless custom field query using `[field_name_or_id, operator, value]` leaves or `["AND" | "OR", [clause1, clause2]]` groups
    • paperless_filters (optional): Additional documented `/api/documents/` Paperless query parameters, passed as key/value pairs
    typescript
    // Full-text query
    query_documents({
      query: "invoice 2024"
    })
    
    // Simple search term
    query_documents({
      search: "acme"
    })
    
    // Custom field exact match
    query_documents({
      custom_field_query: ["Invoice Number", "exact", "12345"]
    })
    
    // Custom field empty
    query_documents({
      custom_field_query: ["OR", [
        ["Invoice Number", "isnull", true],
        ["Invoice Number", "exact", ""]
      ]]
    })
    
    // Custom field missing
    query_documents({
      custom_field_query: ["Invoice Number", "exists", false]
    })
    
    // Combined filters
    query_documents({
      query: "invoice",
      tag: 5,
      created__date__gte: "2024-01-01",
      custom_field_query: ["Invoice Number", "exists", true]
    })
    
    // One documented Paperless filter that is not a first-class argument
    query_documents({
      paperless_filters: {
        id__in: [101, 202, 303]
      }
    })

    get_document

    Get a specific document by ID.

    Parameters:

    • id: Document ID
    typescript
    get_document({
      id: 123
    })

    search_documents

    Deprecated compatibility wrapper for full-text search. Prefer `query_documents({ query: ... })` for new integrations.

    Parameters:

    • query: Search query string
    typescript
    search_documents({
      query: "invoice 2024"
    })

    download_document

    Download a document file by ID.

    Parameters:

    • id: Document ID
    • original (optional): If true, downloads original file instead of archived version
    typescript
    download_document({
      id: 123,
      original: false
    })

    get_document_thumbnail

    Get a document thumbnail (image preview) by ID. Returns the thumbnail as a base64-encoded WebP image resource.

    Parameters:

    • id: Document ID
    typescript
    get_document_thumbnail({
      id: 123
    })

    bulk_edit_documents

    Perform bulk operations on multiple documents.

    Parameters:

    • documents: Array of document IDs
    • method: One of:
      • set_correspondent: Set correspondent for documents
      • set_document_type: Set document type for documents
      • set_storage_path: Set storage path for documents
      • add_tag: Add a tag to documents
      • remove_tag: Remove a tag from documents
      • modify_tags: Add and/or remove multiple tags
      • delete: Delete documents
      • reprocess: Reprocess documents
      • set_permissions: Set document permissions
      • merge: Merge multiple documents
      • split: Split a document into multiple documents
      • rotate: Rotate document pages
      • delete_pages: Delete specific pages from a document
    • Additional parameters based on method:
      • correspondent: ID for set_correspondent
      • document_type: ID for set_document_type
      • storage_path: ID for set_storage_path
      • tag: ID for add_tag/remove_tag
      • add_tags: Array of tag IDs for modify_tags
      • remove_tags: Array of tag IDs for modify_tags
      • set_permissions: Object for set_permissions with view/change users and groups (`{"view": {"users": [], "groups": []}, "change": {...}}`). Omitted actions/lists are left untouched
      • owner: User ID (or null to remove) for set_permissions. Unless merge is true, omitting owner clears the current owner
      • merge: Boolean for set_permissions — true adds to existing permissions and keeps the owner; false (default) replaces the listed users/groups
      • metadata_document_id: ID for merge to specify metadata source
      • delete_originals: Boolean for merge/split
      • pages: String for split "[1,2-3,4,5-7]" or delete_pages "[2,3,4]"
      • degrees: Number for rotate (90, 180, or 270)

    Examples:

    typescript
    // Add a tag to multiple documents
    bulk_edit_documents({
      documents: [1, 2, 3],
      method: "add_tag",
      tag: 5
    })
    
    // Set correspondent and document type
    bulk_edit_documents({
      documents: [4, 5],
      method: "set_correspondent",
      correspondent: 2
    })
    
    // Merge documents
    bulk_edit_documents({
      documents: [6, 7, 8],
      method: "merge",
      metadata_document_id: 6,
      delete_originals: true
    })
    
    // Split document into parts
    bulk_edit_documents({
      documents: [9],
      method: "split",
      pages: "[1-2,3-4,5]"
    })
    
    // Modify multiple tags at once
    bulk_edit_documents({
      documents: [10, 11],
      method: "modify_tags",
      add_tags: [1, 2],
      remove_tags: [3, 4]
    })
    
    // Modify custom fields
    bulk_edit_documents({
      documents: [12, 13],
      method: "modify_custom_fields",
      add_custom_fields: [
        { field: 2, value: "year" }
      ],
      remove_custom_fields: []
    })
    
    // Set an empty custom field value, e.g. a date field used as a pending marker
    bulk_edit_documents({
      documents: [14],
      method: "modify_custom_fields",
      add_custom_fields: [
        { field: 9, value: "" }
      ],
      remove_custom_fields: []
    })

    post_document

    Upload a new document to Paperless-NGX.

    Two upload modes:

    1. Base64 mode (traditional): Provide `file` (base64-encoded content) + `filename`

    2. Filesystem mode (efficient): Provide `file_path` (absolute path on server)

    Security Note: When using `file_path`, set the `PAPERLESS_MCP_UPLOAD_PATHS` environment variable (colon-separated list of allowed directories) to restrict uploads to specific locations. Without this, any file on the server's filesystem could be uploaded.

    Parameters:

    • file (optional): Base64 encoded file content. Either `file` or `file_path` required.
    • file_path (optional): Absolute path to file on server's filesystem. Either `file` or `file_path` required.
    • filename (optional): Name of the file. Required with `file`, optional with `file_path` (derives from path).
    • title (optional): Title for the document
    • created (optional): DateTime when the document was created (e.g. "2024-01-19" or "2024-01-19 06:15:00+02:00")
    • correspondent (optional): ID of a correspondent
    • document_type (optional): ID of a document type
    • storage_path (optional): ID of a storage path
    • tags (optional): Array of tag IDs
    • archive_serial_number (optional): Archive serial number
    • custom_fields (optional): Array of custom field IDs

    File size limit: 100MB for both modes

    typescript
    // Base64 mode (traditional)
    post_document({
      file: "base64_encoded_content",
      filename: "invoice.pdf",
      title: "January Invoice",
      created: "2024-01-19",
      correspondent: 1,
      document_type: 2,
      tags: [1, 3],
      archive_serial_number: "2024-001",
      custom_fields: [1, 2]
    })
    
    // Filesystem mode (more efficient for large files)
    post_document({
      file_path: "/var/uploads/invoice.pdf",
      title: "January Invoice",
      correspondent: 1,
      document_type: 2,
      tags: [1, 3]
    })

    Document Notes

    list_document_notes

    List all notes attached to a document.

    Parameters:

    • id: Document ID
    typescript
    list_document_notes({
      id: 123
    })

    create_document_note

    Add a note to a document. Returns the document's full list of notes.

    Parameters:

    • id: Document ID
    • note: The note text to add
    typescript
    create_document_note({
      id: 123,
      note: "Invoice paid on 2026-06-30 from Commerzbank account."
    })

    delete_document_note

    ⚠️ Delete a single note from a document by its note ID. This operation is irreversible.

    Parameters:

    • id: Document ID
    • note_id: The ID of the note to delete
    • confirm: Must be `true` to confirm this destructive operation
    typescript
    delete_document_note({
      id: 123,
      note_id: 5,
      confirm: true
    })

    Tag Operations

    list_tags

    Get all tags.

    typescript
    list_tags()

    create_tag

    Create a new tag.

    Parameters:

    • name: Tag name
    • color (optional): Hex color code (e.g. "#ff0000")
    • match (optional): Text pattern to match
    • matching_algorithm (optional): Number between 0 and 6:

    0 - None

    1 - Any word

    2 - All words

    3 - Exact match

    4 - Regular expression

    5 - Fuzzy word

    6 - Automatic

    typescript
    create_tag({
      name: "Invoice",
      color: "#ff0000",
      match: "invoice",
      matching_algorithm: 5
    })

    Correspondent Operations

    list_correspondents

    Get all correspondents.

    typescript
    list_correspondents()

    create_correspondent

    Create a new correspondent.

    Parameters:

    • name: Correspondent name
    • match (optional): Text pattern to match
    • matching_algorithm (optional): Number between 0 and 6:

    0 - None

    1 - Any word

    2 - All words

    3 - Exact match

    4 - Regular expression

    5 - Fuzzy word

    6 - Automatic

    typescript
    create_correspondent({
      name: "ACME Corp",
      match: "ACME",
      matching_algorithm: 5
    })

    Document Type Operations

    list_document_types

    Get all document types.

    typescript
    list_document_types()

    create_document_type

    Create a new document type.

    Parameters:

    • name: Document type name
    • match (optional): Text pattern to match
    • matching_algorithm (optional): Number between 0 and 6:

    0 - None

    1 - Any word

    2 - All words

    3 - Exact match

    4 - Regular expression

    5 - Fuzzy word

    6 - Automatic

    typescript
    create_document_type({
      name: "Invoice",
      match: "invoice total amount due",
      matching_algorithm: 1
    })

    Custom Field Operations

    list_custom_fields

    Get all custom fields.

    typescript
    list_custom_fields()

    get_custom_field

    Get a specific custom field by ID.

    Parameters:

    • id: Custom field ID
    typescript
    get_custom_field({
      id: 1
    })

    create_custom_field

    Create a new custom field.

    Parameters:

    • name: Custom field name
    • data_type: One of "string", "url", "date", "boolean", "integer", "float", "monetary", "documentlink", "select"
    • extra_data (optional): Extra data for the custom field, such as select options
    typescript
    create_custom_field({
      name: "Invoice Number",
      data_type: "string"
    })

    update_custom_field

    Update an existing custom field.

    Parameters:

    • id: Custom field ID
    • name (optional): New custom field name
    • data_type (optional): New data type
    • extra_data (optional): Extra data for the custom field
    typescript
    update_custom_field({
      id: 1,
      name: "Updated Invoice Number",
      data_type: "string"
    })

    delete_custom_field

    Delete a custom field.

    Parameters:

    • id: Custom field ID
    typescript
    delete_custom_field({
      id: 1
    })

    bulk_edit_custom_fields

    Perform bulk operations on multiple custom fields.

    Parameters:

    • custom_fields: Array of custom field IDs
    • operation: One of "delete"
    typescript
    bulk_edit_custom_fields({
      custom_fields: [1, 2, 3],
      operation: "delete"
    })

    Mail Operations

    Tools for managing Paperless mail accounts and the mail rules that drive

    automatic email ingestion. Account passwords/tokens are never exposed: they are

    redacted from every tool response.

    list_mail_accounts

    List mail accounts so you can pick the account ID needed when creating a mail

    rule. Passwords are redacted.

    Parameters:

    • page (optional): Page number
    • page_size (optional): Number of results per page
    typescript
    list_mail_accounts()

    get_mail_account

    Get a single mail account by ID. Password/token fields are redacted.

    Parameters:

    • id: Mail account ID
    typescript
    get_mail_account({
      id: 1
    })

    process_mail_account

    Manually trigger Paperless mail processing for one account. This can consume

    matching mails according to the account's enabled mail rules.

    Parameters:

    • id: Mail account ID
    typescript
    process_mail_account({
      id: 1
    })

    list_mail_rules

    List mail rules with optional pagination.

    Parameters:

    • page (optional): Page number
    • page_size (optional): Number of results per page
    typescript
    list_mail_rules()

    get_mail_rule

    Get a single mail rule by ID.

    Parameters:

    • id: Mail rule ID
    typescript
    get_mail_rule({
      id: 1
    })

    create_mail_rule

    Create a mail rule. Use `list_mail_accounts` first to choose the account.

    Required parameters:

    • name: Rule name
    • account: Mail account ID
    • folder: IMAP folder to scan (e.g. "INBOX")

    Common optional parameters:

    • enabled (default true): Whether the rule is active
    • filter_from / filter_to / filter_subject / filter_body: Match incoming mail
    • maximum_age: Only process mail newer than this many days
    • action: 1=Delete, 2=Move to folder, 3=Mark as read, 4=Flag, 5=Tag
    • action_parameter: Target folder/tag for the chosen action
    • assign_title_from: 1=Subject, 2=Attachment filename, 3=Do not assign
    • assign_tags / assign_correspondent / assign_document_type: Metadata to apply
    • assign_correspondent_from: 1=None, 2=Mail address, 3=Sender name, 4=Use assign_correspondent
    • attachment_type: 1=Attachments only, 2=All files incl. inline
    • consumption_scope: 1=Attachments only, 2=Full mail as .eml, 3=Both
    • pdf_layout: 0=System default, 1=Text+HTML, 2=HTML+text, 3=HTML only, 4=Text only
    typescript
    create_mail_rule({
      name: "Invoices",
      account: 1,
      folder: "INBOX",
      filter_subject: "invoice",
      action: 3,
      attachment_type: 1
    })

    update_mail_rule

    Patch an existing mail rule. Only the fields you supply are changed.

    Parameters:

    • id: Mail rule ID
    • ...any of the `create_mail_rule` fields to update
    typescript
    update_mail_rule({
      id: 1,
      enabled: false
    })

    delete_mail_rule

    Delete a mail rule. Requires an explicit confirmation flag. This changes future

    mail ingestion behavior but does not delete any existing documents.

    Parameters:

    • id: Mail rule ID
    • confirm: Must be `true` to confirm deletion
    typescript
    delete_mail_rule({
      id: 1,
      confirm: true
    })

    Error Handling

    The server will show clear error messages if:

    • The Paperless-NGX URL or API token is incorrect
    • The Paperless-NGX server is unreachable
    • The requested operation fails
    • The provided parameters are invalid

    Testing

    Unit tests

    Run the unit test suite (no external dependencies required):

    bash
    npm test

    E2E tests

    The E2E suite boots an empty Paperless-ngx instance, runs the compiled MCP server, and drives a deterministic serial scenario through `tools/call` requests — creating a tag, correspondent, and document type, uploading a PDF, then exercising list / get / search / download / thumbnail / bulk-edit on the same document. No LLM and no Paperless REST client outside MCP.

    Prerequisites: Docker, Docker Compose, and `jq`.

    bash
    # 1. Build the MCP server
    npm run build
    
    # 2. Start Paperless-ngx
    docker compose -f docker-compose.e2e.yml up -d
    
    # 3. Wait for Paperless to be ready, then get a token
    TOKEN=$(curl -s -X POST http://localhost:8000/api/token/ \
      -H 'Content-Type: application/json' \
      -d '{"username":"admin","password":"admin123"}' | jq -r '.token')
    
    # 4. Start the MCP server
    node build/index.js --http --port 3001 \
      --baseUrl http://localhost:8000 --token "$TOKEN" &
    MCP_PID=$!
    
    # 5. Run the E2E tests
    MCP_URL=http://localhost:3001/mcp \
    PAPERLESS_URL=http://localhost:8000 \
    PAPERLESS_TOKEN="$TOKEN" \
    npm run test:e2e
    
    # 6. Cleanup
    kill "$MCP_PID"
    docker compose -f docker-compose.e2e.yml down -v

    E2E tests also run automatically in CI on every pull request and push to `main`, covering both the `build/index.js` CLI and the published Docker image.

    Development

    Want to contribute or modify the server? Here's what you need to know:

    1. Clone the repository

    2. Install dependencies:

    bash
    npm install

    3. Make your changes to server.js

    4. Test locally:

    bash
    node server.js http://localhost:8000 your-test-token

    The server is built with:

    • litemcp: A TypeScript framework for building MCP servers
    • zod: TypeScript-first schema validation

    API Documentation

    This MCP server implements endpoints from the Paperless-NGX REST API. For more details about the underlying API, see the official documentation.

    Running the MCP Server

    The MCP server can be run in two modes:

    1. stdio (default)

    This is the default mode. The server communicates over stdio, suitable for CLI and direct integrations.

    code
    npm run start --

    2. HTTP (Streamable HTTP Transport)

    To run the server as an HTTP service, use the `--http` flag. You can also specify the port with `--port` (default: 3000). This mode requires Express to be installed (it is included as a dependency).

    code
    npm run start --   --http --port 3000
    • The MCP API will be available at `POST /mcp` on the specified port.
    • Each request is handled statelessly, following the StreamableHTTPServerTransport pattern.
    • GET and DELETE requests to `/mcp` will return 405 Method Not Allowed.

    Per-request API token (HTTP/Docker mode)

    In HTTP mode, clients authenticate by supplying a Paperless-NGX API token via the standard `Authorization` header:

    code
    Authorization: Bearer

    The token is passed straight through to Paperless-NGX, so each client's own Paperless permissions are enforced end-to-end. This lets a single server instance serve multiple users, each with their own token. The same behaviour applies to both `/mcp` and `/sse` endpoints.

    > ⚠️ Breaking change in v2.0.0 — HTTP mode is now authenticated by default.

    >

    > Previously, a request with no `Authorization` header silently fell back to the server-configured `PAPERLESS_API_KEY`, which left the HTTP endpoint open to anyone who could reach the port. As of v2.0.0, requests without a `Bearer` token are rejected with `401 Unauthorized`. The server token is never used for unauthenticated requests unless you explicitly opt in with `--no-auth`.

    Scenario`--no-auth` off (default)`--no-auth` on
    Client sends `Authorization: Bearer ``` (client-supplied)`` (client-supplied)
    No header, `PAPERLESS_API_KEY` / `--token` set`401 Unauthorized`server token
    No header, no server token`401 Unauthorized``401 Unauthorized`

    Migrating from v1.x: if you relied on the old fallback (a single shared `PAPERLESS_API_KEY` with clients that don't send a token), you have two options:

    1. Recommended: have each client send `Authorization: Bearer `.

    2. Restore the old behaviour (trusted/local networks only): start the server with the `--no-auth` flag, e.g. append it to the Docker `command`/args or your CLI invocation. This requires a server token (`PAPERLESS_API_KEY` or `--token`) to be configured.

    Docker Deployment

    The MCP server can be deployed using Docker and Docker Compose. The Docker image automatically runs in HTTP mode with SSE (Server-Sent Events) support on port 3000.

    Docker Compose Configuration

    Create a `docker-compose.yml` file:

    yaml
    services:
      paperless-mcp:
        container_name: paperless-mcp
        image: ghcr.io/baruchiro/paperless-mcp:latest
        environment:
          - PAPERLESS_URL=http://your-paperless-ngx-server:8000
          - PAPERLESS_API_KEY=your-paperless-api-key
          - PAPERLESS_PUBLIC_URL=https://paperless-ngx.yourpublicurl.com
        ports:
          - "3000:3000"
        restart: unless-stopped

    Then run:

    bash
    docker-compose up -d

    Using with Continue VS Code Extension

    If you're using the Continue VS Code extension, you can configure it to use the Dockerized MCP server via SSE.

    Create or edit `.continue/mcpServers/paperless-mcp.yaml` at your workspace root:

    yaml
    name: Paperless
    version: 0.0.1
    schema: v1
    mcpServers:
      - name: Paperless
        type: sse
        url: http://localhost:3000/sse

    Notes:

    • Replace `localhost` with your Docker host's IP address or hostname if running on a remote server
    • The Docker container handles authentication via environment variables, so no credentials are needed in the Continue config
    • The SSE endpoint is available at `/sse` on the configured port (default: 3000)

    Credits

    This project is a fork of nloui/paperless-mcp. Many thanks to the original author for their work. Contributions and improvements may be returned upstream.

    Debugging

    To debug the MCP server in VS Code, use the following launch configuration:

    json
    {
        "type": "node",
        "request": "launch",
        "name": "Debug Paperless MCP (HTTP, ts-node ESM)",
        "program": "${workspaceFolder}/node_modules/ts-node/dist/bin.js",
        "args": [
            "--esm",
            "src/index.ts",
            "--http",
            "--baseUrl",
            "http://your-paperless-instance:8000",
            "--token",
            "your-api-token",
            "--port",
            "3002"
        ],
        "env": {
            "NODE_OPTIONS": "--loader ts-node/esm",
        },
        "console": "integratedTerminal",
        "skipFiles": [
            "/**"
        ]
    }

    Important: Before debugging, uncomment the following line in `src/index.ts` (around line 175):

    typescript
    // await new Promise((resolve) => setTimeout(resolve, 1000000));

    This prevents the server from exiting immediately and allows you to set breakpoints and debug the code.

    Frequently asked questions

    What is paperless-mcp?

    paperless-mcp is An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.

    How do I install paperless-mcp?

    Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

    Is paperless-mcp open source?

    Yes — it is hosted on GitHub at https://github.com/baruchiro/paperless-mcp and has 139 stars.

    Related MCP tools

    Run your own MCP server? See who uses it and what to fix.

    Measure it with TrackMCP