pdfco-mcp 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/PKG-INFO +2 -1
  2. pdfco_mcp-0.0.3/pdfco/mcp/__init__.py +27 -0
  3. pdfco_mcp-0.0.3/pdfco/mcp/server.py +3 -0
  4. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/services/client.py +17 -7
  5. pdfco_mcp-0.0.3/pdfco/mcp/services/pdf.py +95 -0
  6. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/conversion.py +40 -21
  7. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/document.py +2 -1
  8. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/editing.py +2 -1
  9. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/extraction.py +5 -3
  10. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/file.py +3 -2
  11. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/form.py +6 -3
  12. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/job.py +6 -4
  13. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/modification.py +4 -2
  14. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/search.py +4 -2
  15. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/searchable.py +4 -2
  16. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/apis/security.py +4 -2
  17. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pyproject.toml +3 -2
  18. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/uv.lock +139 -5
  19. pdfco_mcp-0.0.2/pdfco/mcp/__init__.py +0 -8
  20. pdfco_mcp-0.0.2/pdfco/mcp/server.py +0 -3
  21. pdfco_mcp-0.0.2/pdfco/mcp/services/pdf.py +0 -95
  22. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/.github/workflows/release.yml +0 -0
  23. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/.gitignore +0 -0
  24. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/.python-version +0 -0
  25. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/LICENSE +0 -0
  26. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/README.md +0 -0
  27. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/models.py +0 -0
  28. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/services/__init__.py +0 -0
  29. {pdfco_mcp-0.0.2 → pdfco_mcp-0.0.3}/pdfco/mcp/tools/__init__.py +0 -0
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfco-mcp
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: Add your description here
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
+ Requires-Dist: fastmcp>=2.6.1
7
8
  Requires-Dist: httpx>=0.28.1
8
9
  Requires-Dist: langchain-community>=0.3.21
9
10
  Requires-Dist: langchain[google-genai]>=0.3.23
@@ -0,0 +1,27 @@
1
+ import sys
2
+ from pdfco.mcp.server import mcp
3
+ from pdfco.mcp.tools.apis import conversion, job, file, modification, form, search, searchable, security, document, extraction, editing
4
+
5
+ def main():
6
+ if len(sys.argv) > 1:
7
+ transport = sys.argv[1]
8
+ if transport == "stdio":
9
+ mcp.run(transport=transport)
10
+ elif transport == "sse":
11
+ if len(sys.argv) < 2:
12
+ raise ValueError("SSE transport requires a port number")
13
+ port = int(sys.argv[2])
14
+ mcp.run(transport=transport, host="0.0.0.0", port=port)
15
+ elif transport == "streamable-http":
16
+ if len(sys.argv) < 3:
17
+ raise ValueError("Streamable HTTP transport requires a port number and path")
18
+ port = int(sys.argv[2])
19
+ path = sys.argv[3]
20
+ mcp.run(transport=transport, host="0.0.0.0", port=port, path=path)
21
+ else:
22
+ raise ValueError(f"Invalid transport: {transport}")
23
+ else:
24
+ mcp.run(transport="stdio")
25
+
26
+ if __name__ == "__main__":
27
+ main()
@@ -0,0 +1,3 @@
1
+ from fastmcp import FastMCP
2
+
3
+ mcp = FastMCP("pdfco")
@@ -1,7 +1,7 @@
1
1
  from contextlib import asynccontextmanager
2
2
  from httpx import AsyncClient
3
3
  import os, sys
4
- from typing import AsyncGenerator
4
+ from typing import AsyncGenerator, Optional
5
5
  import importlib.metadata
6
6
 
7
7
  __BASE_URL = "https://api.pdf.co"
@@ -11,11 +11,19 @@ __version__ = importlib.metadata.version('pdfco-mcp')
11
11
  print(f"pdfco-mcp version: {__version__}", file=sys.stderr)
12
12
 
13
13
  @asynccontextmanager
