lightpdf-aipdf-mcp 0.1.148__py3-none-any.whl → 0.1.150__py3-none-any.whl

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.
@@ -1,1718 +0,0 @@
1
- """LightPDF Agent MCP Server模块"""
2
- # 标准库导入
3
- import asyncio
4
- import os
5
- import sys
6
- import argparse
7
- import json
8
- from typing import List, Dict, Any, Callable, TypeVar, Optional, Union
9
- from urllib.request import url2pathname
10
-
11
- # 第三方库导入
12
- from dotenv import load_dotenv
13
-
14
- # 加载环境变量
15
- load_dotenv()
16
-
17
- # MCP相关导入
18
- from mcp.server.lowlevel import Server, NotificationOptions
19
- from mcp.server.models import InitializationOptions
20
- import mcp.types as types
21
-
22
- # 本地导入
23
- from .common import BaseResult, Logger, FileHandler
24
- from .converter import Converter, ConversionResult
25
- from .editor import Editor, EditResult, EditType
26
- from .translator import Translator, TranslateResult
27
- from .summarizer import Summarizer
28
- from .ocr import OcrClient
29
-
30
- # 类型定义
31
- T = TypeVar('T', bound=BaseResult)
32
- ProcessFunc = Callable[[str], Any]
33
-
34
- def generate_result_report(
35
- results: List[BaseResult]
36
- ) -> str:
37
- """生成通用结果报告
38
-
39
- Args:
40
- results: 结果列表
41
-
42
- Returns:
43
- str: JSON格式的报告文本
44
- """
45
- # 统计结果
46
- success_count = sum(1 for r in results if r.success)
47
- failed_count = len(results) - success_count
48
-
49
- # 构建结果JSON对象
50
- report_obj = {
51
- "total": len(results),
52
- "success_count": success_count,
53
- "failed_count": failed_count,
54
- "success_files": [],
55
- "failed_files": []
56
- }
57
-
58
- for result in results:
59
- if result.success:
60
- # 添加成功的文件信息
61
- file_info = {
62
- "original_name": result.original_name,
63
- "debug": {
64
- "task_id": result.task_id
65
- }
66
- }
67
- if hasattr(result, "summary") and result.summary is not None:
68
- file_info["summary"] = result.summary
69
- file_info["instruction"] = "Return the 'summary' field content directly without any modification or additional processing."
70
- else:
71
- file_info["download_url"] = result.download_url
72
- report_obj["success_files"].append(file_info)
73
- else:
74
- # 添加失败的文件信息
75
- file_info = {
76
- "error_message": result.error_message,
77
- "original_name": result.original_name,
78
- "debug": {
79
- "task_id": result.task_id
80
- }
81
- }
82
- report_obj["failed_files"].append(file_info)
83
-
84
- # 返回JSON字符串
85
- return json.dumps(report_obj, ensure_ascii=False)
86
-
87
- async def process_batch_files(
88
- file_objects: List[Dict[str, str]],
89
- logger: Logger,
90
- process_func: Callable[[str, Optional[str], Optional[str]], T],
91
- operation_desc: Optional[str] = None
92
- ) -> List[T]:
93
- """通用批处理文件函数
94
-
95
- Args:
96
- file_objects: 文件对象列表,每个对象包含path和可选的password及name
97
- logger: 日志记录器
98
- process_func: 处理单个文件的异步函数,接收file_path、password和original_name参数
99
- operation_desc: 操作描述,用于日志记录
100
-
101
- Returns:
102
- List[T]: 处理结果列表
103
- """
104
- if len(file_objects) > 1 and operation_desc:
105
- await logger.log("info", f"开始批量{operation_desc},共 {len(file_objects)} 个文件")
106
-
107
- # 并发处理文件,限制并发数为2
108
- semaphore = asyncio.Semaphore(6)
109
-
110
- async def process_with_semaphore(file_obj: Dict[str, str]) -> T:
111
- async with semaphore:
112
- file_path = file_obj["path"]
113
- password = file_obj.get("password")
114
- original_name = file_obj.get("name")
115
- return await process_func(file_path, password, original_name)
116
-
117
- # 创建任务列表
118
- tasks = [process_with_semaphore(file_obj) for file_obj in file_objects]
119
- return await asyncio.gather(*tasks)
120
- else:
121
- # 单文件处理
122
- file_path = file_objects[0]["path"]
123
- password = file_objects[0].get("password")
124
- original_name = file_objects[0].get("name")
125
- return [await process_func(file_path, password, original_name)]
126
-
127
- async def process_conversion_file(
128
- file_path: str,
129
- format: str,
130
- converter: Converter,
131
- extra_params: Optional[Dict[str, Any]] = None,
132
- password: Optional[str] = None,
133
- original_name: Optional[str] = None
134
- ) -> ConversionResult:
135
- """处理单个文件转换"""
136
- is_page_numbering = format == "number-pdf"
137
-
138
- if is_page_numbering and extra_params:
139
- # 对于添加页码,使用add_page_numbers方法
140
- return await converter.add_page_numbers(
141
- file_path,
142
- extra_params.get("start_num", 1),
143
- extra_params.get("position", "5"),
144
- extra_params.get("margin", 30),
145
- password,
146
- original_name
147
- )
148
- else:
149
- # 处理extra_params
150
- if extra_params is None:
151
- extra_params = {}
152
-
153
- # 参数名称映射:将image_quality映射为image-quality
154
- if "image_quality" in extra_params:
155
- extra_params["image-quality"] = extra_params.get("image_quality")
156
-
157
- # 直接传递 merge_all 参数(如有)
158
- # 其它逻辑交由 converter.convert_file 处理
159
- return await converter.convert_file(file_path, format, extra_params, password, original_name)
160
-
161
- async def process_edit_file(
162
- file_path: str,
163
- edit_type: str,
164
- editor: Editor,
165
- extra_params: Dict[str, Any] = None,
166
- password: Optional[str] = None,
167
- original_name: Optional[str] = None
168
- ) -> EditResult:
169
- """处理单个文件编辑"""
170
- if edit_type == "decrypt":
171
- return await editor.decrypt_pdf(file_path, password, original_name)
172
- elif edit_type == "add_text_watermark":
173
- return await editor.add_text_watermark(
174
- file_path=file_path,
175
- text=extra_params.get("text", "文本水印"),
176
- position=extra_params.get("position", "center"),
177
- opacity=extra_params.get("opacity", 1.0),
178
- range=extra_params.get("range", ""),
179
- layout=extra_params.get("layout", "on"),
180
- font_family=extra_params.get("font_family"),
181
- font_size=extra_params.get("font_size"),
182
- font_color=extra_params.get("font_color"),
183
- password=password,
184
- original_name=original_name
185
- )
186
- elif edit_type == "add_image_watermark":
187
- return await editor.add_image_watermark(
188
- file_path=file_path,
189
- image_url=extra_params.get("image_url"),
190
- position=extra_params.get("position", "center"),
191
- opacity=extra_params.get("opacity", 0.7),
192
- range=extra_params.get("range", ""),
193
- layout=extra_params.get("layout", "on"),
194
- password=password,
195
- original_name=original_name
196
- )
197
- elif edit_type == "encrypt":
198
- return await editor.encrypt_pdf(
199
- file_path=file_path,
200
- password=extra_params.get("password", ""),
201
- provider=extra_params.get("provider", ""),
202
- original_password=password,
203
- original_name=original_name
204
- )
205
- elif edit_type == "compress":
206
- return await editor.compress_pdf(
207
- file_path=file_path,
208
- image_quantity=extra_params.get("image_quantity", 60),
209
- password=password,
210
- original_name=original_name
211
- )
212
- elif edit_type == "split":
213
- return await editor.split_pdf(
214
- file_path=file_path,
215
- pages=extra_params.get("pages", ""),
216
- password=password,
217
- split_type=extra_params.get("split_type", "page"),
218
- merge_all=extra_params.get("merge_all", 1),
219
- original_name=original_name
220
- )
221
- elif edit_type == "merge":
222
- # 对于合并操作,我们需要特殊处理,因为它需要处理多个文件
223
- return EditResult(
224
- success=False,
225
- file_path=file_path,
226
- error_message="合并操作需要使用特殊处理流程",
227
- original_name=original_name
228
- )
229
- elif edit_type == "rotate":
230
- # 从extra_params获取旋转参数列表
231
- rotation_arguments = extra_params.get("rotates", [])
232
-
233
- # 验证旋转参数列表
234
- if not rotation_arguments:
235
- return EditResult(
236
- success=False,
237
- file_path=file_path,
238
- error_message="旋转操作需要至少提供一个旋转参数",
239
- original_name=original_name
240
- )
241
-
242
- # 构建angle_params字典: {"90": "2-4,6-8", "180": "all"}
243
- angle_params = {}
244
- for arg in rotation_arguments:
245
- angle = str(arg.get("angle", 90))
246
- pages = arg.get("pages", "all") or "all" # 确保空字符串转为"all"
247
- angle_params[angle] = pages
248
-
249
- # 直接调用rotate_pdf方法,传入角度参数字典
250
- return await editor.rotate_pdf(
251
- file_path=file_path,
252
- angle_params=angle_params,
253
- password=password,
254
- original_name=original_name
255
- )
256
- elif edit_type == "remove_margin":
257
- # 直接调用remove_margin方法,不需要额外参数
258
- return await editor.remove_margin(
259
- file_path=file_path,
260
- password=password,
261
- original_name=original_name
262
- )
263
- elif edit_type == "extract_image":
264
- # 调用extract_images方法提取图片
265
- return await editor.extract_images(
266
- file_path=file_path,
267
- format=extra_params.get("format", "png"),
268
- password=password,
269
- original_name=original_name
270
- )
271
- else:
272
- return EditResult(
273
- success=False,
274
- file_path=file_path,
275
- error_message=f"不支持的编辑类型: {edit_type}",
276
- original_name=original_name
277
- )
278
-
279
- async def process_tool_call(
280
- logger: Logger,
281
- file_objects: List[Dict[str, str]],
282
- operation_config: Dict[str, Any]
283
- ) -> types.TextContent:
284
- """通用工具调用处理函数
285
-
286
- Args:
287
- logger: 日志记录器
288
- file_objects: 文件对象列表,每个对象包含path和可选的password
289
- operation_config: 操作配置,包括操作类型、格式、参数等
290
-
291
- Returns:
292
- types.TextContent: 包含处理结果的文本内容
293
- """
294
- file_handler = FileHandler(logger)
295
- editor = Editor(logger, file_handler)
296
- extra_params = operation_config.get("extra_params", {})
297
-
298
- # 新增:摘要操作分支
299
- if operation_config.get("is_summarize_operation"):
300
- summarizer = Summarizer(logger, file_handler)
301
-
302
- results = await process_batch_files(
303
- file_objects,
304
- logger,
305
- lambda file_path, password, original_name: summarizer.summarize_pdf(
306
- file_path=file_path,
307
- prompt=extra_params.get("prompt", "Give me a summary of the document."),
308
- language=extra_params.get("language", "en"),
309
- password=password,
310
- original_name=original_name
311
- ),
312
- "PDF摘要"
313
- )
314
- report_msg = generate_result_report(results)
315
-
316
- # 新增:OCR操作分支
317
- elif operation_config.get("is_ocr_operation"):
318
- ocr_client = OcrClient(logger, file_handler)
319
-
320
- results = await process_batch_files(
321
- file_objects,
322
- logger,
323
- lambda file_path, password, original_name: ocr_client.ocr_document(
324
- file_path=file_path,
325
- format=extra_params.get("format", "pdf"),
326
- language=extra_params.get("language", "English,Digits,ChinesePRC"),
327
- password=password,
328
- original_name=original_name
329
- ),
330
- "文档OCR识别"
331
- )
332
- report_msg = generate_result_report(results)
333
-
334
- # 新增:翻译操作分支
335
- elif operation_config.get("is_translate_operation"):
336
- translator = Translator(logger, file_handler)
337
-
338
- results = await process_batch_files(
339
- file_objects,
340
- logger,
341
- lambda file_path, password, original_name: translator.translate_pdf(
342
- file_path=file_path,
343
- source=extra_params.get("source", "auto"),
344
- target=extra_params.get("target"),
345
- output_type=extra_params.get("output_type", "mono"),
346
- password=password,
347
- original_name=original_name
348
- ),
349
- "PDF翻译"
350
- )
351
-
352
- report_msg = generate_result_report(results)
353
-
354
- # 根据操作类型选择不同的处理逻辑
355
- elif operation_config.get("is_edit_operation"):
356
- # 编辑操作
357
- edit_type = operation_config.get("edit_type", "")
358
-
359
- # 获取操作描述
360
- edit_map = {
361
- "decrypt": "解密",
362
- "add_text_watermark": "添加文本水印",
363
- "add_image_watermark": "添加图片水印",
364
- "encrypt": "加密",
365
- "compress": "压缩",
366
- "split": "拆分",
367
- "merge": "合并",
368
- "rotate": "旋转",
369
- "remove_margin": "去除白边"
370
- }
371
- operation_desc = f"PDF{edit_map.get(edit_type, edit_type)}"
372
-
373
- # 处理文件
374
- results = await process_batch_files(
375
- file_objects,
376
- logger,
377
- lambda file_path, password, original_name: process_edit_file(
378
- file_path, edit_type, editor, extra_params, password, original_name
379
- ),
380
- operation_desc
381
- )
382
-
383
- # 生成报告
384
- report_msg = generate_result_report(results)
385
-
386
- else:
387
- # 转换操作
388
- converter = Converter(logger, file_handler)
389
- format = operation_config.get("format", "")
390
-
391
- # 新增:特殊处理PDF转Markdown和TEX(LaTeX)
392
- if format in ("md", "tex"):
393
- oss_map = {
394
- "md": ("oss://pdf2md", "PDF转Markdown"),
395
- "tex": ("oss://pdf2tex", "PDF转LaTeX")
396
- }
397
- oss_url, operation_desc = oss_map[format]
398
-
399
- results = await process_batch_files(
400
- file_objects,
401
- logger,
402
- lambda file_path, password, original_name: editor.edit_pdf(
403
- file_path,
404
- edit_type=EditType.EDIT,
405
- extra_params={"pages": [{"url": oss_url, "oss_file": ""}]},
406
- password=password,
407
- original_name=original_name
408
- ),
409
- operation_desc
410
- )
411
-
412
- report_msg = generate_result_report(results)
413
-
414
- elif format == "pdf":
415
- # 只调用一次process_batch_files,在lambda里分流
416
- async def pdf_convert_dispatcher(file_path, password, original_name):
417
- ext = file_handler.get_file_extension(file_path)
418
- ext_map = {
419
- ".txt": ("oss://txt2pdf", "TXT转PDF"),
420
- ".tex": ("oss://tex2pdf", "LaTeX转PDF")
421
- }
422
- if ext in ext_map:
423
- oss_url, operation_desc = ext_map[ext]
424
- return await editor.edit_pdf(
425
- file_path,
426
- edit_type=EditType.EDIT,
427
- extra_params={"pages": [{"url": oss_url, "oss_file": ""}]},
428
- password=password,
429
- original_name=original_name
430
- )
431
- else:
432
- return await process_conversion_file(
433
- file_path, format, converter, extra_params, password, original_name
434
- )
435
-
436
- results = await process_batch_files(
437
- file_objects,
438
- logger,
439
- pdf_convert_dispatcher,
440
- f"转换为 {format} 格式"
441
- )
442
-
443
- report_msg = generate_result_report(results)
444
-
445
- else:
446
- # 获取操作描述
447
- if format == "doc-repair":
448
- operation_desc = "去除水印"
449
- elif format == "number-pdf":
450
- operation_desc = "添加页码"
451
- elif format == "flatten-pdf":
452
- operation_desc = "展平PDF"
453
- elif format == "pdf-replace-text":
454
- operation_desc = "替换文本"
455
- else:
456
- operation_desc = f"转换为 {format} 格式"
457
-
458
- # 处理文件
459
- results = await process_batch_files(
460
- file_objects,
461
- logger,
462
- lambda file_path, password, original_name: process_conversion_file(
463
- file_path, format, converter, extra_params, password, original_name
464
- ),
465
- operation_desc
466
- )
467
-
468
- # 生成报告
469
- report_msg = generate_result_report(results)
470
-
471
- # 如果全部失败,记录错误
472
- if not any(r.success for r in results):
473
- await logger.error(report_msg)
474
-
475
- return types.TextContent(type="text", text=report_msg)
476
-
477
- # 创建Server实例
478
- app = Server(
479
- name="LightPDF_AI_tools",
480
- instructions="LightPDF Document Processing Tools.",
481
- )
482
-
483
- # 定义工具
484
- @app.list_tools()
485
- async def handle_list_tools() -> list[types.Tool]:
486
- return [
487
- types.Tool(
488
- name="convert_document",
489
- description="Document format conversion tool.\n\nPDF can be converted to: DOCX, XLSX, PPTX, images (including long images), HTML, TXT (text extraction), CSV, MD (Markdown), or TEX (LaTeX).\nOther formats (DOCX, XLSX, PPTX, images, CAD, CAJ, OFD, HTML, TEX (LaTeX), TXT, ODT) can be converted to PDF. For HTML to PDF, both local HTML files and any web page URL are supported.\n\nPDF to PDF conversion is not supported.\nOnly entire files can be converted.\n\nImportant distinctions:\n- For content-based PDF creation from LaTeX code, use create_pdf tool instead\n- For extracting embedded images from PDFs, use extract_images tool instead\n- For text recognition from scanned/image PDFs, use ocr_document tool instead\n- PDF-to-TXT conversion here extracts existing text; for scanned documents use OCR\n- PDF-to-image conversion creates images of PDF pages; extract_images gets embedded images\n\nThis tool is strictly for file format conversion only.",
490
- inputSchema={
491
- "type": "object",
492
- "properties": {
493
- "files": {
494
- "type": "array",
495
- "items": {
496
- "type": "object",
497
- "properties": {
498
- "path": {
499
- "type": "string",
500
- "description": "File URL, must include protocol, supports http/https/oss."
501
- },
502
- "password": {
503
- "type": "string",
504
- "description": "Document password, required if the document is password-protected."
505
- },
506
- "name": {
507
- "type": "string",
508
- "description": "Original filename of the document."
509
- }
510
- },
511
- "required": ["path"]
512
- },
513
- "description": "List of files to convert, each containing path and optional password."
514
- },
515
- "format": {
516
- "type": "string",
517
- "description": "Target format. PDF can be converted to: DOCX, XLSX, PPTX, images (including long images), HTML, TXT (text extraction), CSV, MD (Markdown), or TEX (LaTeX). Other formats (DOCX, XLSX, PPTX, images, CAD, CAJ, OFD, HTML, TEX (LaTeX), TXT, ODT) can be converted to PDF. For HTML to PDF, both local HTML files and any web page URL are supported. PDF to PDF conversion is not supported.",
518
- "enum": ["pdf", "docx", "xlsx", "pptx", "jpg", "jpeg", "png", "html", "txt", "csv", "md", "tex"]
519
- },
520
- "merge_all": {
521
- "type": "integer",
522
- "enum": [0, 1],
523
- "default": 0,
524
- "description": "Only effective in the following scenarios (meaning varies by scenario):\n"
525
- "- PDF to Image: 1 = merge all pages into one long image, 0 = output a separate image for each page;\n"
526
- "- Image to PDF: 1 = merge all images into a single PDF file, 0 = create one PDF file per image;\n"
527
- "- PDF to Excel: 1 = merge all pages into one sheet, 0 = each page is converted into a separate sheet.\n"
528
- "This parameter is ignored for other conversion types."
529
- },
530
- "one_page_per_sheet": {
531
- "type": "boolean",
532
- "default": False,
533
- "description": "Only effective when converting Excel to PDF. If true, each sheet will be forced to fit into a single PDF page (even if content overflows; no additional pages will be created). If false, each sheet may be split into multiple PDF pages if the content is too large."
534
- },
535
- "image_quality": {
536
- "type": "integer",
537
- "minimum": 0,
538
- "maximum": 200,
539
- "default": 100,
540
- "description": "Image quality setting, 0-200. Only effective when converting PDF to image formats (jpg, jpeg, png). Higher values produce better quality but larger file sizes."
541
- }
542
- },
543
- "required": ["files", "format"]
544
- }
545
- ),
546
- types.Tool(
547
- name="add_page_numbers",
548
- description="Add page numbers to each page of a PDF document.",
549
- inputSchema={
550
- "type": "object",
551
- "properties": {
552
- "files": {
553
- "type": "array",
554
- "items": {
555
- "type": "object",
556
- "properties": {
557
- "path": {
558
- "type": "string",
559
- "description": "PDF file URL, must include protocol, supports http/https/oss"
560
- },
561
- "password": {
562
- "type": "string",
563
- "description": "PDF document password, required if the document is password-protected"
564
- },
565
- "name": {
566
- "type": "string",
567
- "description": "Original filename of the document"
568
- }
569
- },
570
- "required": ["path"]
571
- },
572
- "description": "List of PDF files to add page numbers to, each containing path and optional password"
573
- },
574
- "start_num": {
575
- "type": "integer",
576
- "description": "Starting page number",
577
- "default": 1,
578
- "minimum": 1
579
- },
580
- "position": {
581
- "type": "string",
582
- "description": "Page number position: 1(top-left), 2(top-center), 3(top-right), 4(bottom-left), 5(bottom-center), 6(bottom-right)",
583
- "enum": ["1", "2", "3", "4", "5", "6"],
584
- "default": "5"
585
- },
586
- "margin": {
587
- "type": "integer",
588
- "description": "Page number margin",
589
- "enum": [10, 30, 60],
590
- "default": 30
591
- }
592
- },
593
- "required": ["files"]
594
- }
595
- ),
596
- types.Tool(
597
- name="remove_watermark",
598
- description="Remove watermarks from PDF files. Watermarks are usually overlaid text or images added for copyright protection or branding purposes. This tool specifically targets watermark removal and is not intended for deleting regular document text content. For deleting normal document text, use the replace_text tool instead.",
599
- inputSchema={
600
- "type": "object",
601
- "properties": {
602
- "files": {
603
- "type": "array",
604
- "items": {
605
- "type": "object",
606
- "properties": {
607
- "path": {
608
- "type": "string",
609
- "description": "PDF file URL, must include protocol, supports http/https/oss"
610
- },
611
- "password": {
612
- "type": "string",
613
- "description": "PDF document password, required if the document is password-protected"
614
- },
615
- "name": {
616
- "type": "string",
617
- "description": "Original filename of the document"
618
- }
619
- },
620
- "required": ["path"]
621
- },
622
- "description": "List of PDF files to remove watermarks from, each containing path and optional password"
623
- }
624
- },
625
- "required": ["files"]
626
- }
627
- ),
628
- types.Tool(
629
- name="add_text_watermark",
630
- description="Add text watermarks to PDF files.",
631
- inputSchema={
632
- "type": "object",
633
- "properties": {
634
- "files": {
635
- "type": "array",
636
- "items": {
637
- "type": "object",
638
- "properties": {
639
- "path": {
640
- "type": "string",
641
- "description": "PDF file URL to add text watermark to, must include protocol, supports http/https/oss"
642
- },
643
- "password": {
644
- "type": "string",
645
- "description": "PDF document password, required if the document is password-protected"
646
- },
647
- "name": {
648
- "type": "string",
649
- "description": "Original filename of the document"
650
- }
651
- },
652
- "required": ["path"]
653
- },
654
- "description": "List of PDF files to add text watermarks to, each containing path and optional password"
655
- },
656
- "text": {
657
- "type": "string",
658
- "description": "Watermark text content"
659
- },
660
- "position": {
661
- "type": "string",
662
- "description": "Text watermark position: top-left(topleft), top-center(top), top-right(topright), left(left), center(center), right(right), bottom-left(bottomleft), bottom(bottom), bottom-right(bottomright), diagonal(diagonal, -45 degrees), reverse-diagonal(reverse-diagonal, 45 degrees)",
663
- "enum": ["topleft", "top", "topright", "left", "center", "right",
664
- "bottomleft", "bottom", "bottomright", "diagonal", "reverse-diagonal"],
665
- "default": "center"
666
- },
667
- "opacity": {
668
- "type": "number",
669
- "description": "Opacity, 0.0-1.0",
670
- "default": 1.0,
671
- "minimum": 0.0,
672
- "maximum": 1.0
673
- },
674
- "range": {
675
- "type": "string",
676
- "description": "Page range, e.g. '1,3,5-7' or '' (empty string or not set) for all pages"
677
- },
678
- "layout": {
679
- "type": "string",
680
- "description": "Layout position: on top of content(on) or under content(under)",
681
- "enum": ["on", "under"],
682
- "default": "on"
683
- },
684
- "font_family": {
685
- "type": "string",
686
- "description": "Font family"
687
- },
688
- "font_size": {
689
- "type": "integer",
690
- "description": "Font size"
691
- },
692
- "font_color": {
693
- "type": "string",
694
- "description": "Font color, e.g. '#ff0000' for red"
695
- }
696
- },
697
- "required": ["files", "text", "position"]
698
- }
699
- ),
700
- types.Tool(
701
- name="add_image_watermark",
702
- description="Add image watermarks to PDF files.",
703
- inputSchema={
704
- "type": "object",
705
- "properties": {
706
- "files": {
707
- "type": "array",
708
- "items": {
709
- "type": "object",
710
- "properties": {
711
- "path": {
712
- "type": "string",
713
- "description": "PDF file URL to add image watermark to, must include protocol, supports http/https/oss"
714
- },
715
- "password": {
716
- "type": "string",
717
- "description": "PDF document password, required if the document is password-protected"
718
- },
719
- "name": {
720
- "type": "string",
721
- "description": "Original filename of the document"
722
- }
723
- },
724
- "required": ["path"]
725
- },
726
- "description": "List of PDF files to add image watermarks to, each containing path and optional password"
727
- },
728
- "image_url": {
729
- "type": "string",
730
- "description": "Image URL for the watermark, must include protocol, supports http/https/oss"
731
- },
732
- "position": {
733
- "type": "string",
734
- "description": "Image watermark position: top-left(topleft), top-center(top), top-right(topright), left(left), center(center), right(right), bottom-left(bottomleft), bottom(bottom), bottom-right(bottomright), diagonal(diagonal, -45 degrees), reverse-diagonal(reverse-diagonal, 45 degrees)",
735
- "enum": ["topleft", "top", "topright", "left", "center", "right",
736
- "bottomleft", "bottom", "bottomright", "diagonal", "reverse-diagonal"],
737
- "default": "center"
738
- },
739
- "opacity": {
740
- "type": "number",
741
- "description": "Opacity, 0.0-1.0",
742
- "default": 0.7,
743
- "minimum": 0.0,
744
- "maximum": 1.0
745
- },
746
- "range": {
747
- "type": "string",
748
- "description": "Page range, e.g. '1,3,5-7' or '' (empty string or not set) for all pages"
749
- },
750
- "layout": {
751
- "type": "string",
752
- "description": "Layout position: on top of content(on) or under content(under)",
753
- "enum": ["on", "under"],
754
- "default": "on"
755
- }
756
- },
757
- "required": ["files", "image_url", "position"]
758
- }
759
- ),
760
- types.Tool(
761
- name="unlock_pdf",
762
- description="Remove password protection from PDF files.",
763
- inputSchema={
764
- "type": "object",
765
- "properties": {
766
- "files": {
767
- "type": "array",
768
- "items": {
769
- "type": "object",
770
- "properties": {
771
- "path": {
772
- "type": "string",
773
- "description": "PDF file URL to decrypt, must include protocol, supports http/https/oss"
774
- },
775
- "password": {
776
- "type": "string",
777
- "description": "PDF document password, required to unlock the document if it is password-protected"
778
- },
779
- "name": {
780
- "type": "string",
781
- "description": "Original filename of the document"
782
- }
783
- },
784
- "required": ["path", "password"]
785
- },
786
- "description": "List of PDF files to decrypt, each containing path and password"
787
- }
788
- },
789
- "required": ["files"]
790
- }
791
- ),
792
- types.Tool(
793
- name="protect_pdf",
794
- description="Add password protection to PDF files. This tool adds a user password (open password) that is required to open and view the PDF document. Note: This is different from restrict_printing which sets permission restrictions - use restrict_printing if you want to control printing permissions specifically.",
795
- inputSchema={
796
- "type": "object",
797
- "properties": {
798
- "files": {
799
- "type": "array",
800
- "items": {
801
- "type": "object",
802
- "properties": {
803
- "path": {
804
- "type": "string",
805
- "description": "PDF file URL to encrypt, must include protocol, supports http/https/oss"
806
- },
807
- "password": {
808
- "type": "string",
809
- "description": "PDF document password, required if the document is password-protected"
810
- },
811
- "name": {
812
- "type": "string",
813
- "description": "Original filename of the document"
814
- }
815
- },
816
- "required": ["path"]
817
- },
818
- "description": "List of PDF files to encrypt, each containing path and optional current password"
819
- },
820
- "password": {
821
- "type": "string",
822
- "description": "New password to set"
823
- }
824
- },
825
- "required": ["files", "password"]
826
- }
827
- ),
828
- types.Tool(
829
- name="compress_pdf",
830
- description="Reduce the size of PDF files.",
831
- inputSchema={
832
- "type": "object",
833
- "properties": {
834
- "files": {
835
- "type": "array",
836
- "items": {
837
- "type": "object",
838
- "properties": {
839
- "path": {
840
- "type": "string",
841
- "description": "PDF file URL to compress, must include protocol, supports http/https/oss"
842
- },
843
- "password": {
844
- "type": "string",
845
- "description": "PDF document password, required if the document is password-protected"
846
- },
847
- "name": {
848
- "type": "string",
849
- "description": "Original filename of the document"
850
- }
851
- },
852
- "required": ["path"]
853
- },
854
- "description": "List of PDF files to compress, each containing path and optional password"
855
- },
856
- "image_quantity": {
857
- "type": "integer",
858
- "description": "Image quality, 1-100, lower values result in higher compression",
859
- "default": 60,
860
- "minimum": 1,
861
- "maximum": 100
862
- }
863
- },
864
- "required": ["files"]
865
- }
866
- ),
867
- types.Tool(
868
- name="split_pdf",
869
- description="Split PDF documents by pages. You can split each page into a separate PDF file, split by specified page ranges, or split by bookmarks/outlines/table of contents/headings (bookmark). Split files can be multiple independent PDF files (returned as a zip package) or merged into a single PDF file.",
870
- inputSchema={
871
- "type": "object",
872
- "properties": {
873
- "files": {
874
- "type": "array",
875
- "items": {
876
- "type": "object",
877
- "properties": {
878
- "path": {
879
- "type": "string",
880
- "description": "PDF file URL to split, must include protocol, supports http/https/oss"
881
- },
882
- "password": {
883
- "type": "string",
884
- "description": "PDF document password, required if the document is password-protected"
885
- },
886
- "name": {
887
- "type": "string",
888
- "description": "Original filename of the document"
889
- }
890
- },
891
- "required": ["path"]
892
- },
893
- "description": "List of PDF files to split, each containing path and optional password"
894
- },
895
- "split_type": {
896
- "type": "string",
897
- "description": "Split type: 'every' (split each page into a separate file), 'page' (split by page ranges), or 'bookmark' (split by PDF bookmarks/outlines/table of contents/headings, each node as a separate PDF file).",
898
- "enum": ["every", "page", "bookmark"]
899
- },
900
- "pages": {
901
- "type": "string",
902
- "description": "Page ranges to split, e.g. '1,3,5-7' or '' (empty for all pages). Required and only valid when split_type is 'page'."
903
- },
904
- "merge_all": {
905
- "type": "integer",
906
- "description": "Whether to merge results into a single PDF file: 1=yes, 0=no (will return a zip package of multiple files). Only valid when split_type is 'page'.",
907
- "enum": [0, 1],
908
- "default": 0
909
- }
910
- },
911
- "required": ["files", "split_type"]
912
- }
913
- ),
914
- types.Tool(
915
- name="merge_pdfs",
916
- description="Merge multiple PDF files into a single PDF file. You must provide at least two files in the 'files' array, otherwise the operation will fail.",
917
- inputSchema={
918
- "type": "object",
919
- "properties": {
920
- "files": {
921
- "type": "array",
922
- "items": {
923
- "type": "object",
924
- "properties": {
925
- "path": {
926
- "type": "string",
927
- "description": "PDF file URL to merge, must include protocol, supports http/https/oss"
928
- },
929
- "password": {
930
- "type": "string",
931
- "description": "PDF document password, required if the document is password-protected"
932
- },
933
- "name": {
934
- "type": "string",
935
- "description": "Original filename of the document"
936
- }
937
- },
938
- "required": ["path"]
939
- },
940
- "description": "List of PDF files to merge (must be at least two), each containing path and optional password"
941
- }
942
- },
943
- "required": ["files"]
944
- }
945
- ),
946
- types.Tool(
947
- name="rotate_pdf",
948
- description="Rotate pages in PDF files.",
949
- inputSchema={
950
- "type": "object",
951
- "properties": {
952
- "files": {
953
- "type": "array",
954
- "items": {
955
- "type": "object",
956
- "properties": {
957
- "path": {
958
- "type": "string",
959
- "description": "PDF file URL to rotate, must include protocol, supports http/https/oss"
960
- },
961
- "password": {
962
- "type": "string",
963
- "description": "PDF document password, required if the document is password-protected"
964
- },
965
- "name": {
966
- "type": "string",
967
- "description": "Original filename of the document"
968
- }
969
- },
970
- "required": ["path"]
971
- },
972
- "description": "List of PDF files to rotate, each containing path and optional password"
973
- },
974
- "rotates": {
975
- "type": "array",
976
- "items": {
977
- "type": "object",
978
- "properties": {
979
- "angle": {
980
- "type": "integer",
981
- "description": "Rotation angle, options are 90, 180, 270",
982
- "enum": [90, 180, 270],
983
- "default": 90
984
- },
985
- "pages": {
986
- "type": "string",
987
- "description": "Specify page ranges to rotate, e.g. '1,3,5-7' or 'all' for all pages",
988
- "default": "all"
989
- }
990
- },
991
- "required": ["angle", "pages"]
992
- },
993
- "description": "Parameter list, each containing rotation angle and page range"
994
- }
995
- },
996
- "required": ["files", "rotates"]
997
- }
998
- ),
999
- types.Tool(
1000
- name="remove_margin",
1001
- description="Remove white margins from PDF files (crop page margins).",
1002
- inputSchema={
1003
- "type": "object",
1004
- "properties": {
1005
- "files": {
1006
- "type": "array",
1007
- "items": {
1008
- "type": "object",
1009
- "properties": {
1010
- "path": {
1011
- "type": "string",
1012
- "description": "PDF file URL to remove margins from, must include protocol, supports http/https/oss"
1013
- },
1014
- "password": {
1015
- "type": "string",
1016
- "description": "PDF document password, required if the document is password-protected"
1017
- },
1018
- "name": {
1019
- "type": "string",
1020
- "description": "Original filename of the document"
1021
- }
1022
- },
1023
- "required": ["path"]
1024
- },
1025
- "description": "List of PDF files to remove margins from, each containing path and optional password"
1026
- }
1027
- },
1028
- "required": ["files"]
1029
- }
1030
- ),
1031
- types.Tool(
1032
- name="extract_images",
1033
- description="Extract embedded image resources from all pages of a PDF, supporting multiple image formats. This tool extracts actual images that are embedded within the PDF file. Note: This is different from convert_document PDF-to-image conversion, which converts PDF pages into image files - use convert_document if you want to convert PDF pages to images.",
1034
- inputSchema={
1035
- "type": "object",
1036
- "properties": {
1037
- "files": {
1038
- "type": "array",
1039
- "items": {
1040
- "type": "object",
1041
- "properties": {
1042
- "path": {
1043
- "type": "string",
1044
- "description": "PDF file URL to extract images from, must include protocol, supports http/https/oss"
1045
- },
1046
- "password": {
1047
- "type": "string",
1048
- "description": "PDF document password, required if the document is password-protected"
1049
- },
1050
- "name": {
1051
- "type": "string",
1052
- "description": "Original filename of the document"
1053
- }
1054
- },
1055
- "required": ["path"]
1056
- },
1057
- "description": "List of PDF files to extract images from, each containing path and optional password"
1058
- },
1059
- "format": {
1060
- "type": "string",
1061
- "description": "Extracted image format",
1062
- "enum": ["bmp", "png", "gif", "tif", "jpg"],
1063
- "default": "png"
1064
- }
1065
- },
1066
- "required": ["files"]
1067
- }
1068
- ),
1069
- types.Tool(
1070
- name="flatten_pdf",
1071
- description="Flatten PDF files (convert editable elements such as text, form fields, annotations, and layers into non-editable static content or fixed content).",
1072
- inputSchema={
1073
- "type": "object",
1074
- "properties": {
1075
- "files": {
1076
- "type": "array",
1077
- "items": {
1078
- "type": "object",
1079
- "properties": {
1080
- "path": {
1081
- "type": "string",
1082
- "description": "PDF file URL to flatten, must include protocol, supports http/https/oss"
1083
- },
1084
- "password": {
1085
- "type": "string",
1086
- "description": "PDF document password, required if the document is password-protected"
1087
- },
1088
- "name": {
1089
- "type": "string",
1090
- "description": "Original filename of the document"
1091
- }
1092
- },
1093
- "required": ["path"]
1094
- },
1095
- "description": "List of PDF files to flatten, each containing path and optional password"
1096
- }
1097
- },
1098
- "required": ["files"]
1099
- }
1100
- ),
1101
- types.Tool(
1102
- name="restrict_printing",
1103
- description="Restrict PDF printing permission. This tool sets permission restrictions (owner password) to control what users can do with the PDF - specifically preventing printing. Note: This is different from protect_pdf which adds a user password to open the document - use protect_pdf if you want to prevent unauthorized access to the document.",
1104
- inputSchema={
1105
- "type": "object",
1106
- "properties": {
1107
- "files": {
1108
- "type": "array",
1109
- "items": {
1110
- "type": "object",
1111
- "properties": {
1112
- "path": {
1113
- "type": "string",
1114
- "description": "PDF file URL to restrict printing, must include protocol, supports http/https/oss"
1115
- },
1116
- "password": {
1117
- "type": "string",
1118
- "description": "PDF document password, required if the document is password-protected"
1119
- },
1120
- "name": {
1121
- "type": "string",
1122
- "description": "Original filename of the document"
1123
- }
1124
- },
1125
- "required": ["path"]
1126
- },
1127
- "description": "List of PDF files to restrict printing, each containing path and optional password"
1128
- },
1129
- "password": {
1130
- "type": "string",
1131
- "description": "New permission password to set"
1132
- }
1133
- },
1134
- "required": ["files", "password"]
1135
- }
1136
- ),
1137
- types.Tool(
1138
- name="resize_pdf",
1139
- description="Resize PDF pages. You can specify the target page size (a0/a1/a2/a3/a4/a5/a6/letter) and/or the image resolution (dpi, e.g., 72). If not set, the corresponding property will not be changed.",
1140
- inputSchema={
1141
- "type": "object",
1142
- "properties": {
1143
- "files": {
1144
- "type": "array",
1145
- "items": {
1146
- "type": "object",
1147
- "properties": {
1148
- "path": {
1149
- "type": "string",
1150
- "description": "PDF file URL to resize, must include protocol, supports http/https/oss"
1151
- },
1152
- "password": {
1153
- "type": "string",
1154
- "description": "PDF document password, required if the document is password-protected"
1155
- },
1156
- "name": {
1157
- "type": "string",
1158
- "description": "Original filename of the document"
1159
- }
1160
- },
1161
- "required": ["path"]
1162
- },
1163
- "description": "List of PDF files to resize, each containing path and optional password"
1164
- },
1165
- "page_size": {
1166
- "type": "string",
1167
- "description": "Target page size. Any valid page size name is supported (e.g., a4, letter, legal, etc.), or use width,height in points (pt, e.g., 595,842). If not set, page size will not be changed."
1168
- },
1169
- "resolution": {
1170
- "type": "integer",
1171
- "description": "Image resolution (dpi), e.g., 72. If not set, resolution will not be changed."
1172
- }
1173
- },
1174
- "required": ["files"]
1175
- }
1176
- ),
1177
- types.Tool(
1178
- name="replace_text",
1179
- description="Replace, edit, or delete regular text content in PDF files. Use this tool to modify or remove normal document text. When new_text is empty, the old_text will be completely deleted from the PDF. Note: This tool is for regular document text only, not for removing watermarks. For watermark removal, use the remove_watermark tool instead.",
1180
- inputSchema={
1181
- "type": "object",
1182
- "properties": {
1183
- "files": {
1184
- "type": "array",
1185
- "items": {
1186
- "type": "object",
1187
- "properties": {
1188
- "path": {
1189
- "type": "string",
1190
- "description": "PDF file URL to replace text in, must include protocol, supports http/https/oss"
1191
- },
1192
- "password": {
1193
- "type": "string",
1194
- "description": "PDF document password, required if the document is password-protected"
1195
- },
1196
- "name": {
1197
- "type": "string",
1198
- "description": "Original filename of the document"
1199
- }
1200
- },
1201
- "required": ["path"]
1202
- },
1203
- "description": "List of PDF files to replace text in, each containing path and optional password"
1204
- },
1205
- "old_text": {
1206
- "type": "string",
1207
- "description": "The text to be replaced or deleted"
1208
- },
1209
- "new_text": {
1210
- "type": "string",
1211
- "description": "The replacement text. If empty, the old_text will be deleted"
1212
- }
1213
- },
1214
- "required": ["files", "old_text", "new_text"]
1215
- }
1216
- ),
1217
- types.Tool(
1218
- name="create_pdf",
1219
- description="Generate PDF documents from text-only instructions or descriptions. The tool creates PDFs based on written prompts such as 'create a business report', 'generate meeting minutes', etc. Only accepts plain text input - no file uploads or multimedia content supported.",
1220
- inputSchema={
1221
- "type": "object",
1222
- "properties": {
1223
- "prompt": {
1224
- "type": "string",
1225
- "description": "A text-only description or instruction of what PDF content to generate (e.g., 'Create a business report about market analysis', 'Generate a technical documentation for API usage'). Must be plain text input only - no file uploads, attachments, images, or multimedia content are supported."
1226
- },
1227
- "filename": {
1228
- "type": "string",
1229
- "description": "The filename for the generated PDF"
1230
- },
1231
- "language": {
1232
- "type": "string",
1233
- "description": "The language for the generated PDF content.",
1234
- "enum": ["zh", "en", "de", "es", "fr", "ja", "pt", "zh-tw", "ar", "cs", "da", "fi", "el", "hu", "it", "nl", "no", "pl", "sv", "tr"]
1235
- },
1236
- "enable_web_search": {
1237
- "type": "boolean",
1238
- "description": "Whether to enable web search to gather additional information for content generation",
1239
- "default": False
1240
- }
1241
- },
1242
- "required": ["prompt", "filename", "language"]
1243
- }
1244
- ),
1245
- types.Tool(
1246
- name="translate_pdf",
1247
- description="Translate only the text in a PDF file into a specified target language and output a new PDF file. All non-text elements (such as images, tables, and layout) will remain unchanged.",
1248
- inputSchema={
1249
- "type": "object",
1250
- "properties": {
1251
- "files": {
1252
- "type": "array",
1253
- "items": {
1254
- "type": "object",
1255
- "properties": {
1256
- "path": {
1257
- "type": "string",
1258
- "description": "PDF file URL, must include protocol, supports http/https/oss."
1259
- },
1260
- "password": {
1261
- "type": "string",
1262
- "description": "PDF document password, required if the document is password-protected."
1263
- },
1264
- "name": {
1265
- "type": "string",
1266
- "description": "Original filename of the document."
1267
- }
1268
- },
1269
- "required": ["path"]
1270
- },
1271
- "description": "List of PDF files to translate, each containing path and optional password."
1272
- },
1273
- "source": {
1274
- "type": "string",
1275
- "description": "Source language. Supports 'auto' for automatic detection.",
1276
- "enum": ["auto", "ar", "bg", "cz", "da", "de", "el", "en", "es", "fi", "fr", "hbs", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "ru", "sl", "sv", "th", "tr", "vi", "zh", "zh-tw"],
1277
- "default": "auto"
1278
- },
1279
- "target": {
1280
- "type": "string",
1281
- "description": "Target language. Must be specified. ar: Arabic, bg: Bulgarian, cz: Czech, da: Danish, de: German, el: Greek, en: English, es: Spanish, fi: Finnish, fr: French, hbs: Croatian, hi: Hindi, hu: Hungarian, id: Indonesian, it: Italian, ja: Japanese, ko: Korean, ms: Malay, nl: Dutch, no: Norwegian, pl: Polish, pt: Portuguese, ru: Russian, sl: Slovenian, sv: Swedish, th: Thai, tr: Turkish, vi: Vietnamese, zh: Simplified Chinese, zh-tw: Traditional Chinese.",
1282
- "enum": ["ar", "bg", "cz", "da", "de", "el", "en", "es", "fi", "fr", "hbs", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "ru", "sl", "sv", "th", "tr", "vi", "zh", "zh-tw"]
1283
- },
1284
- "output_type": {
1285
- "type": "string",
1286
- "description": "Output type: 'mono' for target language only, 'dual' for source/target bilingual output.",
1287
- "enum": ["mono", "dual"],
1288
- "default": "mono"
1289
- }
1290
- },
1291
- "required": ["files", "target"]
1292
- }
1293
- ),
1294
- types.Tool(
1295
- name="ocr_document",
1296
- description="Perform OCR (Optical Character Recognition) on documents to recognize and extract text from scanned PDF documents. Output as the specified format file. Note: Use this tool for scanned documents or image-based PDFs where text needs to be recognized. For regular PDF text extraction, use convert_document PDF-to-TXT conversion instead.",
1297
- inputSchema={
1298
- "type": "object",
1299
- "properties": {
1300
- "files": {
1301
- "type": "array",
1302
- "items": {
1303
- "type": "object",
1304
- "properties": {
1305
- "path": {
1306
- "type": "string",
1307
- "description": "PDF file URL, must include protocol, supports http/https/oss."
1308
- },
1309
- "password": {
1310
- "type": "string",
1311
- "description": "PDF document password, required if the document is password-protected."
1312
- },
1313
- "name": {
1314
- "type": "string",
1315
- "description": "Original filename of the document."
1316
- }
1317
- },
1318
- "required": ["path"]
1319
- },
1320
- "description": "List of files to be recognized, each item contains path and optional password, name."
1321
- },
1322
- "format": {
1323
- "type": "string",
1324
- "description": "Output format, supports pdf/docx/pptx/xlsx/txt, default is pdf.",
1325
- "enum": ["pdf", "docx", "pptx", "xlsx", "txt"],
1326
- "default": "pdf"
1327
- },
1328
- "language": {
1329
- "type": "string",
1330
- "description": "Specify the language(s) or type(s) to recognize, multiple values can be selected and separated by commas. Optional values: Abkhaz/Adyghe/Afrikaans/Agul/Albanian/Altaic/Arabic/Armenian/Awar/Aymara/Azeri/Bashkir/Basque/Belarusian/Bemba/Blackfoot/Breton/Bugotu/Bulgarian/Buryat/Catalan/Chamorro/Chechen/ChinesePRC/ChineseTaiwan/Chukcha/Chuvash/Corsican/CrimeanTatar/Croatian/Crow/Czech/Danish/Dargwa/Dungan/Dutch/English/Eskimo/Esperanto/Estonian/Even/Evenki/Faeroese/Fijian/Finnish/French/Frisian/Friulian/GaelicScottish/Gagauz/Galician/Ganda/German/Greek/Guarani/Hani/Hausa/Hawaiian/Hebrew/Hungarian/Icelandic/Ido/Indonesian/Ingush/Interlingua/Irish/Italian/Japanese/Kabardian/Kalmyk/KarachayBalkar/Karakalpak/Kasub/Kawa/Kazakh/Khakas/Khanty/Kikuyu/Kirgiz/Kongo/Korean/Koryak/Kpelle/Kumyk/Kurdish/Lak/Lappish/Latin/Latvian/LatvianGothic/Lezgin/Lithuanian/Luba/Macedonian/Malagasy/Malay/Malinke/Maltese/Mansi/Maori/Mari/Maya/Miao/Minankabaw/Mohawk/Moldavian/Mongol/Mordvin/Nahuatl/Nenets/Nivkh/Nogay/Norwegian/Nyanja/Occidental/Ojibway/Ossetic/Papiamento/PidginEnglish/Polish/PortugueseBrazilian/PortugueseStandard/Provencal/Quechua/RhaetoRomanic/Romanian/Romany/Ruanda/Rundi/Russian/Samoan/Selkup/SerbianCyrillic/SerbianLatin/Shona/Sioux/Slovak/Slovenian/Somali/Sorbian/Sotho/Spanish/Sunda/Swahili/Swazi/Swedish/Tabassaran/Tagalog/Tahitian/Tajik/Tatar/Thai/Tinpo/Tongan/Tswana/Tun/Turkish/Turkmen/Tuvin/Udmurt/UighurCyrillic/UighurLatin/Ukrainian/UzbekCyrillic/UzbekLatin/Vietnamese/Visayan/Welsh/Wolof/Xhosa/Yakut/Yiddish/Zapotec/Zulu/Basic/C++/Cobol/Fortran/Java/Pascal/Chemistry/Digits/. Default: English,Digits,ChinesePRC",
1331
- "default": "English,Digits,ChinesePRC"
1332
- }
1333
- },
1334
- "required": ["files"]
1335
- }
1336
- ),
1337
- types.Tool(
1338
- name="summarize_document",
1339
- description="Summarize the content of documents and generate a concise abstract based on the user's prompt. The tool extracts and condenses the main ideas or information from the document(s) according to the user's requirements.",
1340
- inputSchema={
1341
- "type": "object",
1342
- "properties": {
1343
- "files": {
1344
- "type": "array",
1345
- "items": {
1346
- "type": "object",
1347
- "properties": {
1348
- "path": {
1349
- "type": "string",
1350
- "description": "PDF file URL, must include protocol, supports http/https/oss."
1351
- },
1352
- "password": {
1353
- "type": "string",
1354
- "description": "PDF document password, required if the document is password-protected."
1355
- },
1356
- "name": {
1357
- "type": "string",
1358
- "description": "Original filename of the document."
1359
- }
1360
- },
1361
- "required": ["path"]
1362
- },
1363
- "description": "List of files to summarize, each containing path and optional password."
1364
- },
1365
- "prompt": {
1366
- "type": "string",
1367
- "description": "User's requirement or instruction for the summary."
1368
- },
1369
- "language": {
1370
- "type": "string",
1371
- "description": "The language in which the summary should be generated. If not set, defaults to the language of the user's current query.",
1372
- "enum": [
1373
- "af","am","ar","as","az","ba","be","bg","bn","bo","br","bs","ca","cs","cy","da","de","el","en","es","et","eu","fa","fi","fo","fr","gl","gu","ha","haw","he","hi","hr","ht","hu","hy","id","is","it","ja","jw","ka","kk","km","kn","ko","la","lb","ln","lo","lt","lv","mg","mi","mk","ml","mn","mr","ms","mt","my","ne","nl","nn","no","oc","pa","pl","ps","pt","ro","ru","sa","sd","si","sk","sl","sn","so","sq","sr","su","sv","sw","ta","te","tg","th","tk","tl","tr","tt","uk","ur","uz","vi","yi","yo","zh"
1374
- ]
1375
- }
1376
- },
1377
- "required": ["files", "prompt", "language"]
1378
- }
1379
- ),
1380
- ]
1381
-
1382
- @app.call_tool()
1383
- async def handle_call_tool(name: str, arguments: dict | None) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
1384
- # 创建日志记录器
1385
- logger = Logger(app.request_context)
1386
-
1387
- # 定义工具配置和默认参数值
1388
- TOOL_CONFIG = {
1389
- "convert_document": {
1390
- "format_key": "format", # 从arguments获取format
1391
- "is_edit_operation": False,
1392
- "param_keys": ["merge_all", "one_page_per_sheet", "image_quality"]
1393
- },
1394
- "remove_watermark": {
1395
- "format": "doc-repair", # 固定format
1396
- "is_edit_operation": False,
1397
- },
1398
- "add_page_numbers": {
1399
- "format": "number-pdf", # 固定format
1400
- "is_edit_operation": False,
1401
- "param_keys": ["start_num", "position", "margin"] # 需要从arguments获取的参数
1402
- },
1403
- "flatten_pdf": {
1404
- "format": "flatten-pdf", # 固定format
1405
- "is_edit_operation": False
1406
- },
1407
- "resize_pdf": {
1408
- "format": "resize-pdf",
1409
- "is_edit_operation": False,
1410
- "param_keys": ["page_size", "resolution"]
1411
- },
1412
- "replace_text": {
1413
- "format": "pdf-replace-text",
1414
- "is_edit_operation": False,
1415
- "param_keys": ["old_text", "new_text"]
1416
- },
1417
- "unlock_pdf": {
1418
- "edit_type": "decrypt", # 编辑类型
1419
- "is_edit_operation": True, # 标记为编辑操作
1420
- },
1421
- "add_text_watermark": {
1422
- "edit_type": "add_text_watermark", # 编辑类型,文本水印
1423
- "is_edit_operation": True, # 标记为编辑操作
1424
- "param_keys": ["text", "position", "opacity", "range", "layout",
1425
- "font_family", "font_size", "font_color"] # 需要从arguments获取的参数(文本水印)
1426
- },
1427
- "add_image_watermark": {
1428
- "edit_type": "add_image_watermark",
1429
- "is_edit_operation": True,
1430
- "param_keys": ["image_url", "position", "opacity", "range", "layout"]
1431
- },
1432
- "protect_pdf": {
1433
- "edit_type": "encrypt", # 编辑类型
1434
- "is_edit_operation": True, # 标记为编辑操作
1435
- "param_keys": ["password"] # 需要从arguments获取的参数
1436
- },
1437
- "restrict_printing": {
1438
- "edit_type": "encrypt", # 或protect,和protect_pdf一致
1439
- "is_edit_operation": True,
1440
- "param_keys": ["password"] # 增加password参数
1441
- },
1442
- "compress_pdf": {
1443
- "edit_type": "compress", # 编辑类型
1444
- "is_edit_operation": True, # 标记为编辑操作
1445
- "param_keys": ["image_quantity"] # 需要从arguments获取的参数
1446
- },
1447
- "split_pdf": {
1448
- "edit_type": "split", # 编辑类型
1449
- "is_edit_operation": True, # 标记为编辑操作
1450
- "param_keys": ["pages", "split_type", "merge_all"] # 需要从arguments获取的参数
1451
- },
1452
- "merge_pdfs": {
1453
- "edit_type": "merge", # 编辑类型
1454
- "is_edit_operation": True, # 标记为编辑操作
1455
- },
1456
- "rotate_pdf": {
1457
- "edit_type": "rotate", # 编辑类型
1458
- "is_edit_operation": True, # 标记为编辑操作
1459
- "param_keys": ["rotates"] # 只需要rotates参数,移除对旧格式的支持
1460
- },
1461
- "remove_margin": {
1462
- "edit_type": "remove_margin", # 编辑类型
1463
- "is_edit_operation": True, # 标记为编辑操作
1464
- },
1465
- "extract_images": {
1466
- "edit_type": "extract_image", # 编辑类型
1467
- "is_edit_operation": True, # 标记为编辑操作
1468
- "param_keys": ["format"] # 需要从arguments获取的参数
1469
- },
1470
- "translate_pdf": {
1471
- "is_translate_operation": True,
1472
- "param_keys": ["source", "target", "output_type"]
1473
- },
1474
- "ocr_document": {
1475
- "is_ocr_operation": True,
1476
- "param_keys": ["format", "language"]
1477
- },
1478
- "summarize_document": {
1479
- "is_summarize_operation": True,
1480
- "param_keys": ["prompt", "language"]
1481
- },
1482
- }
1483
-
1484
- DEFAULTS = {
1485
- "start_num": 1,
1486
- "position_page_numbers": "5", # 添加页码的位置默认值
1487
- "position_watermark": "center", # 水印的位置默认值
1488
- "margin": 30,
1489
- "opacity": 1.0,
1490
- "range": "",
1491
- "layout": "on", # 添加layout默认值
1492
- "image_quantity": 60,
1493
- "split_type": "page",
1494
- "merge_all": 0,
1495
- "angle": 90,
1496
- "pages": "",
1497
- "format": "png", # 提取图片的默认格式
1498
- "page_size": "",
1499
- "resolution": 0,
1500
- "image_quality": 100, # PDF转图片的图片质量默认值
1501
- }
1502
-
1503
- if name in TOOL_CONFIG:
1504
- # 处理文件信息
1505
- file_objects = arguments.get("files", [])
1506
- if not file_objects:
1507
- error_msg = "未提供文件信息"
1508
- await logger.error(error_msg)
1509
- return [types.TextContent(type="text", text=error_msg)]
1510
-
1511
- # 确保file_objects是一个列表
1512
- if isinstance(file_objects, dict):
1513
- file_objects = [file_objects]
1514
-
1515
- # file_objects中的path需要处理file://协议
1516
- for file_obj in file_objects:
1517
- path = file_obj.get("path")
1518
- if path and path.startswith("file://"):
1519
- file_obj["path"] = url2pathname(path.removeprefix('file:'))
1520
-
1521
- config = TOOL_CONFIG[name]
1522
- operation_config = dict(config) # 复制配置
1523
-
1524
- # 处理格式
1525
- if not operation_config.get("format") and "format_key" in config:
1526
- operation_config["format"] = arguments.get(config["format_key"], "")
1527
-
1528
- # 处理额外参数
1529
- if "param_keys" in config:
1530
- operation_config["extra_params"] = {}
1531
-
1532
- # 处理特殊情况:position参数在不同工具中有不同的默认值
1533
- for key in config["param_keys"]:
1534
- if key == "position":
1535
- if name == "add_page_numbers":
1536
- # 添加页码工具使用"5"作为position默认值
1537
- operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get("position_page_numbers"))
1538
- elif name == "add_text_watermark":
1539
- # 添加文本水印工具使用"center"作为position默认值
1540
- operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get("position_watermark"))
1541
- else:
1542
- # 其他工具使用通用默认值
1543
- operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get(key))
1544
- else:
1545
- # 其他参数正常处理
1546
- operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get(key, ""))
1547
-
1548
- # restrict_printing工具自动加provider参数
1549
- if name == "restrict_printing":
1550
- operation_config["extra_params"]["provider"] = "printpermission"
1551
-
1552
- # 特殊处理merge_pdfs工具
1553
- if name == "merge_pdfs":
1554
- # 创建编辑器
1555
- file_handler = FileHandler(logger)
1556
- editor = Editor(logger, file_handler)
1557
-
1558
- # 提取文件路径、密码和原始名称
1559
- file_paths = [file_obj["path"] for file_obj in file_objects]
1560
- passwords = [file_obj.get("password") for file_obj in file_objects]
1561
- original_names = [file_obj.get("name") for file_obj in file_objects]
1562
-
1563
- # 由于merge_pdfs方法只接受一个密码参数,如果文件密码不同,可能需要特殊处理
1564
- # 此处简化处理,使用第一个非空密码
1565
- password = next((p for p in passwords if p), None)
1566
-
1567
- # 合并文件名用于结果文件
1568
- merged_name = None
1569
- if any(original_names):
1570
- # 如果有原始文件名,则合并它们(最多使用前两个文件名)
1571
- valid_names = [name for name in original_names if name]
1572
- if valid_names:
1573
- if len(valid_names) == 1:
1574
- merged_name = valid_names[0]
1575
- else:
1576
- merged_name = f"{valid_names[0]}_{valid_names[1]}_等"
1577
-
1578
- # 直接调用merge_pdfs方法
1579
- result = await editor.merge_pdfs(file_paths, password, merged_name)
1580
-
1581
- # 构建结果报告
1582
- report_msg = generate_result_report(
1583
- [result]
1584
- )
1585
-
1586
- # 如果失败,记录错误
1587
- if not result.success:
1588
- await logger.error(report_msg)
1589
-
1590
- return [types.TextContent(type="text", text=report_msg)]
1591
-
1592
- # 调用通用处理函数
1593
- result = await process_tool_call(logger, file_objects, operation_config)
1594
- return [result]
1595
-
1596
- elif name == "create_pdf":
1597
- from .create_pdf import PDFCreator
1598
- prompt = arguments.get("prompt")
1599
- filename = arguments.get("filename")
1600
- language = arguments.get("language")
1601
- enable_web_search = arguments.get("enable_web_search", False)
1602
-
1603
- if not prompt:
1604
- error_msg = "prompt参数不能为空"
1605
- await logger.error(error_msg)
1606
- return [types.TextContent(type="text", text=error_msg)]
1607
- if not filename:
1608
- error_msg = "filename参数不能为空"
1609
- await logger.error(error_msg)
1610
- return [types.TextContent(type="text", text=error_msg)]
1611
- if not language:
1612
- error_msg = "language参数不能为空"
1613
- await logger.error(error_msg)
1614
- return [types.TextContent(type="text", text=error_msg)]
1615
-
1616
- # 创建PDF创建器
1617
- file_handler = FileHandler(logger)
1618
- pdf_creator = PDFCreator(logger, file_handler)
1619
-
1620
- result = await pdf_creator.create_pdf_from_prompt(
1621
- prompt=prompt,
1622
- language=language,
1623
- enable_web_search=enable_web_search,
1624
- original_name=filename
1625
- )
1626
- # 构建结果报告
1627
- report_msg = generate_result_report(
1628
- [result]
1629
- )
1630
- # 如果失败,记录错误
1631
- if not result.success:
1632
- await logger.error(report_msg)
1633
-
1634
- return [types.TextContent(type="text", text=report_msg)]
1635
-
1636
- else:
1637
- error_msg = f"未知工具: {name}"
1638
- await logger.error(error_msg, ValueError)
1639
- return [types.TextContent(type="text", text=error_msg)]
1640
-
1641
- async def main():
1642
- """应用主入口"""
1643
- # 打印版本号
1644
- try:
1645
- import importlib.metadata
1646
- version = importlib.metadata.version("lightpdf-aipdf-mcp")
1647
- print(f"LightPDF AI-PDF MCP Server v{version}", file=sys.stderr)
1648
- except Exception as e:
1649
- print("LightPDF AI-PDF MCP Server (版本信息获取失败)", file=sys.stderr)
1650
-
1651
- # 解析命令行参数
1652
- parser = argparse.ArgumentParser(description="LightPDF AI-PDF MCP Server")
1653
- parser.add_argument("-p", "--port", type=int, default=0, help="指定SSE服务器的端口号,如果提供则使用SSE模式,否则使用stdio模式")
1654
- args = parser.parse_args()
1655
-
1656
- initialization_options = app.create_initialization_options(
1657
- notification_options=NotificationOptions()
1658
- )
1659
-
1660
- if args.port:
1661
- from mcp.server.sse import SseServerTransport
1662
- from starlette.applications import Starlette
1663
- from starlette.routing import Mount, Route
1664
- import uvicorn
1665
-
1666
- # 使用SSE服务器
1667
- print(f"启动SSE服务器,端口号:{args.port}", file=sys.stderr)
1668
-
1669
- # 创建SSE传输
1670
- transport = SseServerTransport("/messages/")
1671
-
1672
- # 定义SSE连接处理函数
1673
- async def handle_sse(request):
1674
- async with transport.connect_sse(
1675
- request.scope, request.receive, request._send
1676
- ) as streams:
1677
- await app.run(
1678
- streams[0], streams[1], initialization_options
1679
- )
1680
-
1681
- # 创建Starlette应用
1682
- sse_app = Starlette(routes=[
1683
- Route("/sse/", endpoint=handle_sse),
1684
- Mount("/messages/", app=transport.handle_post_message),
1685
- ])
1686
-
1687
- # 使用异步方式启动服务器
1688
- server = uvicorn.Server(uvicorn.Config(
1689
- app=sse_app,
1690
- host="0.0.0.0",
1691
- port=args.port,
1692
- log_level="warning"
1693
- ))
1694
- await server.serve()
1695
- else:
1696
- import mcp.server.stdio as stdio
1697
-
1698
- # 使用stdio服务器
1699
- print("启动stdio服务器", file=sys.stderr)
1700
- async with stdio.stdio_server() as (read_stream, write_stream):
1701
- await app.run(
1702
- read_stream,
1703
- write_stream,
1704
- initialization_options
1705
- )
1706
-
1707
- def cli_main():
1708
- try:
1709
- asyncio.run(main())
1710
- except KeyboardInterrupt:
1711
- print("服务器被用户中断", file=sys.stderr)
1712
- sys.exit(0)
1713
- except Exception as e:
1714
- print(f"服务器发生错误: {e}", file=sys.stderr)
1715
- sys.exit(1)
1716
-
1717
- if __name__ == "__main__":
1718
- cli_main()