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,376 @@
1
+ """
2
+ HTTP Stream Transport for MCP Server
3
+
4
+ Implements the MCP Streamable HTTP transport (MCP 2025-11-25 spec):
5
+ - Single endpoint for all MCP communication
6
+ - POST for client→server messages
7
+ - GET for server→client SSE stream (optional)
8
+ - Session management via MCP-Session-Id header
9
+ - Supports batch (JSON) and stream (SSE) response modes
10
+ - Origin header validation for security
11
+ - MCP-Protocol-Version header support
12
+ """
13
+
14
+ import asyncio
15
+ import json
16
+ import logging
17
+ import time
18
+ import uuid
19
+ from typing import TYPE_CHECKING, Any, Dict, Optional
20
+
21
+ if TYPE_CHECKING:
22
+ from ..server import MCPServer
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # MCP Protocol Version (Updated to 2025-11-25)
27
+ PROTOCOL_VERSION = "2025-11-25"
28
+ SUPPORTED_VERSIONS = ["2025-11-25", "2025-03-26"]
29
+
30
+
31
+ class HTTPStreamTransport:
32
+ """
33
+ HTTP Stream transport for MCP server.
34
+
35
+ Implements the Streamable HTTP transport per MCP 2025-11-25 spec.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ server: "MCPServer",
41
+ host: str = "127.0.0.1",
42
+ port: int = 8080,
43
+ endpoint: str = "/mcp",
44
+ cors_origins: Optional[list] = None,
45
+ allowed_origins: Optional[list] = None,
46
+ api_key: Optional[str] = None,
47
+ session_ttl: int = 3600,
48
+ allow_client_termination: bool = True,
49
+ response_mode: str = "batch",
50
+ resumability_enabled: bool = True,
51
+ event_history_duration: int = 300,
52
+ ):
53
+ """
54
+ Initialize HTTP Stream transport.
55
+
56
+ Args:
57
+ server: MCPServer instance
58
+ host: Server host
59
+ port: Server port
60
+ endpoint: MCP endpoint path
61
+ cors_origins: CORS allowed origins
62
+ allowed_origins: Origins allowed for security validation (None = localhost only)
63
+ api_key: Optional API key for authentication
64
+ session_ttl: Session TTL in seconds
65
+ allow_client_termination: Allow client to terminate session via DELETE
66
+ response_mode: Default response mode ("batch" or "stream")
67
+ resumability_enabled: Enable SSE resumability
68
+ event_history_duration: Duration to keep event history (seconds)
69
+ """
70
+ self.server = server
71
+ self.host = host
72
+ self.port = port
73
+ self.endpoint = endpoint
74
+ self.cors_origins = cors_origins or ["*"]
75
+ self.api_key = api_key
76
+ self.session_ttl = session_ttl
77
+ self.allow_client_termination = allow_client_termination
78
+ self.response_mode = response_mode
79
+ self.resumability_enabled = resumability_enabled
80
+ self.event_history_duration = event_history_duration
81
+
82
+ # Allowed origins for security (MCP 2025-11-25 requirement)
83
+ # Default: only localhost if binding to localhost
84
+ if allowed_origins is None:
85
+ if host in ("127.0.0.1", "localhost", "::1"):
86
+ self.allowed_origins = ["http://localhost", "http://127.0.0.1", "https://localhost", "https://127.0.0.1"]
87
+ else:
88
+ # External binding requires explicit origin config
89
+ self.allowed_origins = None # Will reject all Origin headers
90
+ logger.warning("External binding without allowed_origins - Origin validation will reject all requests with Origin header")
91
+ else:
92
+ self.allowed_origins = allowed_origins
93
+
94
+ # Session storage
95
+ self._sessions: Dict[str, Dict[str, Any]] = {}
96
+
97
+ # SSE event history for resumability
98
+ self._event_history: Dict[str, list] = {}
99
+ self._event_counter = 0
100
+ self._event_timestamps: Dict[str, float] = {}
101
+
102
+ def run(self) -> None:
103
+ """Run the HTTP Stream server."""
104
+ try:
105
+ import uvicorn
106
+ except ImportError:
107
+ raise ImportError("uvicorn required. Install with: pip install uvicorn")
108
+
109
+ app = self._create_app()
110
+
111
+ logger.info(f"MCP server '{self.server.name}' starting on http://{self.host}:{self.port}{self.endpoint}")
112
+
113
+ uvicorn.run(app, host=self.host, port=self.port, log_level="warning")
114
+
115
+ def _validate_origin(self, request_origin: Optional[str]) -> bool:
116
+ """
117
+ Validate Origin header per MCP 2025-11-25 security requirements.
118
+
119
+ Returns True if origin is valid or not present.
120
+ Returns False if origin is present but invalid.
121
+ """
122
+ if request_origin is None:
123
+ # No Origin header - allow (same-origin requests don't send Origin)
124
+ return True
125
+
126
+ if self.allowed_origins is None:
127
+ # No allowed origins configured - reject all Origin headers
128
+ return False
129
+
130
+ # Check if origin matches any allowed origin
131
+ for allowed in self.allowed_origins:
132
+ if request_origin == allowed or request_origin.startswith(allowed):
133
+ return True
134
+
135
+ return False
136
+
137
+ def _validate_protocol_version(self, version: Optional[str]) -> bool:
138
+ """Validate MCP-Protocol-Version header."""
139
+ if version is None:
140
+ # No version header - assume 2025-03-26 for backwards compatibility
141
+ return True
142
+ return version in SUPPORTED_VERSIONS
143
+
144
+ def _create_app(self) -> Any:
145
+ """Create the Starlette/FastAPI application."""
146
+ try:
147
+ from starlette.applications import Starlette
148
+ from starlette.middleware import Middleware
149
+ from starlette.middleware.cors import CORSMiddleware
150
+ from starlette.requests import Request
151
+ from starlette.responses import JSONResponse, Response, StreamingResponse
152
+ from starlette.routing import Route
153
+ except ImportError:
154
+ raise ImportError("starlette required. Install with: pip install starlette")
155
+
156
+ async def mcp_post(request: Request) -> Response:
157
+ """Handle POST requests (client→server messages)."""
158
+ # Validate Origin header (MCP 2025-11-25 security requirement)
159
+ origin = request.headers.get("Origin")
160
+ if not self._validate_origin(origin):
161
+ logger.warning(f"Rejected request with invalid Origin: {origin}")
162
+ return JSONResponse(
163
+ {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Forbidden: Invalid Origin"}},
164
+ status_code=403,
165
+ )
166
+
167
+ # Validate MCP-Protocol-Version header
168
+ protocol_version = request.headers.get("MCP-Protocol-Version")
169
+ if protocol_version and not self._validate_protocol_version(protocol_version):
170
+ return JSONResponse(
171
+ {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": f"Unsupported protocol version: {protocol_version}"}},
172
+ status_code=400,
173
+ )
174
+
175
+ # Check authentication
176
+ if self.api_key:
177
+ auth_header = request.headers.get("Authorization", "")
178
+ if not auth_header.startswith("Bearer ") or auth_header[7:] != self.api_key:
179
+ return JSONResponse(
180
+ {"error": "Unauthorized"},
181
+ status_code=401,
182
+ )
183
+
184
+ # Get or create session (check both header casings for compatibility)
185
+ session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
186
+ if session_id and session_id not in self._sessions:
187
+ # Session expired
188
+ return JSONResponse(
189
+ {"error": "Session not found"},
190
+ status_code=404,
191
+ )
192
+
193
+ # Parse request body
194
+ try:
195
+ body = await request.json()
196
+ except json.JSONDecodeError:
197
+ return JSONResponse(
198
+ {"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}},
199
+ status_code=400,
200
+ )
201
+
202
+ # Handle message
203
+ response = await self.server.handle_message(body)
204
+
205
+ # Check if this is an initialize request
206
+ if body.get("method") == "initialize":
207
+ # Create new session
208
+ new_session_id = str(uuid.uuid4())
209
+ self._sessions[new_session_id] = {
210
+ "created_at": time.time(),
211
+ "last_activity": time.time(),
212
+ }
213
+
214
+ # Add session ID to response headers (MCP 2025-11-25 uses MCP-Session-Id)
215
+ headers = {
216
+ "MCP-Session-Id": new_session_id,
217
+ "MCP-Protocol-Version": PROTOCOL_VERSION,
218
+ }
219
+
220
+ if response:
221
+ return JSONResponse(response, headers=headers)
222
+ return Response(status_code=202, headers=headers)
223
+
224
+ # Update session activity
225
+ if session_id and session_id in self._sessions:
226
+ self._sessions[session_id]["last_activity"] = time.time()
227
+
228
+ # Check Accept header for response mode
229
+ accept = request.headers.get("Accept", "application/json")
230
+
231
+ if "text/event-stream" in accept and response:
232
+ # Stream mode - return SSE
233
+ async def generate_sse():
234
+ event_id = self._next_event_id()
235
+ data = json.dumps(response)
236
+ yield f"id:{event_id}\ndata:{data}\n\n"
237
+
238
+ return StreamingResponse(
239
+ generate_sse(),
240
+ media_type="text/event-stream",
241
+ headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
242
+ )
243
+
244
+ # Batch mode - return JSON
245
+ if response:
246
+ return JSONResponse(
247
+ response,
248
+ headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
249
+ )
250
+
251
+ return Response(
252
+ status_code=202,
253
+ headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
254
+ )
255
+
256
+ async def mcp_get(request: Request) -> Response:
257
+ """Handle GET requests (server→client SSE stream)."""
258
+ # Check both header casings for compatibility
259
+ session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
260
+ if not session_id or session_id not in self._sessions:
261
+ return JSONResponse(
262
+ {"error": "Session required"},
263
+ status_code=400,
264
+ )
265
+
266
+ # Check for Last-Event-ID for resumability
267
+ last_event_id = request.headers.get("Last-Event-ID")
268
+
269
+ async def generate_sse():
270
+ # If resuming, replay missed events
271
+ if last_event_id and session_id in self._event_history:
272
+ history = self._event_history[session_id]
273
+ replay = False
274
+ for event_id, data in history:
275
+ if replay:
276
+ yield f"id:{event_id}\ndata:{data}\n\n"
277
+ elif event_id == last_event_id:
278
+ replay = True
279
+
280
+ # Keep connection alive
281
+ while True:
282
+ await asyncio.sleep(30)
283
+ yield ":keepalive\n\n"
284
+
285
+ return StreamingResponse(
286
+ generate_sse(),
287
+ media_type="text/event-stream",
288
+ headers={
289
+ "Cache-Control": "no-cache",
290
+ "MCP-Protocol-Version": PROTOCOL_VERSION,
291
+ },
292
+ )
293
+
294
+ async def mcp_delete(request: Request) -> Response:
295
+ """Handle DELETE requests (session termination)."""
296
+ if not self.allow_client_termination:
297
+ return Response(status_code=405)
298
+
299
+ session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
300
+ if session_id and session_id in self._sessions:
301
+ del self._sessions[session_id]
302
+ if session_id in self._event_history:
303
+ del self._event_history[session_id]
304
+ return Response(status_code=204)
305
+
306
+ return JSONResponse(
307
+ {"error": "Session not found"},
308
+ status_code=404,
309
+ )
310
+
311
+ async def mcp_options(request: Request) -> Response:
312
+ """Handle OPTIONS requests for CORS preflight."""
313
+ return Response(
314
+ status_code=204,
315
+ headers={
316
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
317
+ "Access-Control-Allow-Headers": "Content-Type, Accept, Authorization, MCP-Session-Id, Mcp-Session-Id, MCP-Protocol-Version, Mcp-Protocol-Version, Last-Event-ID",
318
+ },
319
+ )
320
+
321
+ async def health(request: Request) -> Response:
322
+ """Health check endpoint."""
323
+ return JSONResponse({
324
+ "status": "healthy",
325
+ "server": self.server.name,
326
+ "version": self.server.version,
327
+ "protocol_version": PROTOCOL_VERSION,
328
+ "active_sessions": len(self._sessions),
329
+ })
330
+
331
+ async def root(request: Request) -> Response:
332
+ """Root endpoint."""
333
+ return JSONResponse({
334
+ "message": f"PraisonAI MCP Server: {self.server.name}",
335
+ "mcp_endpoint": self.endpoint,
336
+ "protocol_version": PROTOCOL_VERSION,
337
+ })
338
+
339
+ # Create routes
340
+ routes = [
341
+ Route(self.endpoint, mcp_post, methods=["POST"]),
342
+ Route(self.endpoint, mcp_get, methods=["GET"]),
343
+ Route(self.endpoint, mcp_delete, methods=["DELETE"]),
344
+ Route(self.endpoint, mcp_options, methods=["OPTIONS"]),
345
+ Route("/health", health, methods=["GET"]),
346
+ Route("/", root, methods=["GET"]),
347
+ ]
348
+
349
+ # Create middleware
350
+ middleware = [
351
+ Middleware(
352
+ CORSMiddleware,
353
+ allow_origins=self.cors_origins,
354
+ allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
355
+ allow_headers=["*"],
356
+ ),
357
+ ]
358
+
359
+ return Starlette(routes=routes, middleware=middleware)
360
+
361
+ def _next_event_id(self) -> str:
362
+ """Generate next SSE event ID."""
363
+ self._event_counter += 1
364
+ return str(self._event_counter)
365
+
366
+ def _cleanup_sessions(self) -> None:
367
+ """Clean up expired sessions."""
368
+ now = time.time()
369
+ expired = [
370
+ sid for sid, data in self._sessions.items()
371
+ if now - data["last_activity"] > self.session_ttl
372
+ ]
373
+ for sid in expired:
374
+ del self._sessions[sid]
375
+ if sid in self._event_history:
376
+ del self._event_history[sid]
@@ -0,0 +1,132 @@
1
+ """
2
+ STDIO Transport for MCP Server
3
+
4
+ Implements the MCP STDIO transport protocol:
5
+ - JSON-RPC 2.0 messages over stdin/stdout
6
+ - Messages delimited by newlines
7
+ - Logs go to stderr only
8
+ """
9
+
10
+ import asyncio
11
+ import json
12
+ import logging
13
+ import sys
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from ..server import MCPServer
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class StdioTransport:
23
+ """
24
+ STDIO transport for MCP server.
25
+
26
+ Reads JSON-RPC messages from stdin and writes responses to stdout.
27
+ All logging goes to stderr to avoid corrupting the protocol stream.
28
+ """
29
+
30
+ def __init__(self, server: "MCPServer"):
31
+ """
32
+ Initialize STDIO transport.
33
+
34
+ Args:
35
+ server: MCPServer instance to handle messages
36
+ """
37
+ self.server = server
38
+ self._running = False
39
+
40
+ async def run(self) -> None:
41
+ """Run the STDIO transport loop."""
42
+ self._running = True
43
+
44
+ # Configure logging to stderr only
45
+ handler = logging.StreamHandler(sys.stderr)
46
+ handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
47
+ logger.addHandler(handler)
48
+
49
+ logger.info(f"MCP server '{self.server.name}' starting on STDIO transport")
50
+
51
+ # Use asyncio streams for non-blocking I/O
52
+ loop = asyncio.get_event_loop()
53
+ reader = asyncio.StreamReader()
54
+ protocol = asyncio.StreamReaderProtocol(reader)
55
+
56
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
57
+
58
+ # Create writer for stdout
59
+ write_transport, write_protocol = await loop.connect_write_pipe(
60
+ asyncio.streams.FlowControlMixin, sys.stdout
61
+ )
62
+ writer = asyncio.StreamWriter(write_transport, write_protocol, reader, loop)
63
+
64
+ try:
65
+ while self._running:
66
+ try:
67
+ # Read a line (JSON-RPC message)
68
+ line = await reader.readline()
69
+ if not line:
70
+ # EOF reached
71
+ break
72
+
73
+ line = line.decode("utf-8").strip()
74
+ if not line:
75
+ continue
76
+
77
+ # Parse JSON-RPC message
78
+ try:
79
+ message = json.loads(line)
80
+ except json.JSONDecodeError as e:
81
+ logger.error(f"JSON parse error: {e}")
82
+ error_response = {
83
+ "jsonrpc": "2.0",
84
+ "id": None,
85
+ "error": {
86
+ "code": -32700,
87
+ "message": f"Parse error: {e}",
88
+ },
89
+ }
90
+ await self._write_message(writer, error_response)
91
+ continue
92
+
93
+ # Handle message
94
+ response = await self.server.handle_message(message)
95
+
96
+ # Write response if not a notification
97
+ if response is not None:
98
+ await self._write_message(writer, response)
99
+
100
+ except asyncio.CancelledError:
101
+ break
102
+ except Exception as e:
103
+ logger.exception(f"Error processing message: {e}")
104
+
105
+ finally:
106
+ self._running = False
107
+ writer.close()
108
+ logger.info("MCP server stopped")
109
+
110
+ async def _write_message(self, writer: asyncio.StreamWriter, message: dict) -> None:
111
+ """Write a JSON-RPC message to stdout."""
112
+ try:
113
+ data = json.dumps(message) + "\n"
114
+ writer.write(data.encode("utf-8"))
115
+ await writer.drain()
116
+ except Exception as e:
117
+ logger.error(f"Error writing message: {e}")
118
+
119
+ def stop(self) -> None:
120
+ """Stop the transport."""
121
+ self._running = False
122
+
123
+
124
+ def run_stdio_server(server: "MCPServer") -> None:
125
+ """
126
+ Convenience function to run a server with STDIO transport.
127
+
128
+ Args:
129
+ server: MCPServer instance
130
+ """
131
+ transport = StdioTransport(server)
132
+ asyncio.run(transport.run())
@@ -0,0 +1,84 @@
1
+ """
2
+ PraisonAI Persistence Layer
3
+
4
+ Provides database integrations for:
5
+ - Conversation history persistence (ConversationStore)
6
+ - Knowledge storage and retrieval (KnowledgeStore)
7
+ - Session/state management (StateStore)
8
+
9
+ All integrations use lazy loading to avoid importing unused dependencies.
10
+
11
+ Supported Backends (22 total):
12
+ - ConversationStore (6): postgres, mysql, sqlite, singlestore, supabase, surrealdb
13
+ - KnowledgeStore (10): qdrant, pinecone, chroma, weaviate, lancedb, milvus, pgvector, redis, cassandra, clickhouse
14
+ - StateStore (6): redis, dynamodb, firestore, mongodb, upstash, memory
15
+ """
16
+
17
+ from typing import TYPE_CHECKING
18
+
19
+ __all__ = [
20
+ # Base interfaces
21
+ "ConversationStore",
22
+ "ConversationSession",
23
+ "ConversationMessage",
24
+ "KnowledgeStore",
25
+ "KnowledgeDocument",
26
+ "StateStore",
27
+ # Orchestrator
28
+ "PersistenceOrchestrator",
29
+ # Factory functions
30
+ "create_conversation_store",
31
+ "create_knowledge_store",
32
+ "create_state_store",
33
+ "create_stores_from_config",
34
+ # Config
35
+ "PersistenceConfig",
36
+ # Agent hooks
37
+ "wrap_agent_with_persistence",
38
+ "PersistentAgent",
39
+ "create_persistent_session",
40
+ ]
41
+
42
+ # Lazy imports to avoid loading dependencies until needed
43
+ def __getattr__(name: str):
44
+ if name in ("ConversationStore", "ConversationSession", "ConversationMessage"):
45
+ from .conversation.base import ConversationStore, ConversationSession, ConversationMessage
46
+ return locals()[name]
47
+
48
+ if name in ("KnowledgeStore", "KnowledgeDocument"):
49
+ from .knowledge.base import KnowledgeStore, KnowledgeDocument
50
+ return locals()[name]
51
+
52
+ if name == "StateStore":
53
+ from .state.base import StateStore
54
+ return StateStore
55
+
56
+ if name == "PersistenceOrchestrator":
57
+ from .orchestrator import PersistenceOrchestrator
58
+ return PersistenceOrchestrator
59
+
60
+ if name == "PersistenceConfig":
61
+ from .config import PersistenceConfig
62
+ return PersistenceConfig
63
+
64
+ if name == "create_conversation_store":
65
+ from .factory import create_conversation_store
66
+ return create_conversation_store
67
+
68
+ if name == "create_knowledge_store":
69
+ from .factory import create_knowledge_store
70
+ return create_knowledge_store
71
+
72
+ if name == "create_state_store":
73
+ from .factory import create_state_store
74
+ return create_state_store
75
+
76
+ if name == "create_stores_from_config":
77
+ from .factory import create_stores_from_config
78
+ return create_stores_from_config
79
+
80
+ if name in ("wrap_agent_with_persistence", "PersistentAgent", "create_persistent_session"):
81
+ from .hooks.agent_hooks import wrap_agent_with_persistence, PersistentAgent, create_persistent_session
82
+ return locals()[name]
83
+
84
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")