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,280 @@
1
+ """
2
+ Registry Bridge Adapter
3
+
4
+ Bridges praisonaiagents.tools registry/lazy TOOL_MAPPINGS into
5
+ praisonai.mcp_server registry WITHOUT duplicating tool definitions
6
+ or importing tools eagerly.
7
+
8
+ This adapter:
9
+ - Enumerates available tools (metadata only)
10
+ - Loads tool handler lazily on first call
11
+ - Handles name collisions with clear errors
12
+ - Is optional and safe-by-default
13
+ """
14
+
15
+ import logging
16
+ from typing import Any, Callable, Dict, List, Optional, Set
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # Track registered tools to avoid duplicates
21
+ _registered_tools: Set[str] = set()
22
+ _bridge_enabled: bool = False
23
+
24
+
25
+ def is_bridge_available() -> bool:
26
+ """Check if praisonaiagents.tools is available."""
27
+ try:
28
+ import importlib.util
29
+ spec = importlib.util.find_spec("praisonaiagents.tools")
30
+ return spec is not None
31
+ except (ImportError, ModuleNotFoundError):
32
+ return False
33
+
34
+
35
+ def get_tool_mappings() -> Dict[str, Any]:
36
+ """Get TOOL_MAPPINGS from praisonaiagents.tools."""
37
+ try:
38
+ from praisonaiagents.tools import TOOL_MAPPINGS
39
+ return TOOL_MAPPINGS
40
+ except ImportError:
41
+ return {}
42
+
43
+
44
+ def _create_lazy_handler(module_path: str, class_name: Optional[str], tool_name: str) -> Callable:
45
+ """
46
+ Create a lazy handler that imports the tool only when called.
47
+
48
+ Args:
49
+ module_path: Python module path
50
+ class_name: Optional class name within module
51
+ tool_name: Tool name for error messages
52
+
53
+ Returns:
54
+ Callable that lazily loads and executes the tool
55
+ """
56
+ _cached_handler = None
57
+
58
+ def lazy_handler(**kwargs) -> Any:
59
+ nonlocal _cached_handler
60
+
61
+ if _cached_handler is None:
62
+ try:
63
+ import importlib
64
+ mod = importlib.import_module(module_path)
65
+
66
+ if class_name:
67
+ tool_class = getattr(mod, class_name)
68
+ tool_instance = tool_class()
69
+ if hasattr(tool_instance, 'run'):
70
+ _cached_handler = tool_instance.run
71
+ elif hasattr(tool_instance, '__call__'):
72
+ _cached_handler = tool_instance
73
+ else:
74
+ raise AttributeError(f"Tool class {class_name} has no run or __call__ method")
75
+ else:
76
+ # Function-based tool
77
+ func_name = tool_name.split('.')[-1]
78
+ if hasattr(mod, func_name):
79
+ _cached_handler = getattr(mod, func_name)
80
+ elif hasattr(mod, 'run'):
81
+ _cached_handler = mod.run
82
+ else:
83
+ raise AttributeError(f"Module {module_path} has no {func_name} or run function")
84
+
85
+ except Exception as e:
86
+ logger.error(f"Failed to load tool {tool_name}: {e}")
87
+ raise RuntimeError(f"Tool {tool_name} failed to load: {e}")
88
+
89
+ return _cached_handler(**kwargs)
90
+
91
+ return lazy_handler
92
+
93
+
94
+ def _infer_tool_hints(tool_name: str) -> Dict[str, bool]:
95
+ """
96
+ Infer tool annotation hints from tool name/category.
97
+
98
+ Args:
99
+ tool_name: Full tool name (e.g., "praisonai.memory.show")
100
+
101
+ Returns:
102
+ Dict with readOnlyHint, destructiveHint, idempotentHint, openWorldHint
103
+ """
104
+ name_lower = tool_name.lower()
105
+
106
+ # Default hints per MCP 2025-11-25 spec
107
+ hints = {
108
+ "read_only_hint": False,
109
+ "destructive_hint": True,
110
+ "idempotent_hint": False,
111
+ "open_world_hint": True,
112
+ }
113
+
114
+ # Read-only patterns
115
+ read_only_patterns = ['show', 'list', 'get', 'read', 'search', 'find', 'query', 'info', 'status']
116
+ for pattern in read_only_patterns:
117
+ if pattern in name_lower:
118
+ hints["read_only_hint"] = True
119
+ hints["destructive_hint"] = False
120
+ break
121
+
122
+ # Idempotent patterns
123
+ idempotent_patterns = ['set', 'update', 'configure']
124
+ for pattern in idempotent_patterns:
125
+ if pattern in name_lower:
126
+ hints["idempotent_hint"] = True
127
+ break
128
+
129
+ # Closed-world patterns (internal tools)
130
+ closed_world_patterns = ['memory', 'session', 'config', 'local']
131
+ for pattern in closed_world_patterns:
132
+ if pattern in name_lower:
133
+ hints["open_world_hint"] = False
134
+ break
135
+
136
+ return hints
137
+
138
+
139
+ def _extract_category(tool_name: str) -> Optional[str]:
140
+ """Extract category from tool name."""
141
+ parts = tool_name.split('.')
142
+ if len(parts) >= 2:
143
+ return parts[-2] # e.g., "praisonai.memory.show" -> "memory"
144
+ return None
145
+
146
+
147
+ def register_praisonai_tools(
148
+ namespace_prefix: str = "praisonai.agents.",
149
+ skip_on_collision: bool = True,
150
+ ) -> int:
151
+ """
152
+ Bridge praisonaiagents tools to MCP registry.
153
+
154
+ Args:
155
+ namespace_prefix: Prefix to add to tool names to avoid collisions
156
+ skip_on_collision: If True, skip tools that already exist; if False, raise error
157
+
158
+ Returns:
159
+ Number of tools registered
160
+ """
161
+ global _bridge_enabled
162
+
163
+ if not is_bridge_available():
164
+ logger.debug("praisonaiagents.tools not available, skipping bridge")
165
+ return 0
166
+
167
+ from ..registry import get_tool_registry, MCPToolDefinition
168
+
169
+ registry = get_tool_registry()
170
+ tool_mappings = get_tool_mappings()
171
+
172
+ registered_count = 0
173
+
174
+ for tool_name, mapping_info in tool_mappings.items():
175
+ # Handle different mapping formats
176
+ if isinstance(mapping_info, tuple):
177
+ if len(mapping_info) >= 2:
178
+ module_path, class_name = mapping_info[0], mapping_info[1]
179
+ else:
180
+ module_path, class_name = mapping_info[0], None
181
+ elif isinstance(mapping_info, str):
182
+ module_path, class_name = mapping_info, None
183
+ else:
184
+ logger.warning(f"Unknown mapping format for {tool_name}: {type(mapping_info)}")
185
+ continue
186
+
187
+ # Create namespaced name
188
+ full_name = f"{namespace_prefix}{tool_name}"
189
+
190
+ # Check for collision
191
+ if full_name in _registered_tools:
192
+ if skip_on_collision:
193
+ logger.debug(f"Skipping duplicate tool: {full_name}")
194
+ continue
195
+ else:
196
+ raise ValueError(f"Tool name collision: {full_name}")
197
+
198
+ # Check if already in registry
199
+ existing = registry.get(full_name)
200
+ if existing is not None:
201
+ if skip_on_collision:
202
+ logger.debug(f"Tool already registered: {full_name}")
203
+ continue
204
+ else:
205
+ raise ValueError(f"Tool already in registry: {full_name}")
206
+
207
+ try:
208
+ # Create lazy handler
209
+ handler = _create_lazy_handler(module_path, class_name, tool_name)
210
+
211
+ # Infer hints
212
+ hints = _infer_tool_hints(tool_name)
213
+ category = _extract_category(tool_name)
214
+
215
+ # Create tool definition
216
+ tool_def = MCPToolDefinition(
217
+ name=full_name,
218
+ description=f"PraisonAI Agents tool: {tool_name}",
219
+ handler=handler,
220
+ input_schema={"type": "object", "properties": {}}, # Will be refined on first call
221
+ category=category,
222
+ **hints,
223
+ )
224
+
225
+ # Register directly to avoid re-processing
226
+ registry._tools[full_name] = tool_def
227
+ _registered_tools.add(full_name)
228
+ registered_count += 1
229
+
230
+ logger.debug(f"Registered bridged tool: {full_name}")
231
+
232
+ except Exception as e:
233
+ logger.warning(f"Failed to register tool {tool_name}: {e}")
234
+ continue
235
+
236
+ _bridge_enabled = True
237
+ logger.info(f"Registry bridge registered {registered_count} tools from praisonaiagents")
238
+
239
+ return registered_count
240
+
241
+
242
+ def unregister_bridged_tools() -> int:
243
+ """
244
+ Remove all bridged tools from the registry.
245
+
246
+ Returns:
247
+ Number of tools removed
248
+ """
249
+ global _bridge_enabled
250
+
251
+ from ..registry import get_tool_registry
252
+
253
+ registry = get_tool_registry()
254
+ removed_count = 0
255
+
256
+ for tool_name in list(_registered_tools):
257
+ if tool_name in registry._tools:
258
+ del registry._tools[tool_name]
259
+ removed_count += 1
260
+
261
+ _registered_tools.clear()
262
+ _bridge_enabled = False
263
+
264
+ logger.info(f"Removed {removed_count} bridged tools")
265
+ return removed_count
266
+
267
+
268
+ def is_bridge_enabled() -> bool:
269
+ """Check if the bridge is currently enabled."""
270
+ return _bridge_enabled
271
+
272
+
273
+ def get_bridged_tool_count() -> int:
274
+ """Get the number of bridged tools."""
275
+ return len(_registered_tools)
276
+
277
+
278
+ def list_bridged_tools() -> List[str]:
279
+ """List all bridged tool names."""
280
+ return list(_registered_tools)
@@ -0,0 +1,48 @@
1
+ """
2
+ MCP Authentication and Authorization Module
3
+
4
+ Implements OAuth 2.1 and OpenID Connect support per MCP 2025-11-25 specification.
5
+
6
+ Features:
7
+ - API Key authentication
8
+ - OAuth 2.1 authorization framework
9
+ - OpenID Connect Discovery
10
+ - Client ID Metadata Documents
11
+ - Incremental scope handling
12
+ - WWW-Authenticate challenges
13
+ """
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from .oauth import OAuthConfig, OAuthManager
19
+ from .oidc import OIDCDiscovery, OIDCConfig
20
+ from .api_key import APIKeyAuth
21
+ from .scopes import ScopeManager, ScopeChallenge
22
+
23
+ __all__ = [
24
+ "OAuthConfig",
25
+ "OAuthManager",
26
+ "OIDCDiscovery",
27
+ "OIDCConfig",
28
+ "APIKeyAuth",
29
+ "ScopeManager",
30
+ "ScopeChallenge",
31
+ ]
32
+
33
+
34
+ def __getattr__(name: str):
35
+ """Lazy load auth components."""
36
+ if name in ("OAuthConfig", "OAuthManager"):
37
+ from .oauth import OAuthConfig, OAuthManager
38
+ return OAuthConfig if name == "OAuthConfig" else OAuthManager
39
+ elif name in ("OIDCDiscovery", "OIDCConfig"):
40
+ from .oidc import OIDCDiscovery, OIDCConfig
41
+ return OIDCDiscovery if name == "OIDCDiscovery" else OIDCConfig
42
+ elif name == "APIKeyAuth":
43
+ from .api_key import APIKeyAuth
44
+ return APIKeyAuth
45
+ elif name in ("ScopeManager", "ScopeChallenge"):
46
+ from .scopes import ScopeManager, ScopeChallenge
47
+ return ScopeManager if name == "ScopeManager" else ScopeChallenge
48
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,291 @@
1
+ """
2
+ API Key Authentication for MCP
3
+
4
+ Simple API key authentication for MCP servers.
5
+ """
6
+
7
+ import hashlib
8
+ import hmac
9
+ import logging
10
+ import secrets
11
+ import time
12
+ from dataclasses import dataclass, field
13
+ from typing import Dict, List, Optional
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @dataclass
19
+ class APIKey:
20
+ """API Key representation."""
21
+ key_id: str
22
+ key_hash: str
23
+ name: Optional[str] = None
24
+ scopes: List[str] = field(default_factory=list)
25
+ created_at: float = field(default_factory=time.time)
26
+ expires_at: Optional[float] = None
27
+ last_used_at: Optional[float] = None
28
+ metadata: Dict[str, str] = field(default_factory=dict)
29
+
30
+ def is_expired(self) -> bool:
31
+ if self.expires_at is None:
32
+ return False
33
+ return time.time() >= self.expires_at
34
+
35
+ def has_scope(self, scope: str) -> bool:
36
+ if not self.scopes:
37
+ return True # No scope restriction
38
+ return scope in self.scopes or "*" in self.scopes
39
+
40
+
41
+ class APIKeyAuth:
42
+ """
43
+ API Key authentication manager.
44
+
45
+ Supports:
46
+ - Key generation and validation
47
+ - Scope-based authorization
48
+ - Key rotation
49
+ - Rate limiting (optional)
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ keys: Optional[Dict[str, APIKey]] = None,
55
+ allow_env_key: bool = True,
56
+ env_key_name: str = "MCP_API_KEY",
57
+ ):
58
+ """
59
+ Initialize API key auth.
60
+
61
+ Args:
62
+ keys: Pre-configured API keys
63
+ allow_env_key: Allow API key from environment
64
+ env_key_name: Environment variable name for API key
65
+ """
66
+ self._keys: Dict[str, APIKey] = keys or {}
67
+ self._allow_env_key = allow_env_key
68
+ self._env_key_name = env_key_name
69
+ self._env_key_hash: Optional[str] = None
70
+
71
+ # Load env key if configured
72
+ if allow_env_key:
73
+ self._load_env_key()
74
+
75
+ def _load_env_key(self) -> None:
76
+ """Load API key from environment."""
77
+ import os
78
+ env_key = os.environ.get(self._env_key_name)
79
+ if env_key:
80
+ self._env_key_hash = self._hash_key(env_key)
81
+ logger.debug(f"Loaded API key from {self._env_key_name}")
82
+
83
+ def _hash_key(self, key: str) -> str:
84
+ """Hash an API key for storage."""
85
+ return hashlib.sha256(key.encode()).hexdigest()
86
+
87
+ def generate_key(
88
+ self,
89
+ name: Optional[str] = None,
90
+ scopes: Optional[List[str]] = None,
91
+ expires_in: Optional[int] = None,
92
+ metadata: Optional[Dict[str, str]] = None,
93
+ ) -> tuple[str, APIKey]:
94
+ """
95
+ Generate a new API key.
96
+
97
+ Args:
98
+ name: Key name/description
99
+ scopes: Allowed scopes
100
+ expires_in: Expiration in seconds
101
+ metadata: Additional metadata
102
+
103
+ Returns:
104
+ Tuple of (raw_key, APIKey)
105
+ """
106
+ # Generate key
107
+ raw_key = f"mcp_{secrets.token_urlsafe(32)}"
108
+ key_id = secrets.token_hex(8)
109
+ key_hash = self._hash_key(raw_key)
110
+
111
+ expires_at = None
112
+ if expires_in:
113
+ expires_at = time.time() + expires_in
114
+
115
+ api_key = APIKey(
116
+ key_id=key_id,
117
+ key_hash=key_hash,
118
+ name=name,
119
+ scopes=scopes or [],
120
+ expires_at=expires_at,
121
+ metadata=metadata or {},
122
+ )
123
+
124
+ self._keys[key_id] = api_key
125
+
126
+ return raw_key, api_key
127
+
128
+ def validate(
129
+ self,
130
+ key: str,
131
+ required_scope: Optional[str] = None,
132
+ ) -> tuple[bool, Optional[APIKey]]:
133
+ """
134
+ Validate an API key.
135
+
136
+ Args:
137
+ key: Raw API key
138
+ required_scope: Required scope for authorization
139
+
140
+ Returns:
141
+ Tuple of (is_valid, api_key)
142
+ """
143
+ key_hash = self._hash_key(key)
144
+
145
+ # Check environment key
146
+ if self._env_key_hash and hmac.compare_digest(key_hash, self._env_key_hash):
147
+ # Env key has all scopes
148
+ return True, None
149
+
150
+ # Check stored keys
151
+ for api_key in self._keys.values():
152
+ if hmac.compare_digest(key_hash, api_key.key_hash):
153
+ if api_key.is_expired():
154
+ return False, None
155
+
156
+ if required_scope and not api_key.has_scope(required_scope):
157
+ return False, api_key
158
+
159
+ # Update last used
160
+ api_key.last_used_at = time.time()
161
+
162
+ return True, api_key
163
+
164
+ return False, None
165
+
166
+ def validate_header(
167
+ self,
168
+ auth_header: str,
169
+ required_scope: Optional[str] = None,
170
+ ) -> tuple[bool, Optional[APIKey]]:
171
+ """
172
+ Validate Authorization header.
173
+
174
+ Supports:
175
+ - Bearer <key>
176
+ - ApiKey <key>
177
+
178
+ Args:
179
+ auth_header: Authorization header value
180
+ required_scope: Required scope
181
+
182
+ Returns:
183
+ Tuple of (is_valid, api_key)
184
+ """
185
+ if not auth_header:
186
+ return False, None
187
+
188
+ parts = auth_header.split(" ", 1)
189
+ if len(parts) != 2:
190
+ return False, None
191
+
192
+ scheme, key = parts
193
+ if scheme.lower() not in ("bearer", "apikey"):
194
+ return False, None
195
+
196
+ return self.validate(key, required_scope)
197
+
198
+ def revoke(self, key_id: str) -> bool:
199
+ """Revoke an API key."""
200
+ if key_id in self._keys:
201
+ del self._keys[key_id]
202
+ return True
203
+ return False
204
+
205
+ def list_keys(self) -> List[APIKey]:
206
+ """List all API keys (without hashes)."""
207
+ return list(self._keys.values())
208
+
209
+ def add_key(
210
+ self,
211
+ raw_key: str,
212
+ name: Optional[str] = None,
213
+ scopes: Optional[List[str]] = None,
214
+ ) -> APIKey:
215
+ """
216
+ Add a pre-existing API key.
217
+
218
+ Args:
219
+ raw_key: Raw API key
220
+ name: Key name
221
+ scopes: Allowed scopes
222
+
223
+ Returns:
224
+ APIKey instance
225
+ """
226
+ key_id = secrets.token_hex(8)
227
+ key_hash = self._hash_key(raw_key)
228
+
229
+ api_key = APIKey(
230
+ key_id=key_id,
231
+ key_hash=key_hash,
232
+ name=name,
233
+ scopes=scopes or [],
234
+ )
235
+
236
+ self._keys[key_id] = api_key
237
+ return api_key
238
+
239
+
240
+ def create_auth_middleware(
241
+ api_key_auth: APIKeyAuth,
242
+ required_scope: Optional[str] = None,
243
+ exclude_paths: Optional[List[str]] = None,
244
+ ):
245
+ """
246
+ Create authentication middleware for Starlette/FastAPI.
247
+
248
+ Args:
249
+ api_key_auth: APIKeyAuth instance
250
+ required_scope: Required scope for all requests
251
+ exclude_paths: Paths to exclude from auth
252
+
253
+ Returns:
254
+ Middleware class
255
+ """
256
+ exclude_paths = exclude_paths or ["/health", "/"]
257
+
258
+ try:
259
+ from starlette.middleware.base import BaseHTTPMiddleware
260
+ from starlette.requests import Request
261
+ from starlette.responses import JSONResponse
262
+ except ImportError:
263
+ raise ImportError("starlette required for middleware")
264
+
265
+ class AuthMiddleware(BaseHTTPMiddleware):
266
+ async def dispatch(self, request: Request, call_next):
267
+ # Skip excluded paths
268
+ if request.url.path in exclude_paths:
269
+ return await call_next(request)
270
+
271
+ # Get auth header
272
+ auth_header = request.headers.get("Authorization", "")
273
+
274
+ # Validate
275
+ is_valid, api_key = api_key_auth.validate_header(
276
+ auth_header, required_scope
277
+ )
278
+
279
+ if not is_valid:
280
+ return JSONResponse(
281
+ {"error": "Unauthorized"},
282
+ status_code=401,
283
+ headers={"WWW-Authenticate": "Bearer"},
284
+ )
285
+
286
+ # Add key info to request state
287
+ request.state.api_key = api_key
288
+
289
+ return await call_next(request)
290
+
291
+ return AuthMiddleware