Skip to main content
Glama
pdfdotco

PDF.co MCP Server

Official
by pdfdotco

find_text

Search for text in PDF files and retrieve its exact coordinates. Supports regular expressions for advanced pattern matching.

Instructions

Find text in PDF and get coordinates. Supports regular expressions.
Ref: https://developer.pdf.co/api-reference/pdf-find/basic.md

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesURL to the source PDF file. Supports publicly accessible links including Google Drive, Dropbox, PDF.co Built-In Files Storage. Use 'upload_file' tool to upload local files.
searchStringYesText to search. Can support regular expressions if regexSearch is set to True.
httpusernameNoHTTP auth user name if required to access source url. (Optional)
httppasswordNoHTTP auth password if required to access source url. (Optional)
pagesNoComma-separated list of page indices (or ranges) to process. Leave empty for all pages. Example: '0,2-5,7-'. The first-page index is 0. (Optional)
wordMatchingModeNoValues can be either SmartMatch, ExactMatch, or None. (Optional)
passwordNoPassword of the PDF file. (Optional)
regexSearchNoSet to True to enable regular expressions in the search string. (Optional)
api_keyNoPDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)

Implementation Reference

  • The @mcp.tool(name='find_text') decorated async function that defines the find_text tool handler. It accepts url, searchString, httpusername, httppassword, pages, wordMatchingMode, password, regexSearch, and api_key parameters, then delegates to find_text_in_pdf service function.
    @mcp.tool(name="find_text")
    async def find_text(
        url: str = Field(
            description="URL to the source PDF file. Supports publicly accessible links including Google Drive, Dropbox, PDF.co Built-In Files Storage. Use 'upload_file' tool to upload local files."
        ),
        searchString: str = Field(
            description="Text to search. Can support regular expressions if regexSearch is set to True."
        ),
        httpusername: str = Field(
            description="HTTP auth user name if required to access source url. (Optional)",
            default="",
        ),
        httppassword: str = Field(
            description="HTTP auth password if required to access source url. (Optional)",
            default="",
        ),
        pages: str = Field(
            description="Comma-separated list of page indices (or ranges) to process. Leave empty for all pages. Example: '0,2-5,7-'. The first-page index is 0. (Optional)",
            default="",
        ),
        wordMatchingMode: str = Field(
            description="Values can be either SmartMatch, ExactMatch, or None. (Optional)",
            default=None,
        ),
        password: str = Field(
            description="Password of the PDF file. (Optional)", default=""
        ),
        regexSearch: bool = Field(
            description="Set to True to enable regular expressions in the search string. (Optional)",
            default=False,
        ),
        api_key: str = Field(
            description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)",
            default="",
        ),
    ) -> BaseResponse:
        """
        Find text in PDF and get coordinates. Supports regular expressions.
        Ref: https://developer.pdf.co/api-reference/pdf-find/basic.md
        """
        params = ConversionParams(
            url=url,
            httpusername=httpusername,
            httppassword=httppassword,
            pages=pages,
            password=password,
        )
    
        return await find_text_in_pdf(
            params, searchString, regexSearch, wordMatchingMode, api_key=api_key
        )
  • The tool is registered with the MCP server via the @mcp.tool(name='find_text') decorator, where `mcp` is a FastMCP('pdfco') instance from pdfco.mcp.server.
    @mcp.tool(name="find_text")
  • The find_text_in_pdf service function that builds the custom payload with searchString and regexSearch, optionally includes wordMatchingMode, and sends a request to the 'pdf/find' endpoint via the PDF.co API client.
    async def find_text_in_pdf(
        params: ConversionParams,
        search_string: str,
        regex_search: bool = False,
        word_matching_mode: str | None = None,
        api_key: str | None = None,
    ) -> BaseResponse:
        custom_payload = {"searchString": search_string, "regexSearch": regex_search}
        if word_matching_mode:
            custom_payload["wordMatchingMode"] = word_matching_mode
        return await request(
            "pdf/find", params, custom_payload=custom_payload, api_key=api_key
        )
  • The parse_payload method on ConversionParams used to build the payload dict that gets sent to the PDF.co API. It includes url, httpusername, httppassword, pages, password, and other fields from the params object.
    def parse_payload(self, async_mode: bool = True):
        payload = {
            "async": async_mode,
        }
        if self.url:
            payload["url"] = self.url
        if self.httpusername:
            payload["httpusername"] = self.httpusername
        if self.httppassword:
            payload["httppassword"] = self.httppassword
        if self.pages:
            payload["pages"] = self.pages
        if self.unwrap:
            payload["unwrap"] = self.unwrap
        if self.rect:
            payload["rect"] = self.rect
        if self.lang:
            payload["lang"] = self.lang
        if self.line_grouping:
            payload["lineGrouping"] = self.line_grouping
        if self.password:
            payload["password"] = self.password
        if self.name:
            payload["name"] = self.name
        if self.autosize:
            payload["autosize"] = self.autosize
    
        if self.html:
            payload["html"] = self.html
        if self.templateId:
            payload["templateId"] = self.templateId
        if self.templateData:
            payload["templateData"] = self.templateData
        if self.margins:
            payload["margins"] = self.margins
        if self.paperSize:
            payload["paperSize"] = self.paperSize
        if self.orientation:
            payload["orientation"] = self.orientation
        if self.printBackground:
            payload["printBackground"] = self.printBackground
        if self.mediaType:
            payload["mediaType"] = self.mediaType
        if self.DoNotWaitFullLoad:
            payload["DoNotWaitFullLoad"] = self.DoNotWaitFullLoad
        if self.header:
            payload["header"] = self.header
        if self.footer:
            payload["footer"] = self.footer
    
        if self.worksheetIndex:
            payload["worksheetIndex"] = self.worksheetIndex
    
        return payload
  • The request helper function that makes the actual HTTP POST call to the PDF.co API, handles authentication via PDFCoClient, and returns a BaseResponse with the result or error.
    async def request(
        endpoint: str,
        params: ConversionParams,
        custom_payload: dict | None = None,
        api_key: str | None = None,
    ) -> BaseResponse:
        payload = params.parse_payload(async_mode=True)
        if custom_payload:
            payload.update(custom_payload)
    
        try:
            async with PDFCoClient(api_key=api_key) as client:
                url = f"/v1/{endpoint}"
                print(f"Requesting {url} with payload {payload}", file=sys.stderr)
                response = await client.post(url, json=payload)
                print(f"response: {response}", file=sys.stderr)
                json_data = response.json()
                return BaseResponse(
                    status="working",
                    content=json_data,
                    credits_used=json_data.get("credits"),
                    credits_remaining=json_data.get("remainingCredits"),
                    tips=f"You **should** use the 'wait_job_completion' tool to wait for the job [{json_data.get('jobId')}] to complete if a jobId is present.",
                )
        except Exception as e:
            return BaseResponse(
                status="error",
                content=f"{type(e)}: {[arg for arg in e.args if arg]}",
            )
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses support for regular expressions and coordinate output, but lacks details on read-only nature, rate limits, authentication (though implied via API key parameter), and behavior when text is not found. Given no annotations, the description carries the burden but is incomplete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at two sentences, with key information front-loaded. The reference link provides additional details without bloating the description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite high schema coverage, the description lacks critical context about the output format (e.g., coordinate system, structure of matches), error behavior, and pagination or limits. For a tool with 9 parameters and no output schema, this is a significant gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with detailed parameter descriptions. The description adds little beyond schema, only noting regex support which aligns with the 'regexSearch' parameter. Baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool finds text in PDF and returns coordinates, and mentions regex support. It distinguishes from siblings like 'find_table' or 'pdf_to_text' by emphasizing coordinate retrieval.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for text extraction with coordinates, but does not explicitly state when to use this tool versus alternatives (e.g., pdf_to_text for plain text) or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/pdfdotco/pdfco-mcp'

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