14
- async def PDFCoClient() -> AsyncGenerator[AsyncClient, None]:
15
- if not X_API_KEY:
16
- raise ValueError("""X_API_KEY is not set. Please set X_API_KEY in the environment variables in your MCP server.
17
- To get the API key please sign up at https://pdf.co and you can get the API key from the dashboard.
18
- ex) .cursor/mcp.json
14
+ async def PDFCoClient(api_key: str = None) -> AsyncGenerator[AsyncClient, None]:
15
+ # Use provided API key, fall back to environment variable
16
+ x_api_key = api_key or X_API_KEY
17
+
18
+ if not x_api_key:
19
+ raise ValueError("""API key is required. Please provide an API key as a parameter or set X_API_KEY in the environment variables.
20
+
21
+ To get the API key:
22
+ 1. Sign up at https://pdf.co
23
+ 2. Get the API key from the dashboard
24
+ 3. Either set it as an environment variable or provide it as a parameter
25
+
26
+ Environment variable setup example (.cursor/mcp.json):
19
27
  ```json
20
28
  {
21
29
  "mcpServers": {
@@ -31,12 +39,14 @@ async def PDFCoClient() -> AsyncGenerator[AsyncClient, None]:
31
39
  }
32
40
  }
33
41
  ```
42
+
43
+ Or provide the API key as a parameter when calling the tool.
34
44
  """)
35
45
 
36
46
  client = AsyncClient(
37
47
  base_url=__BASE_URL,
38
48
  headers={
39
- "x-api-key": X_API_KEY,
49
+ "x-api-key": x_api_key,
40
50
  "User-Agent": f"pdfco-mcp/{__version__}",
41
51
  },
42
52
  )
@@ -0,0 +1,95 @@
1
+ import sys
2
+ from pdfco.mcp.models import BaseResponse, ConversionParams
3
+ from pdfco.mcp.services.client import PDFCoClient
4
+
5
+ async def convert_to(_from: str, _to: str, params: ConversionParams, api_key: str = None) -> BaseResponse:
6
+ return await request(f'{_from}/convert/to/{_to}', params, api_key=api_key)
7
+
8
+ async def convert_from(_to: str, _from: str, params: ConversionParams, api_key: str = None) -> BaseResponse:
9
+ return await request(f'{_to}/convert/from/{_from}', params, api_key=api_key)
10
+
11
+ async def merge_pdf(params: ConversionParams, api_key: str = None) -> BaseResponse:
12
+ return await request(f'pdf/merge2', params, api_key=api_key)
13
+
14
+ async def split_pdf(params: ConversionParams, api_key: str = None) -> BaseResponse:
15
+ return await request(f'pdf/split', params, api_key=api_key)
16
+
17
+ async def get_pdf_form_fields_info(params: ConversionParams, api_key: str = None) -> BaseResponse:
18
+ return await request('pdf/info/fields', params, api_key=api_key)
19
+
20
+ async def fill_pdf_form_fields(params: ConversionParams, fields: list = None, annotations: list = None, api_key: str = None) -> BaseResponse:
21
+ custom_payload = {}
22
+ if fields:
23
+ custom_payload["fields"] = fields
24
+ if annotations:
25
+ custom_payload["annotations"] = annotations
26
+ return await request('pdf/edit/add', params, custom_payload=custom_payload, api_key=api_key)
27
+
28
+ async def pdf_add(params: ConversionParams, api_key: str = None, **kwargs) -> BaseResponse:
29
+ """General PDF Add function that supports all PDF Add API parameters"""
30
+ custom_payload = {}
31
+
32
+ # Add all supported parameters
33
+ for key, value in kwargs.items():
34
+ if value is not None and value != "":
35
+ custom_payload[key] = value
36
+
37
+ return await request('pdf/edit/add', params, custom_payload=custom_payload, api_key=api_key)
38
+
39
+ async def find_text_in_pdf(params: ConversionParams, search_string: str, regex_search: bool = False, word_matching_mode: str = None, api_key: str = None) -> BaseResponse:
40
+ custom_payload = {
41
+ "searchString": search_string,
42
+ "regexSearch": regex_search
43
+ }
44
+ if word_matching_mode:
45
+ custom_payload["wordMatchingMode"] = word_matching_mode
46
+ return await request('pdf/find', params, custom_payload=custom_payload, api_key=api_key)
47
+
48
+ async def find_table_in_pdf(params: ConversionParams, api_key: str = None) -> BaseResponse:
49
+ return await request('pdf/find/table', params, api_key=api_key)
50
+
51
+ async def make_pdf_searchable(params: ConversionParams, api_key: str = None) -> BaseResponse:
52
+ return await request('pdf/makesearchable', params, api_key=api_key)
53
+
54
+ async def make_pdf_unsearchable(params: ConversionParams, api_key: str = None) -> BaseResponse:
55
+ return await request('pdf/makeunsearchable', params, api_key=api_key)
56
+
57
+ async def get_pdf_info(params: ConversionParams, api_key: str = None) -> BaseResponse:
58
+ return await request('pdf/info', params, api_key=api_key)
59
+
60
+ async def add_pdf_password(params: ConversionParams, api_key: str = None, **kwargs) -> BaseResponse:
61
+ return await request('pdf/security/add', params, custom_payload=kwargs, api_key=api_key)
62
+
63
+ async def remove_pdf_password(params: ConversionParams, api_key: str = None) -> BaseResponse:
64
+ return await request('pdf/security/remove', params, api_key=api_key)
65
+
66
+ async def parse_invoice(params: ConversionParams, api_key: str = None) -> BaseResponse:
67
+ return await request('ai-invoice-parser', params, api_key=api_key)
68
+
69
+ async def extract_pdf_attachments(params: ConversionParams, api_key: str = None) -> BaseResponse:
70
+ return await request('pdf/attachments/extract', params, api_key=api_key)
71
+
72
+ async def request(endpoint: str, params: ConversionParams, custom_payload: dict = None, api_key: str = None) -> BaseResponse:
73
+ payload = params.parse_payload(async_mode=True)
74
+ if custom_payload:
75
+ payload.update(custom_payload)
76
+
77
+ try:
78
+ async with PDFCoClient(api_key=api_key) as client:
79
+ url = f"/v1/{endpoint}"
80
+ print(f"Requesting {url} with payload {payload}", file=sys.stderr)
81
+ response = await client.post(url, json=payload)
82
+ print(f"response: {response}", file=sys.stderr)
83
+ json_data = response.json()
84
+ return BaseResponse(
85
+ status="working",
86
+ content=json_data,
87
+ credits_used=json_data.get("credits"),
88
+ credits_remaining=json_data.get("remainingCredits"),
89
+ 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.",
90
+ )
91
+ except Exception as e:
92
+ return BaseResponse(
93
+ status="error",
94
+ content=f'{type(e)}: {[arg for arg in e.args if arg]}',
95
+ )
@@ -4,7 +4,6 @@ from pdfco.mcp.models import BaseResponse, ConversionParams
4
4
 
5
5
  from pydantic import Field
6
6
 
7
-
8
7
  @mcp.tool()
9
8
  async def pdf_to_json(
10
9
  url: str = Field(description="URL to the source file. Supports publicly accessible links including Google Drive, Dropbox, PDF.co Built-In Files Storage. Use 'upload_file' tool to upload local files."),
@@ -17,12 +16,13 @@ async def pdf_to_json(
17
16
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
18
17
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
19
18
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
19
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
20
20
  ) -> BaseResponse:
21
21
  """
22
22
  Convert PDF and scanned images into JSON representation with text, fonts, images, vectors, and formatting preserved using the /pdf/convert/to/json2 endpoint.
23
23
  Ref: https://developer.pdf.co/api-reference/pdf-to-json/basic.md
24
24
  """
25
- return await convert_to("pdf", "json2", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
25
+ return await convert_to("pdf", "json2", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name), api_key=api_key)
26
26
 
27
27
  @mcp.tool()
28
28
  async def pdf_to_csv(
@@ -36,12 +36,13 @@ async def pdf_to_csv(
36
36
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
37
37
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
38
38
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
39
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
39
40
  ) -> BaseResponse:
40
41
  """
41
42
  Convert PDF and scanned images into CSV representation with layout, columns, rows, and tables.
42
43
  Ref: https://developer.pdf.co/api-reference/pdf-to-csv.md
43
44
  """
44
- return await convert_to("pdf", "csv", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
45
+ return await convert_to("pdf", "csv", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
45
46
 
46
47
  @mcp.tool()
47
48
  async def pdf_to_text(
@@ -55,12 +56,13 @@ async def pdf_to_text(
55
56
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
56
57
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
57
58
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
59
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
58
60
  ) -> BaseResponse:
59
61
  """
60
62
  Convert PDF and scanned images to text with layout preserved.
61
63
  Ref: https://developer.pdf.co/api-reference/pdf-to-text/basic.md
62
64
  """
63
- return await convert_to("pdf", "text", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
65
+ return await convert_to("pdf", "text", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
64
66
 
65
67
  @mcp.tool()
66
68
  async def pdf_to_xls(
@@ -74,12 +76,13 @@ async def pdf_to_xls(
74
76
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
75
77
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
76
78
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
79
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
77
80
  ) -> BaseResponse:
78
81
  """
79
82
  Convert PDF and scanned images to XLS (Excel 97-2003) format.
80
83
  Ref: https://developer.pdf.co/api-reference/pdf-to-excel/xls.md
81
84
  """
82
- return await convert_to("pdf", "xls", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
85
+ return await convert_to("pdf", "xls", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
83
86
 
84
87
  @mcp.tool()
85
88
  async def pdf_to_xlsx(
@@ -93,12 +96,13 @@ async def pdf_to_xlsx(
93
96
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
94
97
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
95
98
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
99
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
96
100
  ) -> BaseResponse:
97
101
  """
98
102
  Convert PDF and scanned images to XLSX (Excel 2007+) format.
99
103
  Ref: https://developer.pdf.co/api-reference/pdf-to-excel/xlsx.md
100
104
  """
101
- return await convert_to("pdf", "xlsx", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
105
+ return await convert_to("pdf", "xlsx", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
102
106
 
103
107
  @mcp.tool()
104
108
  async def pdf_to_xml(
@@ -112,12 +116,13 @@ async def pdf_to_xml(
112
116
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
113
117
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
114
118
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
119
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
115
120
  ) -> BaseResponse:
116
121
  """
117
122
  Convert PDF and scanned images to XML format.
118
123
  Ref: https://developer.pdf.co/api-reference/pdf-to-xml.md
119
124
  """
120
- return await convert_to("pdf", "xml", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
125
+ return await convert_to("pdf", "xml", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
121
126
 
122
127
  @mcp.tool()
123
128
  async def pdf_to_html(
@@ -131,12 +136,13 @@ async def pdf_to_html(
131
136
  line_grouping: str = Field(description="Enables line grouping within table cells when set to '1'. (Optional)", default="0"),
132
137
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
133
138
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
139
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
134
140
  ) -> BaseResponse:
135
141
  """
136
142
  Convert PDF and scanned images to HTML format.
137
143
  Ref: https://developer.pdf.co/api-reference/pdf-to-html.md
138
144
  """
139
- return await convert_to("pdf", "html", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
145
+ return await convert_to("pdf", "html", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
140
146
 
141
147
  @mcp.tool()
142
148
  async def pdf_to_image(
@@ -151,6 +157,7 @@ async def pdf_to_image(
151
157
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
152
158
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
153
159
  type: str = Field(description="Type of image to convert to. (jpg, png, webp, tiff) (Optional)", default="jpg", choices=["jpg", "png", "webp", "tiff"]),
160
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
154
161
  ) -> BaseResponse:
155
162
  """
156
163
  Convert PDF and scanned images to various image formats (JPG, PNG, WebP, TIFF).
@@ -160,7 +167,7 @@ async def pdf_to_image(
160
167
  - https://developer.pdf.co/api-reference/pdf-to-image/webp.md
161
168
  - https://developer.pdf.co/api-reference/pdf-to-image/tiff.md
162
169
  """
163
- return await convert_to("pdf", type, ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name))
170
+ return await convert_to("pdf", type, ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, unwrap=unwrap, rect=rect, lang=lang, line_grouping=line_grouping, password=password, name=name, api_key=api_key))
164
171
 
165
172
  @mcp.tool()
166
173
  async def document_to_pdf(
@@ -170,12 +177,13 @@ async def document_to_pdf(
170
177
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
171
178
  pages: str = Field(description="Comma-separated page indices (e.g., '0, 1, 2-' or '1, 3-7'). Use '!' for inverted page numbers (e.g., '!0' for last page). Processes all pages if None. (Optional)", default=""),
172
179
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
180
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
173
181
  ) -> BaseResponse:
174
182
  """
175
183
  Convert various document types (DOC, DOCX, RTF, TXT, XLS, XLSX, CSV, HTML, JPG, PNG, TIFF, WEBP) into PDF.
176
184
  Ref: https://developer.pdf.co/api-reference/pdf-from-document/doc.md
177
185
  """
178
- return await convert_from("pdf", "doc", ConversionParams(url=url, autosize=autosize, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name))
186
+ return await convert_from("pdf", "doc", ConversionParams(url=url, autosize=autosize, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name, api_key=api_key))
179
187
 
180
188
  @mcp.tool()
181
189
  async def csv_to_pdf(
@@ -185,12 +193,13 @@ async def csv_to_pdf(
185
193
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
186
194
  pages: str = Field(description="Comma-separated page indices (e.g., '0, 1, 2-' or '1, 3-7'). Use '!' for inverted page numbers (e.g., '!0' for last page). Processes all pages if None. (Optional)", default=""),
187
195
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
196
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
188
197
  ) -> BaseResponse:
189
198
  """
190
199
  Convert CSV or spreadsheet files (XLS, XLSX) to PDF.
191
200
  Ref: https://developer.pdf.co/api-reference/pdf-from-document/csv.md
192
201
  """
193
- return await convert_from("pdf", "csv", ConversionParams(url=url, autosize=autosize, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name))
202
+ return await convert_from("pdf", "csv", ConversionParams(url=url, autosize=autosize, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name, api_key=api_key))
194
203
 
195
204
  @mcp.tool()
196
205
  async def image_to_pdf(
@@ -199,13 +208,14 @@ async def image_to_pdf(
199
208
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
200
209
  pages: str = Field(description="Comma-separated page indices (e.g., '0, 1, 2-' or '1, 3-7'). Use '!' for inverted page numbers (e.g., '!0' for last page). Processes all pages if None. (Optional)", default=""),
201
210
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
211
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
202
212
  ) -> BaseResponse:
203
213
  """
204
214
  Convert various image formats (JPG, PNG, TIFF) to PDF.
205
215
  Ref: https://developer.pdf.co/api-reference/pdf-from-image.md
206
216
  ```
207
217
  """
208
- return await convert_from("pdf", "image", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name))
218
+ return await convert_from("pdf", "image", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, pages=pages, name=name, api_key=api_key))
209
219
 
210
220
  @mcp.tool()
211
221
  async def webpage_to_pdf(
@@ -221,6 +231,7 @@ async def webpage_to_pdf(
221
231
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
222
232
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
223
233
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
234
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
224
235
  ) -> BaseResponse:
225
236
  """
226
237
  Convert external webpage URL to PDF.
@@ -237,7 +248,7 @@ async def webpage_to_pdf(
237
248
  ```html
238
249
  <span style='font-size:10px'>Page <span class='pageNumber'></span> of <span class='totalPages'></span>.</span>
239
250
  """
240
- return await convert_from("pdf", "url", ConversionParams(url=url, margins=margins, paperSize=paperSize, orientation=orientation, printBackground=printBackground, mediaType=mediaType, DoNotWaitFullLoad=DoNotWaitFullLoad, header=header, footer=footer, httpusername=httpusername, httppassword=httppassword, name=name))
251
+ return await convert_from("pdf", "url", ConversionParams(url=url, margins=margins, paperSize=paperSize, orientation=orientation, printBackground=printBackground, mediaType=mediaType, DoNotWaitFullLoad=DoNotWaitFullLoad, header=header, footer=footer, httpusername=httpusername, httppassword=httppassword, name=name, api_key=api_key))
241
252
 
242
253
  @mcp.tool()
243
254
  async def html_to_pdf(
@@ -253,6 +264,7 @@ async def html_to_pdf(
253
264
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
254
265
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
255
266
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
267
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
256
268
  ) -> BaseResponse:
257
269
  """
258
270
  Convert HTML to PDF.
@@ -269,7 +281,7 @@ async def html_to_pdf(
269
281
  ```html
270
282
  <span style='font-size:10px'>Page <span class='pageNumber'></span> of <span class='totalPages'></span>.</span>
271
283
  """
272
- return await convert_from("pdf", "html", ConversionParams(html=html, margins=margins, paperSize=paperSize, orientation=orientation, printBackground=printBackground, mediaType=mediaType, DoNotWaitFullLoad=DoNotWaitFullLoad, header=header, footer=footer, httpusername=httpusername, httppassword=httppassword, name=name))
284
+ return await convert_from("pdf", "html", ConversionParams(html=html, margins=margins, paperSize=paperSize, orientation=orientation, printBackground=printBackground, mediaType=mediaType, DoNotWaitFullLoad=DoNotWaitFullLoad, header=header, footer=footer, httpusername=httpusername, httppassword=httppassword, name=name, api_key=api_key))
273
285
 
274
286
  @mcp.tool()
275
287
  async def email_to_pdf(
@@ -279,12 +291,13 @@ async def email_to_pdf(
279
291
  margins: str = Field(description="Set to CSS style margins like 10px, 5mm, 5in for all sides or 5px 5px 5px 5px (the order of margins is top, right, bottom, left). (Optional)", default=""),
280
292
  paperSize: str = Field(description="A4 is set by default. Can be Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6 or a custom size. Custom size can be set in px (pixels), mm or in (inches) with width and height separated by space like this: 200 300, 200px 300px, 200mm 300mm, 20cm 30cm or 6in 8in. (Optional)", default=""),
281
293
  orientation: str = Field(description="Set to Portrait or Landscape. Portrait is set by default. (Optional)", default=""),
294
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
282
295
  ) -> BaseResponse:
283
296
  """
284
297
  Convert email to PDF.
285
298
  Ref: https://developer.pdf.co/api-reference/pdf-from-email.md
286
299
  """
287
- return await convert_from("pdf", "email", ConversionParams(url=url, embedAttachments=embedAttachments, convertAttachments=convertAttachments, margins=margins, paperSize=paperSize, orientation=orientation))
300
+ return await convert_from("pdf", "email", ConversionParams(url=url, embedAttachments=embedAttachments, convertAttachments=convertAttachments, margins=margins, paperSize=paperSize, orientation=orientation, api_key=api_key))
288
301
 
289
302
  @mcp.tool()
290
303
  async def excel_to_csv(
@@ -293,12 +306,13 @@ async def excel_to_csv(
293
306
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
294
307
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
295
308
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
309
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
296
310
  ) -> BaseResponse:
297
311
  """
298
312
  Convert Excel(XLS, XLSX) to CSV.
299
313
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/csv.md
300
314
  """
301
- return await convert_to("xls", "csv", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
315
+ return await convert_to("xls", "csv", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
302
316
 
303
317
  @mcp.tool()
304
318
  async def excel_to_json(
@@ -307,12 +321,13 @@ async def excel_to_json(
307
321
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
308
322
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
309
323
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
324
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
310
325
  ) -> BaseResponse:
311
326
  """
312
327
  Convert Excel(XLS, XLSX) to JSON.
313
328
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/json.md
314
329
  """
315
- return await convert_to("xls", "json", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
330
+ return await convert_to("xls", "json", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
316
331
 
317
332
  @mcp.tool()
318
333
  async def excel_to_html(
@@ -321,12 +336,13 @@ async def excel_to_html(
321
336
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
322
337
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
323
338
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
339
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
324
340
  ) -> BaseResponse:
325
341
  """
326
342
  Convert Excel(XLS, XLSX) to HTML.
327
343
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/html.md
328
344
  """
329
- return await convert_to("xls", "html", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
345
+ return await convert_to("xls", "html", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
330
346
 
331
347
  @mcp.tool()
332
348
  async def excel_to_txt(
@@ -335,12 +351,13 @@ async def excel_to_txt(
335
351
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
336
352
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
337
353
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
354
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
338
355
  ) -> BaseResponse:
339
356
  """
340
357
  Convert Excel(XLS, XLSX) to TXT.
341
358
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/text.md
342
359
  """
343
- return await convert_to("xls", "txt", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
360
+ return await convert_to("xls", "txt", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
344
361
 
345
362
  @mcp.tool()
346
363
  async def excel_to_xml(
@@ -349,12 +366,13 @@ async def excel_to_xml(
349
366
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
350
367
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
351
368
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
369
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
352
370
  ) -> BaseResponse:
353
371
  """
354
372
  Convert Excel(XLS, XLSX) to XML.
355
373
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/xml.md
356
374
  """
357
- return await convert_to("xls", "xml", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
375
+ return await convert_to("xls", "xml", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
358
376
 
359
377
  @mcp.tool()
360
378
  async def excel_to_pdf(
@@ -363,9 +381,10 @@ async def excel_to_pdf(
363
381
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
364
382
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
365
383
  worksheetIndex: str = Field(description="Index of the worksheet to convert. (Optional)", default=""),
384
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
366
385
  ) -> BaseResponse:
367
386
  """
368
387
  Convert Excel(XLS, XLSX) to PDF.
369
388
  Ref: https://developer.pdf.co/api-reference/convert-from-excel/pdf.md
370
389
  """
371
- return await convert_to("xls", "pdf", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex))
390
+ return await convert_to("xls", "pdf", ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name, worksheetIndex=worksheetIndex, api_key=api_key))
@@ -10,6 +10,7 @@ async def pdf_info_reader(
10
10
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
11
11
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
12
12
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
13
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
13
14
  ) -> BaseResponse:
14
15
  """
15
16
  Get detailed information about a PDF document - number of pages, metadata, security, form fields, and more.
@@ -22,4 +23,4 @@ async def pdf_info_reader(
22
23
  password=password,
23
24
  )
24
25
 
25
- return await get_pdf_info(params)
26
+ return await get_pdf_info(params, api_key=api_key)
@@ -19,6 +19,7 @@ async def pdf_add_annotations_images_fields(
19
19
  expiration: int = Field(description="Set the expiration time for the output link in minutes. After this specified duration, any generated output file(s) will be automatically deleted. (Optional)", default=60),
20
20
  encrypt: bool = Field(description="Encrypt output file. (Optional)", default=False),
21
21
  flatten: bool = Field(description="Flatten filled form fields and annotations into PDF content. Set to true to disable editing of filled form fields in the output PDF. (Optional)", default=False),
22
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
22
23
  ) -> BaseResponse:
23
24
  """
24
25
  Add text, images, forms, other PDFs, fill forms, links to external sites and external PDF files. You can update or modify PDF and scanned PDF files.
@@ -68,4 +69,4 @@ async def pdf_add_annotations_images_fields(
68
69
  if flatten:
69
70
  add_params["flatten"] = flatten
70
71
 
71
- return await pdf_add(params, **add_params)
72
+ return await pdf_add(params, **add_params, api_key=api_key)
@@ -8,18 +8,19 @@ from pydantic import Field
8
8
  @mcp.tool()
9
9
  async def ai_invoice_parser(
10
10
  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."),
11
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
11
12
  ) -> BaseResponse:
12
13
  """
13
14
  AI Invoice Parser: Extracts data from invoices using AI.
14
15
  Ref: https://developer.pdf.co/api-reference/ai-invoice-parser.md
15
16
  """
16
17
 
17
- # Pass arguments directly; ConversionParams now handles Optional[str] with default=None
18
+ # Pass arguments directly; ConversionParams now handles str with default=None
18
19
  params = ConversionParams(
19
20
  url=url,
20
21
  )
21
22
 
22
- return await parse_invoice(params)
23
+ return await parse_invoice(params, api_key=api_key)
23
24
 
24
25
 
25
26
  @mcp.tool()
@@ -28,6 +29,7 @@ async def extract_attachments(
28
29
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
29
30
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
30
31
  password: str = Field(description="Password of PDF file. (Optional)", default=""),
32
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
31
33
  ) -> BaseResponse:
32
34
  """
33
35
  Extracts attachments from a source PDF file.
@@ -39,4 +41,4 @@ async def extract_attachments(
39
41
  httppassword=httppassword if httppassword else None,
40
42
  password=password if password else None,
41
43
  )
42
- return await extract_pdf_attachments(params)
44
+ return await extract_pdf_attachments(params, api_key=api_key)
@@ -7,13 +7,14 @@ from pydantic import Field
7
7
 
8
8
  @mcp.tool()
9
9
  async def upload_file(
10
- file_path: str = Field(description="The absolute path to the file to upload"),
10
+ file_path: str = Field(description="The absolute path to the file to upload"),
11
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None)
11
12
  ) -> BaseResponse:
12
13
  """
13
14
  Upload a file to the PDF.co API
14
15
  """
15
16
  try:
16
- async with PDFCoClient() as client:
17
+ async with PDFCoClient(api_key=api_key) as client:
17
18
  response = await client.post(
18
19
  "/v1/file/upload",
19
20
  files={
@@ -11,6 +11,7 @@ async def read_pdf_forms_info(
11
11
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
12
12
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
13
13
  password: str = Field(description="Password of PDF file. (Optional)", default=""),
14
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
14
15
  ) -> BaseResponse:
15
16
  """
16
17
  Extracts information about fillable PDF fields from an input PDF file.
@@ -23,7 +24,7 @@ async def read_pdf_forms_info(
23
24
  password=password,
24
25
  )
25
26
 
26
- return await get_pdf_form_fields_info(params)
27
+ return await get_pdf_form_fields_info(params, api_key=api_key)
27
28
 
28
29
  @mcp.tool(name="fill_forms")
29
30
  async def fill_pdf_forms(
@@ -32,6 +33,7 @@ async def fill_pdf_forms(
32
33
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
33
34
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
34
35
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
36
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
35
37
  ) -> BaseResponse:
36
38
  """
37
39
  Fill existing form fields in a PDF document.
@@ -56,7 +58,7 @@ async def fill_pdf_forms(
56
58
  name=name,
57
59
  )
58
60
 
59
- return await fill_pdf_form_fields(params, fields=fields)
61
+ return await fill_pdf_form_fields(params, fields=fields, api_key=api_key)
60
62
 
61
63
  @mcp.tool(name="create_fillable_forms")
62
64
  async def create_fillable_forms(
@@ -65,6 +67,7 @@ async def create_fillable_forms(
65
67
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
66
68
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
67
69
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
70
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
68
71
  ) -> BaseResponse:
69
72
  """
70
73
  Create new fillable form elements in a PDF document.
@@ -99,4 +102,4 @@ async def create_fillable_forms(
99
102
  name=name,
100
103
  )
101
104
 
102
- return await fill_pdf_form_fields(params, annotations=annotations)
105
+ return await fill_pdf_form_fields(params, annotations=annotations, api_key=api_key)
@@ -8,7 +8,8 @@ from pydantic import Field
8
8
 
9
9
  @mcp.tool()
10
10
  async def get_job_check(
11
- job_id: str = Field(description="The ID of the job to get the status of")
11
+ job_id: str = Field(description="The ID of the job to get the status of"),
12
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None)
12
13
  ) -> BaseResponse:
13
14
  """
14
15
  Check the status and results of a job
@@ -20,7 +21,7 @@ async def get_job_check(
20
21
  - unknown: unknown background job id. Available only when force is set to true for input request.
21
22
  """
22
23
  try:
23
- async with PDFCoClient() as client:
24
+ async with PDFCoClient(api_key=api_key) as client:
24
25
  response = await client.post("/v1/job/check", json={
25
26
  "jobId": job_id,
26
27
  })
@@ -42,7 +43,8 @@ async def get_job_check(
42
43
  async def wait_job_completion(
43
44
  job_id: str = Field(description="The ID of the job to get the status of"),
44
45
  interval: int = Field(description="The interval to check the status of the job (seconds)", default=1),
45
- timeout: int = Field(description="The timeout to wait for the job to complete (seconds)", default=300)
46
+ timeout: int = Field(description="The timeout to wait for the job to complete (seconds)", default=300),
47
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None)
46
48
  ) -> BaseResponse:
47
49
  """
48
50
  Wait for a job to complete
@@ -52,7 +54,7 @@ async def wait_job_completion(
52
54
  credits_used = 0
53
55
  credits_remaining = 0
54
56
  while True:
55
- response = await get_job_check(job_id)
57
+ response = await get_job_check(job_id, api_key=api_key)
56
58
  job_check_count += 1
57
59
  credits_used += response.credits_used
58
60
  credits_remaining = response.credits_remaining
@@ -11,12 +11,13 @@ async def pdf_merge(
11
11
  httpusername: str = Field(description="HTTP auth user name if required to access source url. (Optional)", default=""),
12
12
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
13
13
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
14
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
14
15
  ) -> BaseResponse:
15
16
  """
16
17
  Merge PDF from two or more PDF, DOC, XLS, images, even ZIP with documents and images into a new PDF.
17
18
  Ref: https://developer.pdf.co/api-reference/merge/various-files.md
18
19
  """
19
- return await merge_pdf(ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name))
20
+ return await merge_pdf(ConversionParams(url=url, httpusername=httpusername, httppassword=httppassword, name=name), api_key=api_key)
20
21
 
21
22
  @mcp.tool()
22
23
  async def pdf_split(
@@ -26,6 +27,7 @@ async def pdf_split(
26
27
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
27
28
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
28
29
  name: str = Field(description="Base file name for the generated output files. (Optional)", default=""),
30
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
29
31
  ) -> BaseResponse:
30
32
  """
31
33
  Split a PDF into multiple PDF files using page indexes or page ranges.
@@ -40,4 +42,4 @@ async def pdf_split(
40
42
  name=name,
41
43
  )
42
44
 
43
- return await split_pdf(params)
45
+ return await split_pdf(params, api_key=api_key)
@@ -15,6 +15,7 @@ async def find_text(
15
15
  wordMatchingMode: str = Field(description="Values can be either SmartMatch, ExactMatch, or None. (Optional)", default=None),
16
16
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
17
17
  regexSearch: bool = Field(description="Set to True to enable regular expressions in the search string. (Optional)", default=False),
18
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
18
19
  ) -> BaseResponse:
19
20
  """
20
21
  Find text in PDF and get coordinates. Supports regular expressions.
@@ -28,7 +29,7 @@ async def find_text(
28
29
  password=password,
29
30
  )
30
31
 
31
- return await find_text_in_pdf(params, searchString, regexSearch, wordMatchingMode)
32
+ return await find_text_in_pdf(params, searchString, regexSearch, wordMatchingMode, api_key=api_key)
32
33
 
33
34
 
34
35
  @mcp.tool(name="find_table")
@@ -38,6 +39,7 @@ async def find_table(
38
39
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
39
40
  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=""),
40
41
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
42
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
41
43
  ) -> BaseResponse:
42
44
  """
43
45
  Find tables in PDF and get their coordinates.
@@ -51,4 +53,4 @@ async def find_table(
51
53
  password=password,
52
54
  )
53
55
 
54
- return await find_table_in_pdf(params)
56
+ return await find_table_in_pdf(params, api_key=api_key)
@@ -14,6 +14,7 @@ async def pdf_make_searchable(
14
14
  pages: str = Field(description="Comma-separated page indices (e.g., '0, 1, 2-' or '1, 3-7'). Use '!' for inverted page numbers (e.g., '!0' for last page). Processes all pages if None. (Optional)", default=""),
15
15
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
16
16
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
17
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
17
18
  ) -> BaseResponse:
18
19
  """
19
20
  Convert scanned PDF documents or image files into a text-searchable PDF.
@@ -30,7 +31,7 @@ async def pdf_make_searchable(
30
31
  name=name,
31
32
  )
32
33
 
33
- return await make_pdf_searchable(params)
34
+ return await make_pdf_searchable(params, api_key=api_key)
34
35
 
35
36
 
36
37
  @mcp.tool()
@@ -41,6 +42,7 @@ async def pdf_make_unsearchable(
41
42
  pages: str = Field(description="Comma-separated page indices (e.g., '0, 1, 2-' or '1, 3-7'). Use '!' for inverted page numbers (e.g., '!0' for last page). Processes all pages if None. (Optional)", default=""),
42
43
  password: str = Field(description="Password of the PDF file. (Optional)", default=""),
43
44
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
45
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
44
46
  ) -> BaseResponse:
45
47
  """
46
48
  Make existing PDF document non-searchable by removing the text layer from it.
@@ -55,4 +57,4 @@ async def pdf_make_unsearchable(
55
57
  name=name,
56
58
  )
57
59
 
58
- return await make_pdf_unsearchable(params)
60
+ return await make_pdf_unsearchable(params, api_key=api_key)
@@ -23,6 +23,7 @@ async def pdf_add_password(
23
23
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
24
24
  password: str = Field(description="Password of the PDF file if it's already password-protected. (Optional)", default=""),
25
25
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
26
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
26
27
  ) -> BaseResponse:
27
28
  """
28
29
  Add password protection to a PDF file.
@@ -70,7 +71,7 @@ async def pdf_add_password(
70
71
  if print_quality is not None:
71
72
  additional_params["PrintQuality"] = print_quality
72
73
 
73
- return await add_pdf_password(params, **additional_params)
74
+ return await add_pdf_password(params, **additional_params, api_key=api_key)
74
75
 
75
76
 
76
77
  @mcp.tool()
@@ -80,6 +81,7 @@ async def pdf_remove_password(
80
81
  httppassword: str = Field(description="HTTP auth password if required to access source url. (Optional)", default=""),
81
82
  password: str = Field(description="Password of the PDF file to be removed. (Optional)", default=""),
82
83
  name: str = Field(description="File name for the generated output. (Optional)", default=""),
84
+ api_key: str = Field(description="PDF.co API key. If not provided, will use X_API_KEY environment variable. (Optional)", default=None),
83
85
  ) -> BaseResponse:
84
86
  """
85
87
  Remove password protection from a PDF file.
@@ -93,4 +95,4 @@ async def pdf_remove_password(
93
95
  name=name,
94
96
  )
95
97
 
96
- return await remove_pdf_password(params)
98
+ return await remove_pdf_password(params, api_key=api_key)
@@ -1,10 +1,11 @@
1
1
  [project]
2
2
  name = "pdfco-mcp"
3
- version = "0.0.2"
3
+ version = "0.0.3"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  dependencies = [
8
+ "fastmcp>=2.6.1",
8
9
  "httpx>=0.28.1",
9
10
  "langchain-community>=0.3.21",
10
11
  "langchain[google-genai]>=0.3.23",
@@ -20,4 +21,4 @@ requires = ["hatchling"]
20
21
  build-backend = "hatchling.build"
21
22
 
22
23
  [tool.hatch.build.targets.wheel]
23
- packages = ["pdfco"]
24
+ packages = ["pdfco"]
@@ -109,6 +109,18 @@ wheels = [
109
109
  { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 },
110
110
  ]
111
111
 
112
+ [[package]]
113
+ name = "authlib"
114
+ version = "1.6.0"
115
+ source = { registry = "https://pypi.org/simple" }
116
+ dependencies = [
117
+ { name = "cryptography" },
118
+ ]
119
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/9d/b1e08d36899c12c8b894a44a5583ee157789f26fc4b176f8e4b6217b56e1/authlib-1.6.0.tar.gz", hash = "sha256:4367d32031b7af175ad3a323d571dc7257b7099d55978087ceae4a0d88cd3210", size = 158371 }
120
+ wheels = [
121
+ { url = "https://files.pythonhosted.org/packages/84/29/587c189bbab1ccc8c86a03a5d0e13873df916380ef1be461ebe6acebf48d/authlib-1.6.0-py2.py3-none-any.whl", hash = "sha256:91685589498f79e8655e8a8947431ad6288831d643f11c55c2143ffcc738048d", size = 239981 },
122
+ ]
123
+
112
124
  [[package]]
113
125
  name = "cachetools"
114
126
  version = "5.5.2"
@@ -216,6 +228,41 @@ wheels = [
216
228
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
217
229
  ]
218
230
 
231
+ [[package]]
232
+ name = "cryptography"
233
+ version = "45.0.3"
234
+ source = { registry = "https://pypi.org/simple" }
235
+ dependencies = [
236
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
237
+ ]
238
+ sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738 }
239
+ wheels = [
240
+ { url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239 },
241
+ { url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541 },
242
+ { url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275 },
243
+ { url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173 },
244
+ { url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150 },
245
+ { url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473 },
246
+ { url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890 },
247
+ { url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300 },
248
+ { url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483 },
249
+ { url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714 },
250
+ { url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752 },
251
+ { url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465 },
252
+ { url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892 },
253
+ { url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181 },
254
+ { url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370 },
255
+ { url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839 },
256
+ { url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324 },
257
+ { url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447 },
258
+ { url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576 },
259
+ { url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308 },
260
+ { url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125 },
261
+ { url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038 },
262
+ { url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070 },
263
+ { url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005 },
264
+ ]
265
+
219
266
  [[package]]
220
267
  name = "dataclasses-json"
221
268
  version = "0.6.7"
@@ -229,6 +276,38 @@ wheels = [
229
276
  { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 },
230
277
  ]
231
278
 
279
+ [[package]]
280
+ name = "exceptiongroup"
281
+ version = "1.3.0"
282
+ source = { registry = "https://pypi.org/simple" }
283
+ dependencies = [
284
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
285
+ ]
286
+ sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 }
287
+ wheels = [
288
+ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 },
289
+ ]
290
+
291
+ [[package]]
292
+ name = "fastmcp"
293
+ version = "2.6.1"
294
+ source = { registry = "https://pypi.org/simple" }
295
+ dependencies = [
296
+ { name = "authlib" },
297
+ { name = "exceptiongroup" },
298
+ { name = "httpx" },
299
+ { name = "mcp" },
300
+ { name = "openapi-pydantic" },
301
+ { name = "python-dotenv" },
302
+ { name = "rich" },
303
+ { name = "typer" },
304
+ { name = "websockets" },
305
+ ]
306
+ sdist = { url = "https://files.pythonhosted.org/packages/ad/d2/5bd3cf09b63e51c3e10ece07a1031cfe362cf080bb0155e6b894414301b5/fastmcp-2.6.1.tar.gz", hash = "sha256:212f15a4edf8289e5c3c70796910dc612ef891f84df3257a277457bb761d1362", size = 1585121 }
307
+ wheels = [
308
+ { url = "https://files.pythonhosted.org/packages/d7/33/b452a453f62e9b7f6b3f0f263dffb2fa155bc1cab3c69902f7d32b11d9d1/fastmcp-2.6.1-py3-none-any.whl", hash = "sha256:d83a2fcffa721cbb91b29c738d39de20b54e1b1ebef5be652d6a4956ecac7ad3", size = 125910 },
309
+ ]
310
+
232
311
  [[package]]
233
312
  name = "filetype"
234
313
  version = "1.2.0"
@@ -681,7 +760,7 @@ wheels = [
681
760
 
682
761
  [[package]]
683
762
  name = "mcp"
684
- version = "1.6.0"
763
+ version = "1.9.2"
685
764
  source = { registry = "https://pypi.org/simple" }
686
765
  dependencies = [
687
766
  { name = "anyio" },
@@ -689,13 +768,14 @@ dependencies = [
689
768
  { name = "httpx-sse" },
690
769
  { name = "pydantic" },
691
770
  { name = "pydantic-settings" },
771
+ { name = "python-multipart" },
692
772
  { name = "sse-starlette" },
693
773
  { name = "starlette" },
694
- { name = "uvicorn" },
774
+ { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
695
775
  ]
696
- sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 }
776
+ sdist = { url = "https://files.pythonhosted.org/packages/ea/03/77c49cce3ace96e6787af624611b627b2828f0dca0f8df6f330a10eea51e/mcp-1.9.2.tar.gz", hash = "sha256:3c7651c053d635fd235990a12e84509fe32780cd359a5bbef352e20d4d963c05", size = 333066 }
697
777
  wheels = [
698
- { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 },
778
+ { url = "https://files.pythonhosted.org/packages/5d/a6/8f5ee9da9f67c0fd8933f63d6105f02eabdac8a8c0926728368ffbb6744d/mcp-1.9.2-py3-none-any.whl", hash = "sha256:bc29f7fd67d157fef378f89a4210384f5fecf1168d0feb12d22929818723f978", size = 131083 },
699
779
  ]
700
780
 
701
781
  [package.optional-dependencies]
@@ -820,6 +900,18 @@ wheels = [
820
900
  { url = "https://files.pythonhosted.org/packages/3e/05/eb7eec66b95cf697f08c754ef26c3549d03ebd682819f794cb039574a0a6/numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d", size = 12739119 },
821
901
  ]
822
902
 
903
+ [[package]]
904
+ name = "openapi-pydantic"
905
+ version = "0.5.1"
906
+ source = { registry = "https://pypi.org/simple" }
907
+ dependencies = [
908
+ { name = "pydantic" },
909
+ ]
910
+ sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 }
911
+ wheels = [
912
+ { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
913
+ ]
914
+
823
915
  [[package]]
824
916
  name = "orjson"
825
917
  version = "3.10.16"
@@ -891,9 +983,10 @@ wheels = [
891
983
 
892
984
  [[package]]
893
985
  name = "pdfco-mcp"
894
- version = "0.1.0"
986
+ version = "0.0.2"
895
987
  source = { editable = "." }
896
988
  dependencies = [
989
+ { name = "fastmcp" },
897
990
  { name = "httpx" },
898
991
  { name = "langchain", extra = ["google-genai"] },
899
992
  { name = "langchain-community" },
@@ -903,6 +996,7 @@ dependencies = [
903
996
 
904
997
  [package.metadata]
905
998
  requires-dist = [
999
+ { name = "fastmcp", specifier = ">=2.6.1" },
906
1000
  { name = "httpx", specifier = ">=0.28.1" },
907
1001
  { name = "langchain", extras = ["google-genai"], specifier = ">=0.3.23" },
908
1002
  { name = "langchain-community", specifier = ">=0.3.21" },
@@ -1111,6 +1205,15 @@ wheels = [
1111
1205
  { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
1112
1206
  ]
1113
1207
 
1208
+ [[package]]
1209
+ name = "python-multipart"
1210
+ version = "0.0.20"
1211
+ source = { registry = "https://pypi.org/simple" }
1212
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
1213
+ wheels = [
1214
+ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
1215
+ ]
1216
+
1114
1217
  [[package]]
1115
1218
  name = "pyyaml"
1116
1219
  version = "6.0.2"
@@ -1341,6 +1444,37 @@ wheels = [
1341
1444
  { url = "https://files.pythonhosted.org/packages/5f/38/a5801450940a858c102a7ad9e6150146a25406a119851c993148d56ab041/uvicorn-0.34.1-py3-none-any.whl", hash = "sha256:984c3a8c7ca18ebaad15995ee7401179212c59521e67bfc390c07fa2b8d2e065", size = 62404 },
1342
1445
  ]
1343
1446
 
1447
+ [[package]]
1448
+ name = "websockets"
1449
+ version = "15.0.1"
1450
+ source = { registry = "https://pypi.org/simple" }
1451
+ sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 }
1452
+ wheels = [
1453
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437 },
1454
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096 },
1455
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332 },
1456
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152 },
1457
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096 },
1458
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523 },
1459
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790 },
1460
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165 },
1461
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160 },
1462
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395 },
1463
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841 },
1464
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 },
1465
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 },
1466
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 },
1467
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 },
1468
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 },
1469
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 },
1470
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 },
1471
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 },
1472
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 },
1473
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 },
1474
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 },
1475
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
1476
+ ]
1477
+
1344
1478
  [[package]]
1345
1479
  name = "xxhash"
1346
1480
  version = "3.5.0"
@@ -1,8 +0,0 @@
1
- from pdfco.mcp.server import mcp
2
- from pdfco.mcp.tools.apis import conversion, job, file, modification, form, search, searchable, security, document, extraction, editing
3
-
4
- def main():
5
- mcp.run(transport="stdio")
6
-
7
- if __name__ == "__main__":
8
- main()
@@ -1,3 +0,0 @@
1
- from mcp.server.fastmcp import FastMCP
2
-
3
- mcp = FastMCP("pdfco")
@@ -1,95 +0,0 @@
1
- import sys
2
- from pdfco.mcp.models import BaseResponse, ConversionParams
3
- from pdfco.mcp.services.client import PDFCoClient
4
-
5
- async def convert_to(_from: str, _to: str, params: ConversionParams) -> BaseResponse:
6
- return await request(f'{_from}/convert/to/{_to}', params)
7
-
8
- async def convert_from(_to: str, _from: str, params: ConversionParams) -> BaseResponse:
9
- return await request(f'{_to}/convert/from/{_from}', params)
10
-
11
- async def merge_pdf(params: ConversionParams) -> BaseResponse:
12
- return await request(f'pdf/merge2', params)
13
-
14
- async def split_pdf(params: ConversionParams) -> BaseResponse:
15
- return await request(f'pdf/split', params)
16
-
17
- async def get_pdf_form_fields_info(params: ConversionParams) -> BaseResponse:
18
- return await request('pdf/info/fields', params)
19
-
20
- async def fill_pdf_form_fields(params: ConversionParams, fields: list = None, annotations: list = None) -> BaseResponse:
21
- custom_payload = {}
22
- if fields:
23
- custom_payload["fields"] = fields
24
- if annotations:
25
- custom_payload["annotations"] = annotations
26
- return await request('pdf/edit/add', params, custom_payload=custom_payload)
27
-
28
- async def pdf_add(params: ConversionParams, **kwargs) -> BaseResponse:
29
- """General PDF Add function that supports all PDF Add API parameters"""
30
- custom_payload = {}
31
-
32
- # Add all supported parameters
33
- for key, value in kwargs.items():
34
- if value is not None and value != "":
35
- custom_payload[key] = value
36
-
37
- return await request('pdf/edit/add', params, custom_payload=custom_payload)
38
-
39
- async def find_text_in_pdf(params: ConversionParams, search_string: str, regex_search: bool = False, word_matching_mode: str = None) -> BaseResponse:
40
- custom_payload = {
41
- "searchString": search_string,
42
- "regexSearch": regex_search
43
- }
44
- if word_matching_mode:
45
- custom_payload["wordMatchingMode"] = word_matching_mode
46
- return await request('pdf/find', params, custom_payload=custom_payload)
47
-
48
- async def find_table_in_pdf(params: ConversionParams) -> BaseResponse:
49
- return await request('pdf/find/table', params)
50
-
51
- async def make_pdf_searchable(params: ConversionParams) -> BaseResponse:
52
- return await request('pdf/makesearchable', params)
53
-
54
- async def make_pdf_unsearchable(params: ConversionParams) -> BaseResponse:
55
- return await request('pdf/makeunsearchable', params)
56
-
57
- async def get_pdf_info(params: ConversionParams) -> BaseResponse:
58
- return await request('pdf/info', params)
59
-
60
- async def add_pdf_password(params: ConversionParams, **kwargs) -> BaseResponse:
61
- return await request('pdf/security/add', params, custom_payload=kwargs)
62
-
63
- async def remove_pdf_password(params: ConversionParams) -> BaseResponse:
64
- return await request('pdf/security/remove', params)
65
-
66
- async def parse_invoice(params: ConversionParams) -> BaseResponse:
67
- return await request('ai-invoice-parser', params)
68
-
69
- async def extract_pdf_attachments(params: ConversionParams) -> BaseResponse:
70
- return await request('pdf/attachments/extract', params)
71
-
72
- async def request(endpoint: str, params: ConversionParams, custom_payload: dict = None) -> BaseResponse:
73
- payload = params.parse_payload(async_mode=True)
74
- if custom_payload:
75
- payload.update(custom_payload)
76
-
77
- try:
78
- async with PDFCoClient() as client:
79
- url = f"/v1/{endpoint}"
80
- print(f"Requesting {url} with payload {payload}", file=sys.stderr)
81
- response = await client.post(url, json=payload)
82
- print(f"response: {response}", file=sys.stderr)
83
- json_data = response.json()
84
- return BaseResponse(
85
- status="working",
86
- content=json_data,
87
- credits_used=json_data.get("credits"),
88
- credits_remaining=json_data.get("remainingCredits"),
89
- 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.",
90
- )
91
- except Exception as e:
92
- return BaseResponse(
93
- status="error",
94
- content=f'{type(e)}: {[arg for arg in e.args if arg]}',
95
- )
File without changes
File without changes
File without changes
File without changes
File without changes