lightpdf-aipdf-mcp 0.1.145__py3-none-any.whl → 0.1.146__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.
- lightpdf_aipdf_mcp/create_pdf.py +100 -35
- lightpdf_aipdf_mcp/server.py +9 -1
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.146.dist-info}/METADATA +1 -1
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.146.dist-info}/RECORD +6 -6
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.146.dist-info}/WHEEL +0 -0
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.146.dist-info}/entry_points.txt +0 -0
lightpdf_aipdf_mcp/create_pdf.py
CHANGED
@@ -167,7 +167,6 @@ class PDFCreator(BaseApiClient):
|
|
167
167
|
async def _get_real_user_info(self, language: str) -> dict:
|
168
168
|
"""获取真实的用户信息"""
|
169
169
|
import platform
|
170
|
-
import socket
|
171
170
|
|
172
171
|
# 获取真实的用户代理字符串
|
173
172
|
system = platform.system()
|
@@ -225,9 +224,12 @@ class PDFCreator(BaseApiClient):
|
|
225
224
|
return language_codes.get(language, "en-US,en;q=0.9")
|
226
225
|
|
227
226
|
def _get_local_ip(self) -> str:
|
228
|
-
"""获取本地IP
|
227
|
+
"""获取本地IP地址的稳定方法,优先获取物理网络接口IP"""
|
229
228
|
import socket
|
230
229
|
|
230
|
+
# 收集所有可能的IP地址
|
231
|
+
candidate_ips = []
|
232
|
+
|
231
233
|
# 方法1: 尝试连接外部DNS服务器
|
232
234
|
try:
|
233
235
|
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
@@ -236,9 +238,9 @@ class PDFCreator(BaseApiClient):
|
|
236
238
|
try:
|
237
239
|
s.connect((dns_server, 80))
|
238
240
|
ip = s.getsockname()[0]
|
239
|
-
# 验证是否为私有IP地址
|
240
241
|
if self._is_valid_local_ip(ip):
|
241
|
-
|
242
|
+
candidate_ips.append(ip)
|
243
|
+
break # 找到第一个有效IP就停止
|
242
244
|
except Exception:
|
243
245
|
continue
|
244
246
|
except Exception:
|
@@ -249,58 +251,121 @@ class PDFCreator(BaseApiClient):
|
|
249
251
|
hostname = socket.gethostname()
|
250
252
|
ip_list = socket.gethostbyname_ex(hostname)[2]
|
251
253
|
for ip in ip_list:
|
252
|
-
if self._is_valid_local_ip(ip):
|
253
|
-
|
254
|
+
if self._is_valid_local_ip(ip) and ip not in candidate_ips:
|
255
|
+
candidate_ips.append(ip)
|
254
256
|
except Exception:
|
255
257
|
pass
|
256
258
|
|
257
|
-
# 方法3:
|
259
|
+
# 方法3: 使用系统网络接口信息
|
258
260
|
try:
|
259
|
-
import subprocess
|
260
261
|
import platform
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
result = subprocess.run(['
|
265
|
-
|
266
|
-
if 'IPv4' in line and 'Address' in line:
|
267
|
-
ip = line.split(':')[-1].strip()
|
268
|
-
if self._is_valid_local_ip(ip):
|
269
|
-
return ip
|
270
|
-
else:
|
271
|
-
# Unix/Linux/macOS系统
|
272
|
-
result = subprocess.run(['hostname', '-I'], capture_output=True, text=True)
|
262
|
+
if platform.system() != "Windows":
|
263
|
+
# Unix/Linux/macOS系统:尝试使用更详细的网络接口信息
|
264
|
+
import subprocess
|
265
|
+
result = subprocess.run(['ip', 'route', 'get', '8.8.8.8'],
|
266
|
+
capture_output=True, text=True)
|
273
267
|
if result.returncode == 0:
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
268
|
+
for line in result.stdout.split('\n'):
|
269
|
+
if 'src' in line:
|
270
|
+
parts = line.split()
|
271
|
+
if 'src' in parts:
|
272
|
+
src_index = parts.index('src')
|
273
|
+
if src_index + 1 < len(parts):
|
274
|
+
ip = parts[src_index + 1]
|
275
|
+
if self._is_valid_local_ip(ip) and ip not in candidate_ips:
|
276
|
+
candidate_ips.append(ip)
|
277
|
+
break
|
278
278
|
except Exception:
|
279
279
|
pass
|
280
280
|
|
281
|
-
#
|
282
|
-
|
283
|
-
|
284
|
-
s.bind(('', 0))
|
285
|
-
ip = s.getsockname()[0]
|
286
|
-
if self._is_valid_local_ip(ip):
|
287
|
-
return ip
|
288
|
-
except Exception:
|
289
|
-
pass
|
281
|
+
# 选择最优IP地址
|
282
|
+
if candidate_ips:
|
283
|
+
return self._select_best_ip(candidate_ips)
|
290
284
|
|
291
285
|
# 最后的后备方案
|
292
286
|
return "127.0.0.1"
|
293
287
|
|
288
|
+
def _select_best_ip(self, candidate_ips: list) -> str:
|
289
|
+
"""从候选IP中选择最佳的IP地址"""
|
290
|
+
import ipaddress
|
291
|
+
|
292
|
+
# 按优先级排序IP地址
|
293
|
+
def ip_priority(ip: str) -> int:
|
294
|
+
try:
|
295
|
+
ip_obj = ipaddress.ip_address(ip)
|
296
|
+
|
297
|
+
# 最高优先级:公网IP
|
298
|
+
if ip_obj.is_global:
|
299
|
+
return 1
|
300
|
+
|
301
|
+
# 高优先级:常见的家庭/办公网络
|
302
|
+
common_networks = [
|
303
|
+
ipaddress.ip_network('192.168.0.0/16'), # 家庭网络
|
304
|
+
ipaddress.ip_network('10.0.0.0/8'), # 企业网络
|
305
|
+
ipaddress.ip_network('172.16.0.0/12'), # 企业网络(但要避免虚拟网络段)
|
306
|
+
]
|
307
|
+
|
308
|
+
for network in common_networks:
|
309
|
+
if ip_obj in network:
|
310
|
+
# 进一步细分优先级
|
311
|
+
if ip.startswith('192.168.1.') or ip.startswith('192.168.0.'):
|
312
|
+
return 2 # 最常见的家庭网络
|
313
|
+
elif ip.startswith('192.168.'):
|
314
|
+
return 3 # 其他家庭网络
|
315
|
+
elif ip.startswith('10.'):
|
316
|
+
return 4 # 企业网络
|
317
|
+
else:
|
318
|
+
return 5 # 其他私有网络
|
319
|
+
|
320
|
+
# 较低优先级:其他私有IP
|
321
|
+
if ip_obj.is_private:
|
322
|
+
return 6
|
323
|
+
|
324
|
+
# 最低优先级:其他地址
|
325
|
+
return 7
|
326
|
+
|
327
|
+
except ValueError:
|
328
|
+
return 99 # 无效IP地址
|
329
|
+
|
330
|
+
# 按优先级排序并返回最佳IP
|
331
|
+
candidate_ips.sort(key=ip_priority)
|
332
|
+
return candidate_ips[0]
|
333
|
+
|
294
334
|
def _is_valid_local_ip(self, ip: str) -> bool:
|
295
|
-
"""验证IP地址是否为有效的本地IP"""
|
335
|
+
"""验证IP地址是否为有效的本地IP,排除虚拟网络接口"""
|
296
336
|
import ipaddress
|
297
337
|
|
298
338
|
try:
|
299
339
|
ip_obj = ipaddress.ip_address(ip)
|
340
|
+
|
300
341
|
# 排除回环地址和链路本地地址
|
301
342
|
if ip_obj.is_loopback or ip_obj.is_link_local:
|
302
343
|
return False
|
303
|
-
|
344
|
+
|
345
|
+
# 排除常见的虚拟网络IP段
|
346
|
+
virtual_networks = [
|
347
|
+
# Docker默认网段
|
348
|
+
ipaddress.ip_network('172.17.0.0/16'),
|
349
|
+
ipaddress.ip_network('172.18.0.0/16'),
|
350
|
+
ipaddress.ip_network('172.19.0.0/16'),
|
351
|
+
ipaddress.ip_network('172.20.0.0/16'),
|
352
|
+
# VMware默认网段
|
353
|
+
ipaddress.ip_network('192.168.56.0/24'),
|
354
|
+
ipaddress.ip_network('192.168.57.0/24'),
|
355
|
+
# VirtualBox默认网段
|
356
|
+
ipaddress.ip_network('192.168.100.0/24'),
|
357
|
+
# Hyper-V默认网段
|
358
|
+
ipaddress.ip_network('172.16.0.0/12'),
|
359
|
+
# 其他常见虚拟网段
|
360
|
+
ipaddress.ip_network('10.0.75.0/24'), # Parallels
|
361
|
+
ipaddress.ip_network('169.254.0.0/16'), # APIPA
|
362
|
+
]
|
363
|
+
|
364
|
+
# 检查是否在虚拟网络范围内
|
365
|
+
for network in virtual_networks:
|
366
|
+
if ip_obj in network:
|
367
|
+
return False
|
368
|
+
|
304
369
|
return True
|
305
370
|
except ValueError:
|
306
371
|
return False
|
lightpdf_aipdf_mcp/server.py
CHANGED
@@ -526,6 +526,13 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
526
526
|
"type": "boolean",
|
527
527
|
"default": False,
|
528
528
|
"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."
|
529
|
+
},
|
530
|
+
"image_quality": {
|
531
|
+
"type": "integer",
|
532
|
+
"minimum": 0,
|
533
|
+
"maximum": 200,
|
534
|
+
"default": 100,
|
535
|
+
"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."
|
529
536
|
}
|
530
537
|
},
|
531
538
|
"required": ["files", "format"]
|
@@ -1377,7 +1384,7 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
1377
1384
|
"convert_document": {
|
1378
1385
|
"format_key": "format", # 从arguments获取format
|
1379
1386
|
"is_edit_operation": False,
|
1380
|
-
"param_keys": ["merge_all", "one_page_per_sheet"]
|
1387
|
+
"param_keys": ["merge_all", "one_page_per_sheet", "image_quality"]
|
1381
1388
|
},
|
1382
1389
|
"remove_watermark": {
|
1383
1390
|
"format": "doc-repair", # 固定format
|
@@ -1485,6 +1492,7 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
1485
1492
|
"format": "png", # 提取图片的默认格式
|
1486
1493
|
"page_size": "",
|
1487
1494
|
"resolution": 0,
|
1495
|
+
"image_quality": 100, # PDF转图片的图片质量默认值
|
1488
1496
|
}
|
1489
1497
|
|
1490
1498
|
if name in TOOL_CONFIG:
|
@@ -1,13 +1,13 @@
|
|
1
1
|
lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
|
2
2
|
lightpdf_aipdf_mcp/common.py,sha256=VOipRuz2veRMhpvr0lJ2nZwuEZntx1MiRxDSNx0fSWs,9310
|
3
3
|
lightpdf_aipdf_mcp/converter.py,sha256=r8iO5R5vLNNKWdb6WSnwzTwwmp2TvgLXSIvvA4y___o,15336
|
4
|
-
lightpdf_aipdf_mcp/create_pdf.py,sha256=
|
4
|
+
lightpdf_aipdf_mcp/create_pdf.py,sha256=FKWttbR48foiwUwmSsCN6n6PfI25IpaXETPUscz9DjI,16073
|
5
5
|
lightpdf_aipdf_mcp/editor.py,sha256=BR-sWW9L7tybEPOhdc8W-uwdBoom19EPTmGDvy_2gMc,27941
|
6
6
|
lightpdf_aipdf_mcp/ocr.py,sha256=IyzxisA6qtXcGTHZofpUYXYDdcIjUaaHcVUKpM7DH9A,2832
|
7
|
-
lightpdf_aipdf_mcp/server.py,sha256=
|
7
|
+
lightpdf_aipdf_mcp/server.py,sha256=sEqgSxOZwa9RfbTKuUTZv9BHR_fWb_b6WYbQh6zGxGk,81258
|
8
8
|
lightpdf_aipdf_mcp/summarizer.py,sha256=UPAftDKjp2NFE2Wfoi2yAsGfaWqihu-c_W_BwfhVy0c,3671
|
9
9
|
lightpdf_aipdf_mcp/translator.py,sha256=nuZa4FpsA0xeRWAEGqSPIM55aJuazJX1m32uajowo7I,2778
|
10
|
-
lightpdf_aipdf_mcp-0.1.
|
11
|
-
lightpdf_aipdf_mcp-0.1.
|
12
|
-
lightpdf_aipdf_mcp-0.1.
|
13
|
-
lightpdf_aipdf_mcp-0.1.
|
10
|
+
lightpdf_aipdf_mcp-0.1.146.dist-info/METADATA,sha256=hUDDCP56u4c-P2hS0vDrZlVge9FS2pr6RJPze3LLp3s,8120
|
11
|
+
lightpdf_aipdf_mcp-0.1.146.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
12
|
+
lightpdf_aipdf_mcp-0.1.146.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
|
13
|
+
lightpdf_aipdf_mcp-0.1.146.dist-info/RECORD,,
|
File without changes
|
{lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.146.dist-info}/entry_points.txt
RENAMED
File without changes
|