dicom-mcp 1.2.0 → 1.2.5

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,54 @@
5
5
  格式遵循 [Keep a Changelog](https://keepachangelog.com/),
6
6
  版本号遵循 [Semantic Versioning](https://semver.org/)。
7
7
 
8
+ ## [1.2.5] - 2025-01-13
9
+
10
+ ### 改进
11
+
12
+ - **支持多格式密码提取**: 增强密码识别的灵活性
13
+ - 支持:`安全码:8492`、`密码:8492`、`password:8492`、`code:8492`、`验证码:8492`
14
+ - 同时支持半角冒号 `:` 和全角冒号 `:`
15
+ - 自动清理 URL 中的密码部分
16
+ - 添加诊断日志显示提取的密码
17
+
18
+ ## [1.2.4] - 2025-01-13
19
+
20
+ ### 改进
21
+
22
+ - **标准化安全码提取**: 简化并规范化密码提取逻辑
23
+ - 只支持标准格式:`URL 安全码:8492` 或 `URL 安全码:8492`
24
+ - 自动清理 URL 中的安全码部分
25
+ - 添加诊断日志显示提取的密码
26
+ - 代码更清晰,性能更好
27
+
28
+ ## [1.2.3] - 2025-01-13
29
+
30
+ ### 新功能
31
+
32
+ - **自动密码提取**: 从 URL 中自动识别和提取安全码/密码
33
+ - 支持多种格式:`安全码:8492`、`密码:8492`、`password:8492`、`code:8492`
34
+ - 用户只需粘贴原始链接,无需手动提取密码
35
+ - 自动清理 URL,将密码传递给下载脚本
36
+ - 例如:`https://ylyyx.shdc.org.cn/code.html?... 安全码:8492`
37
+
38
+ ## [1.2.2] - 2025-01-13
39
+
40
+ ### 新功能
41
+
42
+ - **安全码/密码支持**: 支持需要安全码(password)的医院链接
43
+ - 添加 `password` 参数到 `download_dicom` 和 `batch_download_dicom` 工具
44
+ - 自动将密码传递给底层下载脚本
45
+ - 用法:提供 URL 和密码参数即可自动填入
46
+
47
+ ## [1.2.1] - 2025-01-13
48
+
49
+ ### 改进
50
+
51
+ - **增强的路径解析**: 改进 dicom_download 查找逻辑
52
+ - 支持本地开发、NPM 包和 PyPI 安装等多种部署方式
53
+ - 添加诊断日志,显示查找的路径和找到的位置
54
+ - 优化查找顺序,确保 multi_download.py 存在
55
+
8
56
  ## [1.2.0] - 2025-01-13
9
57
 
10
58
  ### 新功能
@@ -13,31 +13,49 @@ from dataclasses import dataclass
13
13
  from mcp.server.fastmcp import FastMCP
14
14
  from pydantic import BaseModel, Field
15
15
 
16
- # Resolve path to dicom_download - supports two deployment methods:
16
+ # Resolve path to dicom_download - supports multiple deployment methods:
17
17
  # 1. Local development: git clone后,dicom_download 在 dicom_mcp 的上级目录
18
- # 2. Installed package: 从 PyPI/npm 安装时,dicom_download 作为依赖已安装
18
+ # 2. NPM package: npx安装时,dicom_download 在node_modules同级
19
+ # 3. Installed package: 从 PyPI 安装时,dicom_download 作为依赖已安装
19
20
  def _resolve_dicom_download_path() -> Path:
20
21
  """Resolve path to dicom_download module."""
21
- # Method 1: Local development (git clone)
22
- local_dev_path = Path(__file__).parent.parent.parent / "dicom_download"
23
- if local_dev_path.exists():
22
+ current_dir = Path(__file__).parent
23
+
24
+ # Method 1: Local development - check parent directory
25
+ local_dev_path = current_dir.parent.parent / "dicom_download"
26
+ if local_dev_path.exists() and (local_dev_path / "multi_download.py").exists():
27
+ print(f"[dicom-mcp] Found dicom_download at: {local_dev_path}", file=sys.stderr)
24
28
  return local_dev_path
25
29
 
26
- # Method 2: Check site-packages or installation location
30
+ # Method 2: NPM package - check in the same package directory
31
+ npm_pkg_path = current_dir.parent / "dicom_download"
32
+ if npm_pkg_path.exists() and (npm_pkg_path / "multi_download.py").exists():
33
+ print(f"[dicom-mcp] Found dicom_download at: {npm_pkg_path}", file=sys.stderr)
34
+ return npm_pkg_path
35
+
36
+ # Method 3: Check site-packages or installation location
27
37
  try:
28
38
  import dicom_download as dd_module
29
39
  if dd_module.__file__:
30
- return Path(dd_module.__file__).parent
40
+ dd_path = Path(dd_module.__file__).parent
41
+ if (dd_path / "multi_download.py").exists():
42
+ print(f"[dicom-mcp] Found dicom_download at: {dd_path}", file=sys.stderr)
43
+ return dd_path
31
44
  except ImportError:
32
45
  pass
33
46
 
34
- # Method 3: Try to find in Python path
47
+ # Method 4: Try to find in Python path
35
48
  for path_item in sys.path:
36
49
  candidate = Path(path_item) / "dicom_download"
37
- if candidate.exists():
50
+ if candidate.exists() and (candidate / "multi_download.py").exists():
51
+ print(f"[dicom-mcp] Found dicom_download at: {candidate}", file=sys.stderr)
38
52
  return candidate
39
53
 
40
- # Fallback to local dev path even if not exists
54
+ # Fallback - return the most likely path with diagnostic message
55
+ print(f"[dicom-mcp] WARNING: Could not find dicom_download. Tried paths:", file=sys.stderr)
56
+ print(f" 1. {local_dev_path}", file=sys.stderr)
57
+ print(f" 2. {npm_pkg_path}", file=sys.stderr)
58
+ print(f" 3. Python path entries", file=sys.stderr)
41
59
  return local_dev_path
42
60
 
43
61
  DICOM_DOWNLOAD_PATH = _resolve_dicom_download_path()
@@ -110,6 +128,9 @@ class BatchDownloadRequest(BaseModel):
110
128
  )
111
129
  mode: str = Field(default="all", description="Download mode")
112
130
  headless: bool = Field(default=True, description="Run in headless mode")
131
+ password: Optional[str] = Field(
132
+ default=None, description="Password/share code if required by the site"
133
+ )
113
134
  create_zip: bool = Field(default=True, description="Create ZIP archives")
114
135
  max_rounds: int = Field(
115
136
  default=_DEFAULT_MAX_ROUNDS,
@@ -214,6 +235,7 @@ async def run_multi_download(
214
235
  provider: str = "auto",
215
236
  mode: str = "all",
216
237
  headless: bool = True,
238
+ password: Optional[str] = None,
217
239
  create_zip: bool = True,
218
240
  max_rounds: int = 3,
219
241
  step_wait_ms: int = 40,
@@ -259,6 +281,9 @@ async def run_multi_download(
259
281
  else:
260
282
  cmd.append("--no-headless")
261
283
 
284
+ if password:
285
+ cmd.extend(["--password", password])
286
+
262
287
  if not create_zip:
263
288
  cmd.append("--no-zip")
264
289
 
@@ -358,6 +383,50 @@ async def run_multi_download(
358
383
  pass
359
384
 
360
385
 
386
+ # ============================================================================
387
+ # Helper Functions for Password Extraction
388
+ # ============================================================================
389
+
390
+
391
+ def _extract_password_from_url(url: str) -> tuple[str, Optional[str]]:
392
+ """
393
+ Extract password/security code from URL string.
394
+
395
+ Supports multiple formats:
396
+ - URL 安全码:8492 or URL 安全码:8492
397
+ - URL 密码:8492 or URL 密码:8492
398
+ - URL password:8492 or URL password:8492
399
+ - URL code:8492 or URL code:8492
400
+ - URL 验证码:8492 or URL 验证码:8492
401
+
402
+ Returns: (clean_url, password)
403
+ """
404
+ import re
405
+
406
+ # Pattern: look for various password indicators with both half-width and full-width colons
407
+ patterns = [
408
+ r'\s*安全码[::]\s*(\d+)', # 安全码:8492 or 安全码:8492
409
+ r'\s*密码[::]\s*(\d+)', # 密码:8492 or 密码:8492
410
+ r'\s*验证码[::]\s*(\d+)', # 验证码:8492 or 验证码:8492
411
+ r'\s*password[::]\s*(\S+)', # password:8492 or password:8492
412
+ r'\s*code[::]\s*(\d+)', # code:8492 or code:8492
413
+ ]
414
+
415
+ password = None
416
+ clean_url = url
417
+
418
+ for pattern in patterns:
419
+ match = re.search(pattern, url)
420
+ if match:
421
+ password = match.group(1)
422
+ # Remove password from URL
423
+ clean_url = re.sub(pattern, '', url).strip()
424
+ print(f"[dicom-mcp] 提取密码: {password}", file=sys.stderr)
425
+ break
426
+
427
+ return clean_url, password
428
+
429
+
361
430
  # ============================================================================
362
431
  # MCP Tools
363
432
  # ============================================================================
@@ -373,14 +442,22 @@ async def download_dicom(request: DownloadRequest) -> DownloadResult:
373
442
  - fz: 复肿 (ylyyx.shdc.org.cn)
374
443
  - nyfy: 宁夏总医院 (zhyl.nyfy.com.cn)
375
444
  - cloud: *.medicalimagecloud.com and other cloud-based systems
445
+
446
+ Automatically extracts password/security code from URL if present.
447
+ Formats: "URL 安全码:8492", "URL password:8492", etc.
376
448
  """
449
+ # Auto-extract password from URL if not explicitly provided
450
+ clean_url, extracted_password = _extract_password_from_url(request.url)
451
+ password = request.password or extracted_password
452
+
377
453
  os.makedirs(request.output_dir, exist_ok=True)
378
454
  results = await run_multi_download(
379
- [request.url],
455
+ [clean_url],
380
456
  request.output_dir,
381
457
  provider=request.provider or "auto",
382
458
  mode=request.mode,
383
459
  headless=request.headless,
460
+ password=password,
384
461
  create_zip=request.create_zip,
385
462
  max_rounds=request.max_rounds,
386
463
  step_wait_ms=request.step_wait_ms,
@@ -400,14 +477,31 @@ async def batch_download_dicom(request: BatchDownloadRequest) -> list[DownloadRe
400
477
 
401
478
  Each URL gets its own subdirectory. Supports auto-detection of provider
402
479
  based on domain, or manual provider specification.
480
+
481
+ Automatically extracts password/security code from URL if present.
482
+ Formats: "URL 安全码:8492", "URL password:8492", etc.
403
483
  """
484
+ # Auto-extract passwords from URLs
485
+ clean_urls = []
486
+ extracted_password = None
487
+
488
+ for url in request.urls:
489
+ clean_url, pwd = _extract_password_from_url(url)
490
+ clean_urls.append(clean_url)
491
+ # Use first extracted password if no explicit password provided
492
+ if not extracted_password and pwd:
493
+ extracted_password = pwd
494
+
495
+ password = request.password or extracted_password
496
+
404
497
  os.makedirs(request.output_parent, exist_ok=True)
405
498
  return await run_multi_download(
406
- request.urls,
499
+ clean_urls,
407
500
  request.output_parent,
408
501
  provider=request.provider,
409
502
  mode=request.mode,
410
503
  headless=request.headless,
504
+ password=password,
411
505
  create_zip=request.create_zip,
412
506
  max_rounds=request.max_rounds,
413
507
  step_wait_ms=request.step_wait_ms,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dicom-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.5",
4
4
  "description": "MCP server for DICOM image downloading from multiple medical imaging sources",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",