__main__.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import argparse
  16. import asyncio
  17. import os
  18. import sys
  19. from fastmcp import FastMCP
  20. from .pipelines import create_pipeline_handler
  21. def _parse_args() -> argparse.Namespace:
  22. """Parse command line arguments."""
  23. parser = argparse.ArgumentParser(
  24. description="PaddleOCR MCP server - Supports local library, AI Studio service, and self-hosted servers."
  25. )
  26. parser.add_argument(
  27. "--pipeline",
  28. choices=["OCR", "PP-StructureV3", "PaddleOCR-VL"],
  29. default=os.getenv("PADDLEOCR_MCP_PIPELINE", "OCR"),
  30. help="Pipeline name.",
  31. )
  32. parser.add_argument(
  33. "--ppocr_source",
  34. choices=["local", "aistudio", "qianfan", "self_hosted"],
  35. default=os.getenv("PADDLEOCR_MCP_PPOCR_SOURCE", "local"),
  36. help="Source of PaddleOCR functionality: local (local library), aistudio (AI Studio service), qianfan (Qianfan service), self_hosted (self-hosted server).",
  37. )
  38. parser.add_argument(
  39. "--http",
  40. action="store_true",
  41. help="Use HTTP transport instead of STDIO (suitable for remote deployment and multiple clients).",
  42. )
  43. parser.add_argument(
  44. "--host",
  45. default="127.0.0.1",
  46. help="Host address for HTTP mode (default: 127.0.0.1).",
  47. )
  48. parser.add_argument(
  49. "--port",
  50. type=int,
  51. default=8000,
  52. help="Port for HTTP mode (default: 8000).",
  53. )
  54. parser.add_argument(
  55. "--verbose", action="store_true", help="Enable verbose logging for debugging."
  56. )
  57. # Local mode configuration
  58. parser.add_argument(
  59. "--pipeline_config",
  60. default=os.getenv("PADDLEOCR_MCP_PIPELINE_CONFIG"),
  61. help="PaddleOCR pipeline configuration file path (for local mode).",
  62. )
  63. parser.add_argument(
  64. "--device",
  65. default=os.getenv("PADDLEOCR_MCP_DEVICE"),
  66. help="Device to run inference on.",
  67. )
  68. # Service mode configuration
  69. parser.add_argument(
  70. "--server_url",
  71. default=os.getenv("PADDLEOCR_MCP_SERVER_URL"),
  72. help="Base URL of the underlying server (required in service mode).",
  73. )
  74. parser.add_argument(
  75. "--aistudio_access_token",
  76. default=os.getenv("PADDLEOCR_MCP_AISTUDIO_ACCESS_TOKEN"),
  77. help="AI Studio access token (required for AI Studio).",
  78. )
  79. parser.add_argument(
  80. "--qianfan_api_key",
  81. default=os.getenv("PADDLEOCR_MCP_QIANFAN_API_KEY"),
  82. help="Qianfan API key (required for Qianfan).",
  83. )
  84. parser.add_argument(
  85. "--timeout",
  86. type=int,
  87. default=int(os.getenv("PADDLEOCR_MCP_TIMEOUT", "60")),
  88. help="HTTP read timeout in seconds for API requests to the underlying server.",
  89. )
  90. args = parser.parse_args()
  91. return args
  92. def _validate_args(args: argparse.Namespace) -> None:
  93. """Validate command line arguments."""
  94. if not args.http and (args.host != "127.0.0.1" or args.port != 8000):
  95. print(
  96. "Host and port arguments are only valid when using HTTP transport (see: `--http`).",
  97. file=sys.stderr,
  98. )
  99. sys.exit(2)
  100. if args.ppocr_source in ["aistudio", "qianfan", "self_hosted"]:
  101. if not args.server_url:
  102. print("Error: The server base URL is required.", file=sys.stderr)
  103. print(
  104. "Please either set `--server_url` or set the environment variable "
  105. "`PADDLEOCR_MCP_SERVER_URL`.",
  106. file=sys.stderr,
  107. )
  108. sys.exit(2)
  109. if args.ppocr_source == "aistudio" and not args.aistudio_access_token:
  110. print("Error: The AI Studio access token is required.", file=sys.stderr)
  111. print(
  112. "Please either set `--aistudio_access_token` or set the environment variable "
  113. "`PADDLEOCR_MCP_AISTUDIO_ACCESS_TOKEN`.",
  114. file=sys.stderr,
  115. )
  116. sys.exit(2)
  117. elif args.ppocr_source == "qianfan":
  118. if not args.qianfan_api_key:
  119. print("Error: The Qianfan API key is required.", file=sys.stderr)
  120. print(
  121. "Please either set `--qianfan_api_key` or set the environment variable "
  122. "`PADDLEOCR_MCP_QIANFAN_API_KEY`.",
  123. file=sys.stderr,
  124. )
  125. sys.exit(2)
  126. if args.pipeline not in ("PaddleOCR-VL", "PP-StructureV3"):
  127. print(
  128. f"{repr(args.pipeline)} is currently not supported when using the {repr(args.ppocr_source)} source.",
  129. file=sys.stderr,
  130. )
  131. sys.exit(2)
  132. async def async_main() -> None:
  133. """Asynchronous main entry point."""
  134. args = _parse_args()
  135. _validate_args(args)
  136. try:
  137. pipeline_handler = create_pipeline_handler(
  138. args.pipeline,
  139. args.ppocr_source,
  140. pipeline_config=args.pipeline_config,
  141. device=args.device,
  142. server_url=args.server_url,
  143. aistudio_access_token=args.aistudio_access_token,
  144. qianfan_api_key=args.qianfan_api_key,
  145. timeout=args.timeout,
  146. )
  147. except Exception as e:
  148. print(f"Failed to create the pipeline handler: {e}", file=sys.stderr)
  149. if args.verbose:
  150. import traceback
  151. traceback.print_exc(file=sys.stderr)
  152. sys.exit(1)
  153. try:
  154. await pipeline_handler.start()
  155. server_name = f"PaddleOCR {args.pipeline} MCP server"
  156. mcp = FastMCP(
  157. name=server_name,
  158. log_level="INFO" if args.verbose else "WARNING",
  159. mask_error_details=True,
  160. )
  161. pipeline_handler.register_tools(mcp)
  162. if args.http:
  163. await mcp.run_async(
  164. transport="streamable-http",
  165. host=args.host,
  166. port=args.port,
  167. )
  168. else:
  169. await mcp.run_async()
  170. except Exception as e:
  171. print(f"Failed to start the server: {e}", file=sys.stderr)
  172. if args.verbose:
  173. import traceback
  174. traceback.print_exc(file=sys.stderr)
  175. sys.exit(1)
  176. finally:
  177. await pipeline_handler.stop()
  178. def main():
  179. """Main entry point."""
  180. asyncio.run(async_main())
  181. if __name__ == "__main__":
  182. main()