PraisonAI 3.0.0__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.
Files changed (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,490 @@
1
+ """
2
+ MCP Server Implementation
3
+
4
+ Core MCP server that handles JSON-RPC messages and routes them to appropriate handlers.
5
+ Supports both STDIO and HTTP Stream transports.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import logging
11
+ from typing import Any, Callable, Dict, Optional, Set
12
+
13
+ from .registry import (
14
+ get_tool_registry,
15
+ get_resource_registry,
16
+ get_prompt_registry,
17
+ MCPToolRegistry,
18
+ MCPResourceRegistry,
19
+ MCPPromptRegistry,
20
+ )
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # MCP Protocol Constants (Updated to 2025-11-25 spec)
25
+ PROTOCOL_VERSION = "2025-11-25"
26
+ SUPPORTED_VERSIONS = ["2025-11-25", "2025-03-26", "2024-11-05"]
27
+
28
+ # JSON-RPC Error Codes
29
+ PARSE_ERROR = -32700
30
+ INVALID_REQUEST = -32600
31
+ METHOD_NOT_FOUND = -32601
32
+ INVALID_PARAMS = -32602
33
+ INTERNAL_ERROR = -32603
34
+
35
+
36
+ class MCPServer:
37
+ """
38
+ MCP Server that exposes PraisonAI capabilities.
39
+
40
+ Supports:
41
+ - STDIO transport (for Claude Desktop, Cursor, etc.)
42
+ - HTTP Stream transport (MCP 2025-11-25 spec)
43
+
44
+ Example:
45
+ server = MCPServer(name="praisonai")
46
+ server.run(transport="stdio")
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ name: str = "praisonai",
52
+ version: str = "1.0.0",
53
+ tool_registry: Optional[MCPToolRegistry] = None,
54
+ resource_registry: Optional[MCPResourceRegistry] = None,
55
+ prompt_registry: Optional[MCPPromptRegistry] = None,
56
+ instructions: Optional[str] = None,
57
+ ):
58
+ """
59
+ Initialize MCP server.
60
+
61
+ Args:
62
+ name: Server name
63
+ version: Server version
64
+ tool_registry: Custom tool registry (uses global if None)
65
+ resource_registry: Custom resource registry (uses global if None)
66
+ prompt_registry: Custom prompt registry (uses global if None)
67
+ instructions: Optional instructions for clients
68
+ """
69
+ self.name = name
70
+ self.version = version
71
+ self.instructions = instructions
72
+
73
+ self._tool_registry = tool_registry or get_tool_registry()
74
+ self._resource_registry = resource_registry or get_resource_registry()
75
+ self._prompt_registry = prompt_registry or get_prompt_registry()
76
+
77
+ self._initialized = False
78
+ self._client_info: Optional[Dict[str, Any]] = None
79
+ self._protocol_version: str = PROTOCOL_VERSION
80
+
81
+ # Cancellation support
82
+ self._active_requests: Dict[str, asyncio.Task] = {}
83
+ self._cancelled_requests: Set[str] = set()
84
+
85
+ # Progress notification callback
86
+ self._progress_callback: Optional[Callable] = None
87
+
88
+ # Method handlers
89
+ self._handlers: Dict[str, Callable] = {
90
+ "initialize": self._handle_initialize,
91
+ "ping": self._handle_ping,
92
+ "tools/list": self._handle_tools_list,
93
+ "tools/call": self._handle_tools_call,
94
+ "tools/search": self._handle_tools_search, # Extension method
95
+ "resources/list": self._handle_resources_list,
96
+ "resources/read": self._handle_resources_read,
97
+ "prompts/list": self._handle_prompts_list,
98
+ "prompts/get": self._handle_prompts_get,
99
+ "logging/setLevel": self._handle_set_log_level,
100
+ }
101
+
102
+ def get_capabilities(self) -> Dict[str, Any]:
103
+ """Get server capabilities per MCP 2025-11-25 spec."""
104
+ return {
105
+ "tools": {
106
+ "listChanged": True,
107
+ },
108
+ "resources": {
109
+ "subscribe": False,
110
+ "listChanged": True,
111
+ },
112
+ "prompts": {
113
+ "listChanged": True,
114
+ },
115
+ "logging": {},
116
+ }
117
+
118
+ def set_progress_callback(self, callback: Callable) -> None:
119
+ """Set callback for progress notifications."""
120
+ self._progress_callback = callback
121
+
122
+ async def send_progress(self, progress_token: str, progress: float, total: Optional[float] = None) -> None:
123
+ """Send progress notification for long-running operations."""
124
+ if self._progress_callback:
125
+ notification = {
126
+ "jsonrpc": "2.0",
127
+ "method": "notifications/progress",
128
+ "params": {
129
+ "progressToken": progress_token,
130
+ "progress": progress,
131
+ }
132
+ }
133
+ if total is not None:
134
+ notification["params"]["total"] = total
135
+ await self._progress_callback(notification)
136
+
137
+ def is_cancelled(self, request_id: str) -> bool:
138
+ """Check if a request has been cancelled."""
139
+ return request_id in self._cancelled_requests
140
+
141
+ async def handle_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
142
+ """
143
+ Handle an incoming JSON-RPC message.
144
+
145
+ Args:
146
+ message: JSON-RPC message
147
+
148
+ Returns:
149
+ Response message or None for notifications
150
+ """
151
+ # Validate JSON-RPC format
152
+ if message.get("jsonrpc") != "2.0":
153
+ return self._error_response(None, INVALID_REQUEST, "Invalid JSON-RPC version")
154
+
155
+ method = message.get("method")
156
+ params = message.get("params", {})
157
+ msg_id = message.get("id")
158
+
159
+ # Notifications don't have an id
160
+ is_notification = msg_id is None
161
+
162
+ # Handle initialized notification
163
+ if method == "notifications/initialized":
164
+ self._initialized = True
165
+ logger.debug("Client sent initialized notification")
166
+ return None
167
+
168
+ # Handle cancellation notification
169
+ if method == "notifications/cancelled":
170
+ request_id = params.get("requestId")
171
+ if request_id:
172
+ self._cancelled_requests.add(str(request_id))
173
+ # Cancel active task if exists
174
+ if str(request_id) in self._active_requests:
175
+ task = self._active_requests[str(request_id)]
176
+ task.cancel()
177
+ logger.debug(f"Cancelled request: {request_id}")
178
+ return None
179
+
180
+ # Check if method exists
181
+ handler = self._handlers.get(method)
182
+ if handler is None:
183
+ if is_notification:
184
+ return None
185
+ return self._error_response(msg_id, METHOD_NOT_FOUND, f"Method not found: {method}")
186
+
187
+ # Execute handler
188
+ try:
189
+ result = await handler(params)
190
+ if is_notification:
191
+ return None
192
+ return self._success_response(msg_id, result)
193
+ except Exception as e:
194
+ logger.exception(f"Error handling {method}")
195
+ if is_notification:
196
+ return None
197
+ return self._error_response(msg_id, INTERNAL_ERROR, str(e))
198
+
199
+ async def _handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:
200
+ """Handle initialize request."""
201
+ self._client_info = params.get("clientInfo")
202
+ client_version = params.get("protocolVersion", PROTOCOL_VERSION)
203
+
204
+ # Negotiate protocol version
205
+ if client_version in SUPPORTED_VERSIONS:
206
+ self._protocol_version = client_version
207
+ else:
208
+ self._protocol_version = PROTOCOL_VERSION
209
+
210
+ logger.info(f"Client initialized: {self._client_info}, protocol: {self._protocol_version}")
211
+
212
+ result = {
213
+ "protocolVersion": self._protocol_version,
214
+ "capabilities": self.get_capabilities(),
215
+ "serverInfo": {
216
+ "name": self.name,
217
+ "version": self.version,
218
+ },
219
+ }
220
+
221
+ if self.instructions:
222
+ result["instructions"] = self.instructions
223
+
224
+ return result
225
+
226
+ async def _handle_ping(self, params: Dict[str, Any]) -> Dict[str, Any]:
227
+ """Handle ping request."""
228
+ return {}
229
+
230
+ async def _handle_tools_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
231
+ """Handle tools/list request with pagination per MCP 2025-11-25 spec."""
232
+ cursor = params.get("cursor")
233
+
234
+ try:
235
+ tools, next_cursor = self._tool_registry.list_paginated(cursor=cursor)
236
+ result = {"tools": tools}
237
+ if next_cursor:
238
+ result["nextCursor"] = next_cursor
239
+ return result
240
+ except ValueError as e:
241
+ # Invalid cursor - return JSON-RPC error
242
+ raise ValueError(f"Invalid cursor: {e}")
243
+
244
+ async def _handle_tools_search(self, params: Dict[str, Any]) -> Dict[str, Any]:
245
+ """
246
+ Handle tools/search request (extension method, not in MCP spec).
247
+
248
+ Provides server-side search/filtering of tools.
249
+
250
+ Args in params:
251
+ query: Text to search in name, description, tags
252
+ category: Filter by category
253
+ tags: Filter by tags (any match)
254
+ readOnly: Filter by readOnlyHint
255
+ cursor: Pagination cursor
256
+ """
257
+ query = params.get("query")
258
+ category = params.get("category")
259
+ tags = params.get("tags")
260
+ read_only = params.get("readOnly")
261
+ cursor = params.get("cursor")
262
+
263
+ try:
264
+ tools, next_cursor, total = self._tool_registry.search(
265
+ query=query,
266
+ category=category,
267
+ tags=tags,
268
+ read_only=read_only,
269
+ cursor=cursor,
270
+ )
271
+ result = {
272
+ "tools": tools,
273
+ "total": total,
274
+ }
275
+ if next_cursor:
276
+ result["nextCursor"] = next_cursor
277
+ return result
278
+ except ValueError as e:
279
+ raise ValueError(f"Search error: {e}")
280
+
281
+ async def _handle_tools_call(self, params: Dict[str, Any]) -> Dict[str, Any]:
282
+ """Handle tools/call request."""
283
+ tool_name = params.get("name")
284
+ arguments = params.get("arguments", {})
285
+
286
+ if not tool_name:
287
+ raise ValueError("Tool name required")
288
+
289
+ tool = self._tool_registry.get(tool_name)
290
+ if tool is None:
291
+ raise ValueError(f"Tool not found: {tool_name}")
292
+
293
+ # Execute tool
294
+ try:
295
+ if asyncio.iscoroutinefunction(tool.handler):
296
+ result = await tool.handler(**arguments)
297
+ else:
298
+ result = tool.handler(**arguments)
299
+
300
+ # Format result as MCP content
301
+ if isinstance(result, str):
302
+ content = [{"type": "text", "text": result}]
303
+ elif isinstance(result, dict):
304
+ content = [{"type": "text", "text": json.dumps(result, indent=2)}]
305
+ elif isinstance(result, list):
306
+ content = [{"type": "text", "text": json.dumps(result, indent=2)}]
307
+ else:
308
+ content = [{"type": "text", "text": str(result)}]
309
+
310
+ return {"content": content, "isError": False}
311
+ except Exception as e:
312
+ logger.exception(f"Tool execution error: {tool_name}")
313
+ return {
314
+ "content": [{"type": "text", "text": f"Error: {str(e)}"}],
315
+ "isError": True,
316
+ }
317
+
318
+ async def _handle_resources_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
319
+ """Handle resources/list request with pagination per MCP 2025-11-25 spec."""
320
+ cursor = params.get("cursor")
321
+
322
+ try:
323
+ resources, next_cursor = self._resource_registry.list_paginated(cursor=cursor)
324
+ result = {"resources": resources}
325
+ if next_cursor:
326
+ result["nextCursor"] = next_cursor
327
+ return result
328
+ except ValueError as e:
329
+ raise ValueError(f"Invalid cursor: {e}")
330
+
331
+ async def _handle_resources_read(self, params: Dict[str, Any]) -> Dict[str, Any]:
332
+ """Handle resources/read request."""
333
+ uri = params.get("uri")
334
+
335
+ if not uri:
336
+ raise ValueError("Resource URI required")
337
+
338
+ resource = self._resource_registry.get(uri)
339
+ if resource is None:
340
+ raise ValueError(f"Resource not found: {uri}")
341
+
342
+ # Execute resource handler
343
+ try:
344
+ if asyncio.iscoroutinefunction(resource.handler):
345
+ result = await resource.handler()
346
+ else:
347
+ result = resource.handler()
348
+
349
+ # Format result
350
+ if isinstance(result, str):
351
+ content = [{"uri": uri, "mimeType": resource.mime_type, "text": result}]
352
+ elif isinstance(result, dict):
353
+ content = [{"uri": uri, "mimeType": "application/json", "text": json.dumps(result)}]
354
+ else:
355
+ content = [{"uri": uri, "mimeType": resource.mime_type, "text": str(result)}]
356
+
357
+ return {"contents": content}
358
+ except Exception:
359
+ logger.exception(f"Resource read error: {uri}")
360
+ raise
361
+
362
+ async def _handle_prompts_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
363
+ """Handle prompts/list request with pagination per MCP 2025-11-25 spec."""
364
+ cursor = params.get("cursor")
365
+
366
+ try:
367
+ prompts, next_cursor = self._prompt_registry.list_paginated(cursor=cursor)
368
+ result = {"prompts": prompts}
369
+ if next_cursor:
370
+ result["nextCursor"] = next_cursor
371
+ return result
372
+ except ValueError as e:
373
+ raise ValueError(f"Invalid cursor: {e}")
374
+
375
+ async def _handle_prompts_get(self, params: Dict[str, Any]) -> Dict[str, Any]:
376
+ """Handle prompts/get request."""
377
+ prompt_name = params.get("name")
378
+ arguments = params.get("arguments", {})
379
+
380
+ if not prompt_name:
381
+ raise ValueError("Prompt name required")
382
+
383
+ prompt = self._prompt_registry.get(prompt_name)
384
+ if prompt is None:
385
+ raise ValueError(f"Prompt not found: {prompt_name}")
386
+
387
+ # Execute prompt handler
388
+ try:
389
+ if asyncio.iscoroutinefunction(prompt.handler):
390
+ result = await prompt.handler(**arguments)
391
+ else:
392
+ result = prompt.handler(**arguments)
393
+
394
+ # Format result as messages
395
+ if isinstance(result, str):
396
+ messages = [{"role": "user", "content": {"type": "text", "text": result}}]
397
+ elif isinstance(result, list):
398
+ messages = result
399
+ else:
400
+ messages = [{"role": "user", "content": {"type": "text", "text": str(result)}}]
401
+
402
+ return {"messages": messages}
403
+ except Exception:
404
+ logger.exception(f"Prompt get error: {prompt_name}")
405
+ raise
406
+
407
+ async def _handle_set_log_level(self, params: Dict[str, Any]) -> Dict[str, Any]:
408
+ """Handle logging/setLevel request per MCP spec."""
409
+ level = params.get("level", "info")
410
+ level_map = {
411
+ "debug": logging.DEBUG,
412
+ "info": logging.INFO,
413
+ "notice": logging.INFO,
414
+ "warning": logging.WARNING,
415
+ "error": logging.ERROR,
416
+ "critical": logging.CRITICAL,
417
+ "alert": logging.CRITICAL,
418
+ "emergency": logging.CRITICAL,
419
+ }
420
+ log_level = level_map.get(level.lower(), logging.INFO)
421
+ logging.getLogger().setLevel(log_level)
422
+ logger.info(f"Log level set to: {level}")
423
+ return {}
424
+
425
+ def _success_response(self, msg_id: Any, result: Any) -> Dict[str, Any]:
426
+ """Create a success response."""
427
+ return {
428
+ "jsonrpc": "2.0",
429
+ "id": msg_id,
430
+ "result": result,
431
+ }
432
+
433
+ def _error_response(self, msg_id: Any, code: int, message: str, data: Any = None) -> Dict[str, Any]:
434
+ """Create an error response."""
435
+ error = {"code": code, "message": message}
436
+ if data is not None:
437
+ error["data"] = data
438
+ return {
439
+ "jsonrpc": "2.0",
440
+ "id": msg_id,
441
+ "error": error,
442
+ }
443
+
444
+ def run(self, transport: str = "stdio", **kwargs) -> None:
445
+ """
446
+ Run the MCP server.
447
+
448
+ Args:
449
+ transport: Transport type ("stdio" or "http-stream")
450
+ **kwargs: Transport-specific options
451
+ """
452
+ if transport == "stdio":
453
+ self.run_stdio()
454
+ elif transport == "http-stream":
455
+ self.run_http_stream(**kwargs)
456
+ else:
457
+ raise ValueError(f"Unknown transport: {transport}")
458
+
459
+ def run_stdio(self) -> None:
460
+ """Run server with STDIO transport."""
461
+ from .transports.stdio import StdioTransport
462
+
463
+ transport = StdioTransport(self)
464
+ asyncio.run(transport.run())
465
+
466
+ def run_http_stream(
467
+ self,
468
+ host: str = "127.0.0.1",
469
+ port: int = 8080,
470
+ endpoint: str = "/mcp",
471
+ **kwargs,
472
+ ) -> None:
473
+ """
474
+ Run server with HTTP Stream transport.
475
+
476
+ Args:
477
+ host: Server host
478
+ port: Server port
479
+ endpoint: MCP endpoint path
480
+ """
481
+ from .transports.http_stream import HTTPStreamTransport
482
+
483
+ transport = HTTPStreamTransport(
484
+ server=self,
485
+ host=host,
486
+ port=port,
487
+ endpoint=endpoint,
488
+ **kwargs,
489
+ )
490
+ transport.run()