Skip to main content
Glama

bulk_edit_documents

Edit multiple documents simultaneously on Paperless-MCP by setting correspondents, document types, storage paths, tags, permissions, or applying actions like merge, split, rotate, or delete.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
add_tagsNo
correspondentNo
degreesNo
delete_originalsNo
document_typeNo
documentsYes
metadata_document_idNo
methodYes
pagesNo
permissionsNo
remove_tagsNo
storage_pathNo
tagNo

Implementation Reference

  • The handler function for the 'bulk_edit_documents' MCP tool. It performs input validation (e.g., confirmation for delete), transforms custom fields parameters, calls the PaperlessAPI.bulkEditDocuments method, and formats the response as MCP content.
    withErrorHandling(async (args, extra) => {
      if (!api) throw new Error("Please configure API connection first");
      if (args.method === "delete" && !args.confirm) {
        throw new Error(
          "Confirmation required for destructive operation. Set confirm: true to proceed."
        );
      }
      const { documents, method, add_custom_fields, ...parameters } = args;
    
      // Transform add_custom_fields into the two separate API parameters
      const apiParameters = { ...parameters };
      if (add_custom_fields && add_custom_fields.length > 0) {
        apiParameters.assign_custom_fields = add_custom_fields.map(
          (cf) => cf.field
        );
        apiParameters.assign_custom_fields_values = add_custom_fields;
      }
    
      const response = await api.bulkEditDocuments(
        documents,
        method,
        apiParameters
      );
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({ result: response.result || response }),
          },
        ],
      };
    })
  • Zod schema defining the input parameters for the 'bulk_edit_documents' tool, including required 'documents' and 'method', optional fields for various operations, and validation transforms.
    {
      documents: z.array(z.number()),
      method: z.enum([
        "set_correspondent",
        "set_document_type",
        "set_storage_path",
        "add_tag",
        "remove_tag",
        "modify_tags",
        "modify_custom_fields",
        "delete",
        "reprocess",
        "set_permissions",
        "merge",
        "split",
        "rotate",
        "delete_pages",
      ]),
      correspondent: z.number().optional(),
      document_type: z.number().optional(),
      storage_path: z.number().optional(),
      tag: z.number().optional(),
      add_tags: z.array(z.number()).optional().transform(arrayNotEmpty),
      remove_tags: z.array(z.number()).optional().transform(arrayNotEmpty),
      add_custom_fields: z
        .array(
          z.object({
            field: z.number(),
            value: z.union([z.string(), z.number(), z.boolean(), z.null()]),
          })
        )
        .optional()
        .transform(arrayNotEmpty),
      remove_custom_fields: z
        .array(z.number())
        .optional()
        .transform(arrayNotEmpty),
      permissions: z
        .object({
          owner: z.number().nullable().optional(),
          set_permissions: z
            .object({
              view: z.object({
                users: z.array(z.number()),
                groups: z.array(z.number()),
              }),
              change: z.object({
                users: z.array(z.number()),
                groups: z.array(z.number()),
              }),
            })
            .optional(),
          merge: z.boolean().optional(),
        })
        .optional()
        .transform(objectNotEmpty),
      metadata_document_id: z.number().optional(),
      delete_originals: z.boolean().optional(),
      pages: z.string().optional(),
      degrees: z.number().optional(),
      confirm: z
        .boolean()
        .optional()
        .describe(
          "Must be true when method is 'delete' to confirm destructive operation"
        ),
    },
  • The server.tool() call that registers the 'bulk_edit_documents' tool on the MCP server, specifying name, description, input schema, and handler function.
    server.tool(
      "bulk_edit_documents",
      "Perform bulk operations on multiple documents. Note: 'remove_tag' removes a tag from specific documents (tag remains in system), while 'delete_tag' permanently deletes a tag from the entire system. ⚠️ WARNING: 'delete' method permanently deletes documents and requires confirmation.",
      {
        documents: z.array(z.number()),
        method: z.enum([
          "set_correspondent",
          "set_document_type",
          "set_storage_path",
          "add_tag",
          "remove_tag",
          "modify_tags",
          "modify_custom_fields",
          "delete",
          "reprocess",
          "set_permissions",
          "merge",
          "split",
          "rotate",
          "delete_pages",
        ]),
        correspondent: z.number().optional(),
        document_type: z.number().optional(),
        storage_path: z.number().optional(),
        tag: z.number().optional(),
        add_tags: z.array(z.number()).optional().transform(arrayNotEmpty),
        remove_tags: z.array(z.number()).optional().transform(arrayNotEmpty),
        add_custom_fields: z
          .array(
            z.object({
              field: z.number(),
              value: z.union([z.string(), z.number(), z.boolean(), z.null()]),
            })
          )
          .optional()
          .transform(arrayNotEmpty),
        remove_custom_fields: z
          .array(z.number())
          .optional()
          .transform(arrayNotEmpty),
        permissions: z
          .object({
            owner: z.number().nullable().optional(),
            set_permissions: z
              .object({
                view: z.object({
                  users: z.array(z.number()),
                  groups: z.array(z.number()),
                }),
                change: z.object({
                  users: z.array(z.number()),
                  groups: z.array(z.number()),
                }),
              })
              .optional(),
            merge: z.boolean().optional(),
          })
          .optional()
          .transform(objectNotEmpty),
        metadata_document_id: z.number().optional(),
        delete_originals: z.boolean().optional(),
        pages: z.string().optional(),
        degrees: z.number().optional(),
        confirm: z
          .boolean()
          .optional()
          .describe(
            "Must be true when method is 'delete' to confirm destructive operation"
          ),
      },
      withErrorHandling(async (args, extra) => {
        if (!api) throw new Error("Please configure API connection first");
        if (args.method === "delete" && !args.confirm) {
          throw new Error(
            "Confirmation required for destructive operation. Set confirm: true to proceed."
          );
        }
        const { documents, method, add_custom_fields, ...parameters } = args;
    
        // Transform add_custom_fields into the two separate API parameters
        const apiParameters = { ...parameters };
        if (add_custom_fields && add_custom_fields.length > 0) {
          apiParameters.assign_custom_fields = add_custom_fields.map(
            (cf) => cf.field
          );
          apiParameters.assign_custom_fields_values = add_custom_fields;
        }
    
        const response = await api.bulkEditDocuments(
          documents,
          method,
          apiParameters
        );
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify({ result: response.result || response }),
            },
          ],
        };
      })
    );
  • Helper method in PaperlessAPI class that sends the bulk edit request to the Paperless-ngx API endpoint '/documents/bulk_edit/'.
    async bulkEditDocuments(
      documents: number[],
      method: string,
      parameters: BulkEditParameters = {}
    ): Promise<BulkEditDocumentsResult> {
      return this.request<BulkEditDocumentsResult>("/documents/bulk_edit/", {
        method: "POST",
        body: JSON.stringify({
          documents,
          method,
          parameters,
        }),
      });
    }
  • TypeScript interfaces for BulkEditDocumentsResult (output) and BulkEditParameters (input parameters for the API call).
    export interface BulkEditDocumentsResult {
      result: string;
    }
    
    export interface BulkEditParameters {
      assign_custom_fields?: number[];
      assign_custom_fields_values?: CustomFieldInstanceRequest[];
      remove_custom_fields?: number[];
      add_tags?: number[];
      remove_tags?: number[];
      degrees?: number;
      pages?: string;
      metadata_document_id?: number;
      delete_originals?: boolean;
      correspondent?: number;
      document_type?: number;
      storage_path?: number;
      tag?: number;
      permissions?: {
        owner?: number | null;
        set_permissions?: {
          view: { users: number[]; groups: number[] };
          change: { users: number[]; groups: number[] };
        };
        merge?: boolean;
      };
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/baruchiro/paperless-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server