ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.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 (535) hide show
  1. agentui/.prettierrc +15 -0
  2. agentui/QUICKSTART.md +272 -0
  3. agentui/README.md +59 -0
  4. agentui/env.example +16 -0
  5. agentui/jsconfig.json +14 -0
  6. agentui/package-lock.json +4242 -0
  7. agentui/package.json +34 -0
  8. agentui/scripts/postinstall/apply-patches.mjs +260 -0
  9. agentui/src/app.css +61 -0
  10. agentui/src/app.d.ts +13 -0
  11. agentui/src/app.html +12 -0
  12. agentui/src/components/LoadingSpinner.svelte +64 -0
  13. agentui/src/components/ThemeSwitcher.svelte +159 -0
  14. agentui/src/components/index.js +4 -0
  15. agentui/src/lib/api/bots.ts +60 -0
  16. agentui/src/lib/api/chat.ts +22 -0
  17. agentui/src/lib/api/http.ts +25 -0
  18. agentui/src/lib/components/BotCard.svelte +33 -0
  19. agentui/src/lib/components/ChatBubble.svelte +63 -0
  20. agentui/src/lib/components/Toast.svelte +21 -0
  21. agentui/src/lib/config.ts +20 -0
  22. agentui/src/lib/stores/auth.svelte.ts +73 -0
  23. agentui/src/lib/stores/theme.svelte.js +64 -0
  24. agentui/src/lib/stores/toast.svelte.ts +31 -0
  25. agentui/src/lib/utils/conversation.ts +39 -0
  26. agentui/src/routes/+layout.svelte +20 -0
  27. agentui/src/routes/+page.svelte +232 -0
  28. agentui/src/routes/login/+page.svelte +200 -0
  29. agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
  30. agentui/src/routes/talk/[agentId]/+page.ts +7 -0
  31. agentui/static/README.md +1 -0
  32. agentui/svelte.config.js +11 -0
  33. agentui/tailwind.config.ts +53 -0
  34. agentui/tsconfig.json +3 -0
  35. agentui/vite.config.ts +10 -0
  36. ai_parrot-0.17.2.dist-info/METADATA +472 -0
  37. ai_parrot-0.17.2.dist-info/RECORD +535 -0
  38. ai_parrot-0.17.2.dist-info/WHEEL +6 -0
  39. ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
  40. ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
  41. ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
  42. crew-builder/.prettierrc +15 -0
  43. crew-builder/QUICKSTART.md +259 -0
  44. crew-builder/README.md +113 -0
  45. crew-builder/env.example +17 -0
  46. crew-builder/jsconfig.json +14 -0
  47. crew-builder/package-lock.json +4182 -0
  48. crew-builder/package.json +37 -0
  49. crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
  50. crew-builder/src/app.css +62 -0
  51. crew-builder/src/app.d.ts +13 -0
  52. crew-builder/src/app.html +12 -0
  53. crew-builder/src/components/LoadingSpinner.svelte +64 -0
  54. crew-builder/src/components/ThemeSwitcher.svelte +149 -0
  55. crew-builder/src/components/index.js +9 -0
  56. crew-builder/src/lib/api/bots.ts +60 -0
  57. crew-builder/src/lib/api/chat.ts +80 -0
  58. crew-builder/src/lib/api/client.ts +56 -0
  59. crew-builder/src/lib/api/crew/crew.ts +136 -0
  60. crew-builder/src/lib/api/index.ts +5 -0
  61. crew-builder/src/lib/api/o365/auth.ts +65 -0
  62. crew-builder/src/lib/auth/auth.ts +54 -0
  63. crew-builder/src/lib/components/AgentNode.svelte +43 -0
  64. crew-builder/src/lib/components/BotCard.svelte +33 -0
  65. crew-builder/src/lib/components/ChatBubble.svelte +67 -0
  66. crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
  67. crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
  68. crew-builder/src/lib/components/JsonViewer.svelte +24 -0
  69. crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
  70. crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
  71. crew-builder/src/lib/components/Toast.svelte +67 -0
  72. crew-builder/src/lib/components/Toolbar.svelte +157 -0
  73. crew-builder/src/lib/components/index.ts +10 -0
  74. crew-builder/src/lib/config.ts +8 -0
  75. crew-builder/src/lib/stores/auth.svelte.ts +228 -0
  76. crew-builder/src/lib/stores/crewStore.ts +369 -0
  77. crew-builder/src/lib/stores/theme.svelte.js +145 -0
  78. crew-builder/src/lib/stores/toast.svelte.ts +69 -0
  79. crew-builder/src/lib/utils/conversation.ts +39 -0
  80. crew-builder/src/lib/utils/markdown.ts +122 -0
  81. crew-builder/src/lib/utils/talkHistory.ts +47 -0
  82. crew-builder/src/routes/+layout.svelte +20 -0
  83. crew-builder/src/routes/+page.svelte +539 -0
  84. crew-builder/src/routes/agents/+page.svelte +247 -0
  85. crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
  86. crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
  87. crew-builder/src/routes/builder/+page.svelte +204 -0
  88. crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
  89. crew-builder/src/routes/crew/ask/+page.ts +1 -0
  90. crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
  91. crew-builder/src/routes/login/+page.svelte +197 -0
  92. crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
  93. crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
  94. crew-builder/static/README.md +1 -0
  95. crew-builder/svelte.config.js +11 -0
  96. crew-builder/tailwind.config.ts +53 -0
  97. crew-builder/tsconfig.json +3 -0
  98. crew-builder/vite.config.ts +10 -0
  99. mcp_servers/calculator_server.py +309 -0
  100. parrot/__init__.py +27 -0
  101. parrot/__pycache__/__init__.cpython-310.pyc +0 -0
  102. parrot/__pycache__/version.cpython-310.pyc +0 -0
  103. parrot/_version.py +34 -0
  104. parrot/a2a/__init__.py +48 -0
  105. parrot/a2a/client.py +658 -0
  106. parrot/a2a/discovery.py +89 -0
  107. parrot/a2a/mixin.py +257 -0
  108. parrot/a2a/models.py +376 -0
  109. parrot/a2a/server.py +770 -0
  110. parrot/agents/__init__.py +29 -0
  111. parrot/bots/__init__.py +12 -0
  112. parrot/bots/a2a_agent.py +19 -0
  113. parrot/bots/abstract.py +3139 -0
  114. parrot/bots/agent.py +1129 -0
  115. parrot/bots/basic.py +9 -0
  116. parrot/bots/chatbot.py +669 -0
  117. parrot/bots/data.py +1618 -0
  118. parrot/bots/database/__init__.py +5 -0
  119. parrot/bots/database/abstract.py +3071 -0
  120. parrot/bots/database/cache.py +286 -0
  121. parrot/bots/database/models.py +468 -0
  122. parrot/bots/database/prompts.py +154 -0
  123. parrot/bots/database/retries.py +98 -0
  124. parrot/bots/database/router.py +269 -0
  125. parrot/bots/database/sql.py +41 -0
  126. parrot/bots/db/__init__.py +6 -0
  127. parrot/bots/db/abstract.py +556 -0
  128. parrot/bots/db/bigquery.py +602 -0
  129. parrot/bots/db/cache.py +85 -0
  130. parrot/bots/db/documentdb.py +668 -0
  131. parrot/bots/db/elastic.py +1014 -0
  132. parrot/bots/db/influx.py +898 -0
  133. parrot/bots/db/mock.py +96 -0
  134. parrot/bots/db/multi.py +783 -0
  135. parrot/bots/db/prompts.py +185 -0
  136. parrot/bots/db/sql.py +1255 -0
  137. parrot/bots/db/tools.py +212 -0
  138. parrot/bots/document.py +680 -0
  139. parrot/bots/hrbot.py +15 -0
  140. parrot/bots/kb.py +170 -0
  141. parrot/bots/mcp.py +36 -0
  142. parrot/bots/orchestration/README.md +463 -0
  143. parrot/bots/orchestration/__init__.py +1 -0
  144. parrot/bots/orchestration/agent.py +155 -0
  145. parrot/bots/orchestration/crew.py +3330 -0
  146. parrot/bots/orchestration/fsm.py +1179 -0
  147. parrot/bots/orchestration/hr.py +434 -0
  148. parrot/bots/orchestration/storage/__init__.py +4 -0
  149. parrot/bots/orchestration/storage/memory.py +100 -0
  150. parrot/bots/orchestration/storage/mixin.py +119 -0
  151. parrot/bots/orchestration/verify.py +202 -0
  152. parrot/bots/product.py +204 -0
  153. parrot/bots/prompts/__init__.py +96 -0
  154. parrot/bots/prompts/agents.py +155 -0
  155. parrot/bots/prompts/data.py +216 -0
  156. parrot/bots/prompts/output_generation.py +8 -0
  157. parrot/bots/scraper/__init__.py +3 -0
  158. parrot/bots/scraper/models.py +122 -0
  159. parrot/bots/scraper/scraper.py +1173 -0
  160. parrot/bots/scraper/templates.py +115 -0
  161. parrot/bots/stores/__init__.py +5 -0
  162. parrot/bots/stores/local.py +172 -0
  163. parrot/bots/webdev.py +81 -0
  164. parrot/cli.py +17 -0
  165. parrot/clients/__init__.py +16 -0
  166. parrot/clients/base.py +1491 -0
  167. parrot/clients/claude.py +1191 -0
  168. parrot/clients/factory.py +129 -0
  169. parrot/clients/google.py +4567 -0
  170. parrot/clients/gpt.py +1975 -0
  171. parrot/clients/grok.py +432 -0
  172. parrot/clients/groq.py +986 -0
  173. parrot/clients/hf.py +582 -0
  174. parrot/clients/models.py +18 -0
  175. parrot/conf.py +395 -0
  176. parrot/embeddings/__init__.py +9 -0
  177. parrot/embeddings/base.py +157 -0
  178. parrot/embeddings/google.py +98 -0
  179. parrot/embeddings/huggingface.py +74 -0
  180. parrot/embeddings/openai.py +84 -0
  181. parrot/embeddings/processor.py +88 -0
  182. parrot/exceptions.c +13868 -0
  183. parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
  184. parrot/exceptions.pxd +22 -0
  185. parrot/exceptions.pxi +15 -0
  186. parrot/exceptions.pyx +44 -0
  187. parrot/generators/__init__.py +29 -0
  188. parrot/generators/base.py +200 -0
  189. parrot/generators/html.py +293 -0
  190. parrot/generators/react.py +205 -0
  191. parrot/generators/streamlit.py +203 -0
  192. parrot/generators/template.py +105 -0
  193. parrot/handlers/__init__.py +4 -0
  194. parrot/handlers/agent.py +861 -0
  195. parrot/handlers/agents/__init__.py +1 -0
  196. parrot/handlers/agents/abstract.py +900 -0
  197. parrot/handlers/bots.py +338 -0
  198. parrot/handlers/chat.py +915 -0
  199. parrot/handlers/creation.sql +192 -0
  200. parrot/handlers/crew/ARCHITECTURE.md +362 -0
  201. parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
  202. parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
  203. parrot/handlers/crew/__init__.py +0 -0
  204. parrot/handlers/crew/handler.py +801 -0
  205. parrot/handlers/crew/models.py +229 -0
  206. parrot/handlers/crew/redis_persistence.py +523 -0
  207. parrot/handlers/jobs/__init__.py +10 -0
  208. parrot/handlers/jobs/job.py +384 -0
  209. parrot/handlers/jobs/mixin.py +627 -0
  210. parrot/handlers/jobs/models.py +115 -0
  211. parrot/handlers/jobs/worker.py +31 -0
  212. parrot/handlers/models.py +596 -0
  213. parrot/handlers/o365_auth.py +105 -0
  214. parrot/handlers/stream.py +337 -0
  215. parrot/interfaces/__init__.py +6 -0
  216. parrot/interfaces/aws.py +143 -0
  217. parrot/interfaces/credentials.py +113 -0
  218. parrot/interfaces/database.py +27 -0
  219. parrot/interfaces/google.py +1123 -0
  220. parrot/interfaces/hierarchy.py +1227 -0
  221. parrot/interfaces/http.py +651 -0
  222. parrot/interfaces/images/__init__.py +0 -0
  223. parrot/interfaces/images/plugins/__init__.py +24 -0
  224. parrot/interfaces/images/plugins/abstract.py +58 -0
  225. parrot/interfaces/images/plugins/analisys.py +148 -0
  226. parrot/interfaces/images/plugins/classify.py +150 -0
  227. parrot/interfaces/images/plugins/classifybase.py +182 -0
  228. parrot/interfaces/images/plugins/detect.py +150 -0
  229. parrot/interfaces/images/plugins/exif.py +1103 -0
  230. parrot/interfaces/images/plugins/hash.py +52 -0
  231. parrot/interfaces/images/plugins/vision.py +104 -0
  232. parrot/interfaces/images/plugins/yolo.py +66 -0
  233. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  234. parrot/interfaces/o365.py +978 -0
  235. parrot/interfaces/onedrive.py +822 -0
  236. parrot/interfaces/sharepoint.py +1435 -0
  237. parrot/interfaces/soap.py +257 -0
  238. parrot/loaders/__init__.py +8 -0
  239. parrot/loaders/abstract.py +1131 -0
  240. parrot/loaders/audio.py +199 -0
  241. parrot/loaders/basepdf.py +53 -0
  242. parrot/loaders/basevideo.py +1568 -0
  243. parrot/loaders/csv.py +409 -0
  244. parrot/loaders/docx.py +116 -0
  245. parrot/loaders/epubloader.py +316 -0
  246. parrot/loaders/excel.py +199 -0
  247. parrot/loaders/factory.py +55 -0
  248. parrot/loaders/files/__init__.py +0 -0
  249. parrot/loaders/files/abstract.py +39 -0
  250. parrot/loaders/files/html.py +26 -0
  251. parrot/loaders/files/text.py +63 -0
  252. parrot/loaders/html.py +152 -0
  253. parrot/loaders/markdown.py +442 -0
  254. parrot/loaders/pdf.py +373 -0
  255. parrot/loaders/pdfmark.py +320 -0
  256. parrot/loaders/pdftables.py +506 -0
  257. parrot/loaders/ppt.py +476 -0
  258. parrot/loaders/qa.py +63 -0
  259. parrot/loaders/splitters/__init__.py +10 -0
  260. parrot/loaders/splitters/base.py +138 -0
  261. parrot/loaders/splitters/md.py +228 -0
  262. parrot/loaders/splitters/token.py +143 -0
  263. parrot/loaders/txt.py +26 -0
  264. parrot/loaders/video.py +89 -0
  265. parrot/loaders/videolocal.py +218 -0
  266. parrot/loaders/videounderstanding.py +377 -0
  267. parrot/loaders/vimeo.py +167 -0
  268. parrot/loaders/web.py +599 -0
  269. parrot/loaders/youtube.py +504 -0
  270. parrot/manager/__init__.py +5 -0
  271. parrot/manager/manager.py +1030 -0
  272. parrot/mcp/__init__.py +28 -0
  273. parrot/mcp/adapter.py +105 -0
  274. parrot/mcp/cli.py +174 -0
  275. parrot/mcp/client.py +119 -0
  276. parrot/mcp/config.py +75 -0
  277. parrot/mcp/integration.py +842 -0
  278. parrot/mcp/oauth.py +933 -0
  279. parrot/mcp/server.py +225 -0
  280. parrot/mcp/transports/__init__.py +3 -0
  281. parrot/mcp/transports/base.py +279 -0
  282. parrot/mcp/transports/grpc_session.py +163 -0
  283. parrot/mcp/transports/http.py +312 -0
  284. parrot/mcp/transports/mcp.proto +108 -0
  285. parrot/mcp/transports/quic.py +1082 -0
  286. parrot/mcp/transports/sse.py +330 -0
  287. parrot/mcp/transports/stdio.py +309 -0
  288. parrot/mcp/transports/unix.py +395 -0
  289. parrot/mcp/transports/websocket.py +547 -0
  290. parrot/memory/__init__.py +16 -0
  291. parrot/memory/abstract.py +209 -0
  292. parrot/memory/agent.py +32 -0
  293. parrot/memory/cache.py +175 -0
  294. parrot/memory/core.py +555 -0
  295. parrot/memory/file.py +153 -0
  296. parrot/memory/mem.py +131 -0
  297. parrot/memory/redis.py +613 -0
  298. parrot/models/__init__.py +46 -0
  299. parrot/models/basic.py +118 -0
  300. parrot/models/compliance.py +208 -0
  301. parrot/models/crew.py +395 -0
  302. parrot/models/detections.py +654 -0
  303. parrot/models/generation.py +85 -0
  304. parrot/models/google.py +223 -0
  305. parrot/models/groq.py +23 -0
  306. parrot/models/openai.py +30 -0
  307. parrot/models/outputs.py +285 -0
  308. parrot/models/responses.py +938 -0
  309. parrot/notifications/__init__.py +743 -0
  310. parrot/openapi/__init__.py +3 -0
  311. parrot/openapi/components.yaml +641 -0
  312. parrot/openapi/config.py +322 -0
  313. parrot/outputs/__init__.py +32 -0
  314. parrot/outputs/formats/__init__.py +108 -0
  315. parrot/outputs/formats/altair.py +359 -0
  316. parrot/outputs/formats/application.py +122 -0
  317. parrot/outputs/formats/base.py +351 -0
  318. parrot/outputs/formats/bokeh.py +356 -0
  319. parrot/outputs/formats/card.py +424 -0
  320. parrot/outputs/formats/chart.py +436 -0
  321. parrot/outputs/formats/d3.py +255 -0
  322. parrot/outputs/formats/echarts.py +310 -0
  323. parrot/outputs/formats/generators/__init__.py +0 -0
  324. parrot/outputs/formats/generators/abstract.py +61 -0
  325. parrot/outputs/formats/generators/panel.py +145 -0
  326. parrot/outputs/formats/generators/streamlit.py +86 -0
  327. parrot/outputs/formats/generators/terminal.py +63 -0
  328. parrot/outputs/formats/holoviews.py +310 -0
  329. parrot/outputs/formats/html.py +147 -0
  330. parrot/outputs/formats/jinja2.py +46 -0
  331. parrot/outputs/formats/json.py +87 -0
  332. parrot/outputs/formats/map.py +933 -0
  333. parrot/outputs/formats/markdown.py +172 -0
  334. parrot/outputs/formats/matplotlib.py +237 -0
  335. parrot/outputs/formats/mixins/__init__.py +0 -0
  336. parrot/outputs/formats/mixins/emaps.py +855 -0
  337. parrot/outputs/formats/plotly.py +341 -0
  338. parrot/outputs/formats/seaborn.py +310 -0
  339. parrot/outputs/formats/table.py +397 -0
  340. parrot/outputs/formats/template_report.py +138 -0
  341. parrot/outputs/formats/yaml.py +125 -0
  342. parrot/outputs/formatter.py +152 -0
  343. parrot/outputs/templates/__init__.py +95 -0
  344. parrot/pipelines/__init__.py +0 -0
  345. parrot/pipelines/abstract.py +210 -0
  346. parrot/pipelines/detector.py +124 -0
  347. parrot/pipelines/models.py +90 -0
  348. parrot/pipelines/planogram.py +3002 -0
  349. parrot/pipelines/table.sql +97 -0
  350. parrot/plugins/__init__.py +106 -0
  351. parrot/plugins/importer.py +80 -0
  352. parrot/py.typed +0 -0
  353. parrot/registry/__init__.py +18 -0
  354. parrot/registry/registry.py +594 -0
  355. parrot/scheduler/__init__.py +1189 -0
  356. parrot/scheduler/models.py +60 -0
  357. parrot/security/__init__.py +16 -0
  358. parrot/security/prompt_injection.py +268 -0
  359. parrot/security/security_events.sql +25 -0
  360. parrot/services/__init__.py +1 -0
  361. parrot/services/mcp/__init__.py +8 -0
  362. parrot/services/mcp/config.py +13 -0
  363. parrot/services/mcp/server.py +295 -0
  364. parrot/services/o365_remote_auth.py +235 -0
  365. parrot/stores/__init__.py +7 -0
  366. parrot/stores/abstract.py +352 -0
  367. parrot/stores/arango.py +1090 -0
  368. parrot/stores/bigquery.py +1377 -0
  369. parrot/stores/cache.py +106 -0
  370. parrot/stores/empty.py +10 -0
  371. parrot/stores/faiss_store.py +1157 -0
  372. parrot/stores/kb/__init__.py +9 -0
  373. parrot/stores/kb/abstract.py +68 -0
  374. parrot/stores/kb/cache.py +165 -0
  375. parrot/stores/kb/doc.py +325 -0
  376. parrot/stores/kb/hierarchy.py +346 -0
  377. parrot/stores/kb/local.py +457 -0
  378. parrot/stores/kb/prompt.py +28 -0
  379. parrot/stores/kb/redis.py +659 -0
  380. parrot/stores/kb/store.py +115 -0
  381. parrot/stores/kb/user.py +374 -0
  382. parrot/stores/models.py +59 -0
  383. parrot/stores/pgvector.py +3 -0
  384. parrot/stores/postgres.py +2853 -0
  385. parrot/stores/utils/__init__.py +0 -0
  386. parrot/stores/utils/chunking.py +197 -0
  387. parrot/telemetry/__init__.py +3 -0
  388. parrot/telemetry/mixin.py +111 -0
  389. parrot/template/__init__.py +3 -0
  390. parrot/template/engine.py +259 -0
  391. parrot/tools/__init__.py +23 -0
  392. parrot/tools/abstract.py +644 -0
  393. parrot/tools/agent.py +363 -0
  394. parrot/tools/arangodbsearch.py +537 -0
  395. parrot/tools/arxiv_tool.py +188 -0
  396. parrot/tools/calculator/__init__.py +3 -0
  397. parrot/tools/calculator/operations/__init__.py +38 -0
  398. parrot/tools/calculator/operations/calculus.py +80 -0
  399. parrot/tools/calculator/operations/statistics.py +76 -0
  400. parrot/tools/calculator/tool.py +150 -0
  401. parrot/tools/cloudwatch.py +988 -0
  402. parrot/tools/codeinterpreter/__init__.py +127 -0
  403. parrot/tools/codeinterpreter/executor.py +371 -0
  404. parrot/tools/codeinterpreter/internals.py +473 -0
  405. parrot/tools/codeinterpreter/models.py +643 -0
  406. parrot/tools/codeinterpreter/prompts.py +224 -0
  407. parrot/tools/codeinterpreter/tool.py +664 -0
  408. parrot/tools/company_info/__init__.py +6 -0
  409. parrot/tools/company_info/tool.py +1138 -0
  410. parrot/tools/correlationanalysis.py +437 -0
  411. parrot/tools/database/abstract.py +286 -0
  412. parrot/tools/database/bq.py +115 -0
  413. parrot/tools/database/cache.py +284 -0
  414. parrot/tools/database/models.py +95 -0
  415. parrot/tools/database/pg.py +343 -0
  416. parrot/tools/databasequery.py +1159 -0
  417. parrot/tools/db.py +1800 -0
  418. parrot/tools/ddgo.py +370 -0
  419. parrot/tools/decorators.py +271 -0
  420. parrot/tools/dftohtml.py +282 -0
  421. parrot/tools/document.py +549 -0
  422. parrot/tools/ecs.py +819 -0
  423. parrot/tools/edareport.py +368 -0
  424. parrot/tools/elasticsearch.py +1049 -0
  425. parrot/tools/employees.py +462 -0
  426. parrot/tools/epson/__init__.py +96 -0
  427. parrot/tools/excel.py +683 -0
  428. parrot/tools/file/__init__.py +13 -0
  429. parrot/tools/file/abstract.py +76 -0
  430. parrot/tools/file/gcs.py +378 -0
  431. parrot/tools/file/local.py +284 -0
  432. parrot/tools/file/s3.py +511 -0
  433. parrot/tools/file/tmp.py +309 -0
  434. parrot/tools/file/tool.py +501 -0
  435. parrot/tools/file_reader.py +129 -0
  436. parrot/tools/flowtask/__init__.py +19 -0
  437. parrot/tools/flowtask/tool.py +761 -0
  438. parrot/tools/gittoolkit.py +508 -0
  439. parrot/tools/google/__init__.py +18 -0
  440. parrot/tools/google/base.py +169 -0
  441. parrot/tools/google/tools.py +1251 -0
  442. parrot/tools/googlelocation.py +5 -0
  443. parrot/tools/googleroutes.py +5 -0
  444. parrot/tools/googlesearch.py +5 -0
  445. parrot/tools/googlesitesearch.py +5 -0
  446. parrot/tools/googlevoice.py +2 -0
  447. parrot/tools/gvoice.py +695 -0
  448. parrot/tools/ibisworld/README.md +225 -0
  449. parrot/tools/ibisworld/__init__.py +11 -0
  450. parrot/tools/ibisworld/tool.py +366 -0
  451. parrot/tools/jiratoolkit.py +1718 -0
  452. parrot/tools/manager.py +1098 -0
  453. parrot/tools/math.py +152 -0
  454. parrot/tools/metadata.py +476 -0
  455. parrot/tools/msteams.py +1621 -0
  456. parrot/tools/msword.py +635 -0
  457. parrot/tools/multidb.py +580 -0
  458. parrot/tools/multistoresearch.py +369 -0
  459. parrot/tools/networkninja.py +167 -0
  460. parrot/tools/nextstop/__init__.py +4 -0
  461. parrot/tools/nextstop/base.py +286 -0
  462. parrot/tools/nextstop/employee.py +733 -0
  463. parrot/tools/nextstop/store.py +462 -0
  464. parrot/tools/notification.py +435 -0
  465. parrot/tools/o365/__init__.py +42 -0
  466. parrot/tools/o365/base.py +295 -0
  467. parrot/tools/o365/bundle.py +522 -0
  468. parrot/tools/o365/events.py +554 -0
  469. parrot/tools/o365/mail.py +992 -0
  470. parrot/tools/o365/onedrive.py +497 -0
  471. parrot/tools/o365/sharepoint.py +641 -0
  472. parrot/tools/openapi_toolkit.py +904 -0
  473. parrot/tools/openweather.py +527 -0
  474. parrot/tools/pdfprint.py +1001 -0
  475. parrot/tools/powerbi.py +518 -0
  476. parrot/tools/powerpoint.py +1113 -0
  477. parrot/tools/pricestool.py +146 -0
  478. parrot/tools/products/__init__.py +246 -0
  479. parrot/tools/prophet_tool.py +171 -0
  480. parrot/tools/pythonpandas.py +630 -0
  481. parrot/tools/pythonrepl.py +910 -0
  482. parrot/tools/qsource.py +436 -0
  483. parrot/tools/querytoolkit.py +395 -0
  484. parrot/tools/quickeda.py +827 -0
  485. parrot/tools/resttool.py +553 -0
  486. parrot/tools/retail/__init__.py +0 -0
  487. parrot/tools/retail/bby.py +528 -0
  488. parrot/tools/sandboxtool.py +703 -0
  489. parrot/tools/sassie/__init__.py +352 -0
  490. parrot/tools/scraping/__init__.py +7 -0
  491. parrot/tools/scraping/docs/select.md +466 -0
  492. parrot/tools/scraping/documentation.md +1278 -0
  493. parrot/tools/scraping/driver.py +436 -0
  494. parrot/tools/scraping/models.py +576 -0
  495. parrot/tools/scraping/options.py +85 -0
  496. parrot/tools/scraping/orchestrator.py +517 -0
  497. parrot/tools/scraping/readme.md +740 -0
  498. parrot/tools/scraping/tool.py +3115 -0
  499. parrot/tools/seasonaldetection.py +642 -0
  500. parrot/tools/shell_tool/__init__.py +5 -0
  501. parrot/tools/shell_tool/actions.py +408 -0
  502. parrot/tools/shell_tool/engine.py +155 -0
  503. parrot/tools/shell_tool/models.py +322 -0
  504. parrot/tools/shell_tool/tool.py +442 -0
  505. parrot/tools/site_search.py +214 -0
  506. parrot/tools/textfile.py +418 -0
  507. parrot/tools/think.py +378 -0
  508. parrot/tools/toolkit.py +298 -0
  509. parrot/tools/webapp_tool.py +187 -0
  510. parrot/tools/whatif.py +1279 -0
  511. parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
  512. parrot/tools/workday/__init__.py +6 -0
  513. parrot/tools/workday/models.py +1389 -0
  514. parrot/tools/workday/tool.py +1293 -0
  515. parrot/tools/yfinance_tool.py +306 -0
  516. parrot/tools/zipcode.py +217 -0
  517. parrot/utils/__init__.py +2 -0
  518. parrot/utils/helpers.py +73 -0
  519. parrot/utils/parsers/__init__.py +5 -0
  520. parrot/utils/parsers/toml.c +12078 -0
  521. parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
  522. parrot/utils/parsers/toml.pyx +21 -0
  523. parrot/utils/toml.py +11 -0
  524. parrot/utils/types.cpp +20936 -0
  525. parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
  526. parrot/utils/types.pyx +213 -0
  527. parrot/utils/uv.py +11 -0
  528. parrot/version.py +10 -0
  529. parrot/yaml-rs/Cargo.lock +350 -0
  530. parrot/yaml-rs/Cargo.toml +19 -0
  531. parrot/yaml-rs/pyproject.toml +19 -0
  532. parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
  533. parrot/yaml-rs/src/lib.rs +222 -0
  534. requirements/docker-compose.yml +24 -0
  535. requirements/requirements-dev.txt +21 -0
@@ -0,0 +1,1082 @@
1
+ """
2
+ QUIC/HTTP3 MCP Server Implementation
3
+ ====================================
4
+
5
+ High-performance MCP server using QUIC/HTTP3 with WebTransport support.
6
+ Provides ultra-low latency for distributed MCP deployments.
7
+
8
+ Features:
9
+ - 0-RTT connection establishment
10
+ - Multiplexed streams without head-of-line blocking
11
+ - Binary serialization (MessagePack) for efficiency
12
+ - Unreliable datagrams for telemetry
13
+ - Connection migration for mobile agents
14
+
15
+ Requires:
16
+ pip install aioquic msgpack --break-system-packages
17
+
18
+ Usage:
19
+ server = QuicMCPServer(config)
20
+ server.register_tool(MyTool())
21
+ await server.start()
22
+
23
+ # For development, create the certificate:
24
+ # openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
25
+ """
26
+
27
+ from __future__ import annotations
28
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
29
+ import asyncio
30
+ import json
31
+ import logging
32
+ import os
33
+ import ssl
34
+ from dataclasses import dataclass, field
35
+ from enum import Enum
36
+ from pathlib import Path
37
+ # Core QUIC library
38
+ from aioquic.asyncio import (
39
+ QuicConnectionProtocol,
40
+ connect as quic_connect,
41
+ serve as quic_serve
42
+ )
43
+ from aioquic.asyncio.server import QuicServer
44
+ from aioquic.h3.connection import H3_ALPN, H3Connection
45
+ from aioquic.h3.events import (
46
+ DataReceived,
47
+ H3Event,
48
+ HeadersReceived,
49
+ WebTransportStreamDataReceived,
50
+ )
51
+ from aioquic.quic.configuration import QuicConfiguration
52
+ from aioquic.quic.events import (
53
+ ConnectionTerminated,
54
+ DatagramFrameReceived,
55
+ ProtocolNegotiated,
56
+ QuicEvent,
57
+ StreamDataReceived,
58
+ )
59
+ from aioquic.tls import SessionTicket
60
+ from .base import MCPServerBase
61
+ from ..config import MCPServerConfig
62
+
63
+ # Optional: faster serialization
64
+ try:
65
+ import msgpack
66
+ HAS_MSGPACK = True
67
+ except ImportError:
68
+ HAS_MSGPACK = False
69
+
70
+ try:
71
+ from google.protobuf import json_format
72
+ HAS_PROTOBUF = True
73
+ except ImportError:
74
+ HAS_PROTOBUF = False
75
+
76
+
77
+ class SerializationFormat(Enum):
78
+ """Supported serialization formats for MCP messages."""
79
+ JSON = "json" # Standard, compatible
80
+ MSGPACK = "msgpack" # ~2-3x faster, smaller
81
+ PROTOBUF = "protobuf" # Fastest, smallest, needs schema
82
+
83
+ class MCPConnectionError(Exception):
84
+ """MCP connection error."""
85
+ pass
86
+
87
+ @dataclass
88
+ class QuicMCPConfig:
89
+ """Unified QUIC configuration."""
90
+ # Connection
91
+ host: str = "localhost"
92
+ port: int = 4433
93
+
94
+ # TLS (required for QUIC)
95
+ cert_path: str = "cert.pem"
96
+ key_path: str = "key.pem"
97
+ ca_cert_path: Optional[str] = None
98
+ verify_mode: ssl.VerifyMode = ssl.CERT_REQUIRED
99
+ insecure: bool = False # For development only
100
+
101
+ # Performance
102
+ max_datagram_size: int = 65536
103
+ idle_timeout: float = 60.0
104
+ max_streams_bidi: int = 128
105
+ max_streams_uni: int = 128
106
+
107
+ # Serialization
108
+ serialization: SerializationFormat = SerializationFormat.MSGPACK
109
+
110
+ # 0-RTT
111
+ enable_0rtt: bool = True
112
+ session_ticket_path: Optional[str] = None
113
+
114
+ # WebTransport
115
+ enable_webtransport: bool = True
116
+ webtransport_path: str = "/mcp"
117
+
118
+ # Advanced
119
+ congestion_control: str = "cubic"
120
+ quic_log_dir: Optional[str] = None
121
+
122
+
123
+ class MCPSerializer:
124
+ """Handles serialization/deserialization of MCP messages."""
125
+
126
+ def __init__(self, format: SerializationFormat = SerializationFormat.MSGPACK):
127
+ self.format = format
128
+
129
+ if format == SerializationFormat.MSGPACK and not HAS_MSGPACK:
130
+ logging.warning("msgpack not available, falling back to JSON")
131
+ self.format = SerializationFormat.JSON
132
+
133
+ def serialize(self, message: Dict[str, Any]) -> bytes:
134
+ """Serialize MCP message to bytes."""
135
+ if self.format == SerializationFormat.MSGPACK:
136
+ return msgpack.packb(message, use_bin_type=True)
137
+ elif self.format == SerializationFormat.PROTOBUF:
138
+ # Would need generated protobuf classes
139
+ raise NotImplementedError("Protobuf serialization requires schema")
140
+ else:
141
+ return json.dumps(message).encode('utf-8')
142
+
143
+ def deserialize(self, data: bytes) -> Dict[str, Any]:
144
+ """Deserialize bytes to MCP message."""
145
+ if self.format == SerializationFormat.MSGPACK:
146
+ return msgpack.unpackb(data, raw=False)
147
+ elif self.format == SerializationFormat.PROTOBUF:
148
+ # Would need generated protobuf classes
149
+ raise NotImplementedError("Protobuf serialization requires schema")
150
+ else:
151
+ return json.loads(data.decode('utf-8'))
152
+
153
+ @property
154
+ def content_type(self) -> str:
155
+ """MIME type for the serialization format."""
156
+ return {
157
+ SerializationFormat.JSON: "application/json",
158
+ SerializationFormat.MSGPACK: "application/msgpack",
159
+ SerializationFormat.PROTOBUF: "application/x-protobuf",
160
+ }[self.format]
161
+
162
+
163
+ class QuicMCPClientProtocol(QuicConnectionProtocol):
164
+ """QUIC protocol handler for MCP client connections."""
165
+
166
+ def __init__(self, *args, **kwargs):
167
+ super().__init__(*args, **kwargs)
168
+ self._http: Optional[H3Connection] = None
169
+ self._request_id = 0
170
+ self._pending_requests: Dict[int, asyncio.Future] = {}
171
+ self._stream_queues: Dict[int, asyncio.Queue] = {}
172
+ self._session_id: Optional[int] = None
173
+ self._initialized = False
174
+ self.logger = logging.getLogger("QuicMCPClient")
175
+ self.serializer: Optional[MCPSerializer] = None
176
+
177
+ def quic_event_received(self, event: QuicEvent):
178
+ """Handle QUIC-level events."""
179
+ if isinstance(event, ConnectionTerminated):
180
+ self.logger.warning(f"Connection terminated: {event.reason_phrase}")
181
+ # Cancel all pending requests
182
+ for future in self._pending_requests.values():
183
+ if not future.done():
184
+ future.set_exception(
185
+ ConnectionError(f"Connection lost: {event.reason_phrase}")
186
+ )
187
+ return
188
+
189
+ if isinstance(event, DatagramFrameReceived):
190
+ # Handle unreliable datagrams (telemetry, heartbeats)
191
+ self._handle_datagram(event.data)
192
+ return
193
+
194
+ # Pass to HTTP/3 layer
195
+ if self._http is not None:
196
+ for h3_event in self._http.handle_event(event):
197
+ self._handle_h3_event(h3_event)
198
+
199
+ def _handle_h3_event(self, event: H3Event):
200
+ """Handle HTTP/3 level events."""
201
+ if isinstance(event, HeadersReceived):
202
+ # Response headers received
203
+ stream_id = event.stream_id
204
+ if stream_id not in self._stream_queues:
205
+ self._stream_queues[stream_id] = asyncio.Queue()
206
+
207
+ elif isinstance(event, DataReceived):
208
+ # Response data received
209
+ stream_id = event.stream_id
210
+ if stream_id in self._stream_queues:
211
+ asyncio.get_event_loop().call_soon(
212
+ self._stream_queues[stream_id].put_nowait,
213
+ event.data
214
+ )
215
+ if event.stream_ended:
216
+ asyncio.get_event_loop().call_soon(
217
+ self._stream_queues[stream_id].put_nowait,
218
+ None # Signal end of stream
219
+ )
220
+
221
+ elif isinstance(event, WebTransportStreamDataReceived):
222
+ # WebTransport bidirectional stream data
223
+ self._handle_webtransport_data(event)
224
+
225
+ def _handle_webtransport_data(self, event: WebTransportStreamDataReceived):
226
+ """Handle WebTransport stream data."""
227
+ try:
228
+ message = self.serializer.deserialize(event.data)
229
+
230
+ # Match response to request
231
+ msg_id = message.get("id")
232
+ if msg_id is not None and msg_id in self._pending_requests:
233
+ future = self._pending_requests.pop(msg_id)
234
+ if not future.done():
235
+ future.set_result(message)
236
+ else:
237
+ # Server-initiated message (notification)
238
+ self._handle_notification(message)
239
+
240
+ except Exception as e:
241
+ self.logger.error(f"Failed to handle WebTransport data: {e}")
242
+
243
+ def _handle_notification(self, message: Dict[str, Any]):
244
+ """Handle server-initiated notifications."""
245
+ method = message.get("method", "")
246
+ self.logger.debug(f"Received notification: {method}")
247
+ # Could emit to event handlers here
248
+
249
+ def _handle_datagram(self, data: bytes):
250
+ """Handle unreliable datagram (telemetry, etc.)."""
251
+ try:
252
+ # Datagrams use a simple format: 1 byte type + payload
253
+ msg_type = data[0]
254
+ payload = data[1:]
255
+
256
+ if msg_type == 0x01: # Heartbeat
257
+ self.logger.debug("Received heartbeat")
258
+ elif msg_type == 0x02: # Telemetry
259
+ self.logger.debug(f"Received telemetry: {len(payload)} bytes")
260
+ # Add more types as needed
261
+
262
+ except Exception as e:
263
+ self.logger.debug(f"Failed to handle datagram: {e}")
264
+
265
+
266
+ class QuicMCPServerProtocol(QuicConnectionProtocol):
267
+ """
268
+ QUIC protocol handler for MCP server connections.
269
+
270
+ Handles:
271
+ - HTTP/3 requests
272
+ - WebTransport sessions
273
+ - Unreliable datagrams for telemetry
274
+ """
275
+
276
+ def __init__(self, *args, mcp_handler: 'QuicMCPServer', **kwargs):
277
+ super().__init__(*args, **kwargs)
278
+ self.mcp_handler = mcp_handler
279
+ self._http: Optional[H3Connection] = None
280
+ self.serializer = mcp_handler.serializer
281
+ self.logger = logging.getLogger("QuicMCPServerProtocol")
282
+
283
+ # WebTransport session tracking
284
+ self._webtransport_sessions: Dict[int, Dict[str, Any]] = {}
285
+
286
+ # Buffer for incomplete messages
287
+ self._stream_buffers: Dict[int, bytes] = {}
288
+
289
+ def quic_event_received(self, event: QuicEvent) -> None:
290
+ """Handle QUIC-level events."""
291
+
292
+ if isinstance(event, ProtocolNegotiated):
293
+ # Protocol negotiated, setup HTTP/3
294
+ if event.alpn_protocol in H3_ALPN:
295
+ self._http = H3Connection(
296
+ self._quic,
297
+ enable_webtransport=self.mcp_handler.quic_config.enable_webtransport
298
+ )
299
+ return
300
+
301
+ if isinstance(event, ConnectionTerminated):
302
+ self.logger.info(
303
+ f"Connection terminated: {event.error_code} - {event.reason_phrase}"
304
+ )
305
+ # Cleanup WebTransport sessions
306
+ self._webtransport_sessions.clear()
307
+ return
308
+
309
+ if isinstance(event, DatagramFrameReceived):
310
+ # Handle unreliable datagrams (telemetry, heartbeats)
311
+ asyncio.create_task(self._handle_datagram(event.data))
312
+ return
313
+
314
+ # Pass to HTTP/3 layer
315
+ if self._http is not None:
316
+ for h3_event in self._http.handle_event(event):
317
+ asyncio.create_task(self._handle_h3_event(h3_event))
318
+
319
+ async def _handle_h3_event(self, event: H3Event) -> None:
320
+ """Handle HTTP/3 level events."""
321
+
322
+ if isinstance(event, HeadersReceived):
323
+ await self._handle_headers(event)
324
+
325
+ elif isinstance(event, DataReceived):
326
+ await self._handle_data(event)
327
+
328
+ elif isinstance(event, WebTransportStreamDataReceived):
329
+ await self._handle_webtransport_data(event)
330
+
331
+ async def _handle_headers(self, event: HeadersReceived) -> None:
332
+ """Handle incoming request headers."""
333
+ headers = dict(event.headers)
334
+ method = headers.get(b":method", b"").decode()
335
+ path = headers.get(b":path", b"/").decode()
336
+
337
+ self.logger.debug(f"Headers received: {method} {path}")
338
+
339
+ # Check for WebTransport CONNECT
340
+ if method == "CONNECT" and headers.get(b":protocol") == b"webtransport":
341
+ await self._accept_webtransport(event.stream_id, path)
342
+ return
343
+
344
+ # Regular HTTP/3 POST for MCP
345
+ if method == "POST" and path == self.mcp_handler.quic_config.webtransport_path:
346
+ # Data will come in DataReceived event
347
+ self._stream_buffers[event.stream_id] = b""
348
+
349
+ async def _accept_webtransport(self, stream_id: int, path: str) -> None:
350
+ """Accept a WebTransport session."""
351
+ self._webtransport_sessions[stream_id] = {
352
+ "path": path,
353
+ "created_at": asyncio.get_event_loop().time(),
354
+ }
355
+
356
+ # Send success response
357
+ self._http.send_headers(
358
+ stream_id=stream_id,
359
+ headers=[
360
+ (b":status", b"200"),
361
+ (b"sec-webtransport-http3-draft", b"draft02"),
362
+ ],
363
+ )
364
+ self.transmit()
365
+
366
+ self.logger.info(f"WebTransport session accepted: {stream_id}")
367
+
368
+ async def _handle_data(self, event: DataReceived) -> None:
369
+ """Handle incoming HTTP/3 data."""
370
+ stream_id = event.stream_id
371
+
372
+ # Accumulate data
373
+ if stream_id in self._stream_buffers:
374
+ self._stream_buffers[stream_id] += event.data
375
+ else:
376
+ self._stream_buffers[stream_id] = event.data
377
+
378
+ # Process if stream ended
379
+ if event.stream_ended:
380
+ data = self._stream_buffers.pop(stream_id, b"")
381
+ if data:
382
+ await self._process_mcp_request(stream_id, data)
383
+
384
+ async def _handle_webtransport_data(self, event: WebTransportStreamDataReceived) -> None:
385
+ """Handle WebTransport stream data."""
386
+ await self._process_mcp_request(event.stream_id, event.data)
387
+
388
+ async def _process_mcp_request(self, stream_id: int, data: bytes) -> None:
389
+ """Process an MCP JSON-RPC request."""
390
+ try:
391
+ # Deserialize request
392
+ request = self.serializer.deserialize(data)
393
+
394
+ method = request.get("method", "")
395
+ params = request.get("params", {})
396
+ request_id = request.get("id")
397
+
398
+ self.logger.info(f"MCP request: {method} (id={request_id})")
399
+
400
+ # Dispatch to handler
401
+ try:
402
+ if method == "initialize":
403
+ result = await self.mcp_handler.handle_initialize(params)
404
+ elif method == "tools/list":
405
+ result = await self.mcp_handler.handle_tools_list(params)
406
+ elif method == "tools/call":
407
+ result = await self.mcp_handler.handle_tools_call(params)
408
+ elif method == "notifications/initialized":
409
+ # Notification - no response needed
410
+ self.logger.info("Client initialization complete")
411
+ return
412
+ else:
413
+ raise RuntimeError(f"Unknown method: {method}")
414
+
415
+ response = {
416
+ "jsonrpc": "2.0",
417
+ "id": request_id,
418
+ "result": result
419
+ }
420
+
421
+ except Exception as e:
422
+ self.logger.error(f"Error handling {method}: {e}")
423
+ response = {
424
+ "jsonrpc": "2.0",
425
+ "id": request_id,
426
+ "error": {
427
+ "code": -32603,
428
+ "message": str(e)
429
+ }
430
+ }
431
+
432
+ # Send response
433
+ response_data = self.serializer.serialize(response)
434
+ self._http.send_data(
435
+ stream_id=stream_id,
436
+ data=response_data,
437
+ end_stream=True
438
+ )
439
+ self.transmit()
440
+
441
+ except Exception as e:
442
+ self.logger.error(f"Failed to process MCP request: {e}")
443
+ error_response = {
444
+ "jsonrpc": "2.0",
445
+ "id": None,
446
+ "error": {
447
+ "code": -32700,
448
+ "message": f"Parse error: {e}"
449
+ }
450
+ }
451
+ self._http.send_data(
452
+ stream_id=stream_id,
453
+ data=self.serializer.serialize(error_response),
454
+ end_stream=True
455
+ )
456
+ self.transmit()
457
+
458
+ async def _handle_datagram(self, data: bytes) -> None:
459
+ """Handle unreliable datagram (telemetry, heartbeats)."""
460
+ if not data:
461
+ return
462
+
463
+ try:
464
+ msg_type = data[0]
465
+ payload = data[1:]
466
+
467
+ if msg_type == 0x01: # Heartbeat
468
+ self.logger.debug("Received heartbeat datagram")
469
+ # Send heartbeat response
470
+ self._quic.send_datagram_frame(bytes([0x01]))
471
+ self.transmit()
472
+
473
+ elif msg_type == 0x02: # Telemetry
474
+ self.logger.debug(f"Received telemetry: {len(payload)} bytes")
475
+ # Could forward to metrics system
476
+
477
+ elif msg_type == 0x03: # Ping
478
+ # Send pong
479
+ self._quic.send_datagram_frame(bytes([0x04]) + payload)
480
+ self.transmit()
481
+
482
+ except Exception as e:
483
+ self.logger.debug(f"Failed to handle datagram: {e}")
484
+
485
+
486
+ class QuicMCPServer(MCPServerBase):
487
+ """
488
+ QUIC/HTTP3 MCP Server with WebTransport support.
489
+
490
+ Inherits behavior from MCPServerBase and adds QUIC transport layer.
491
+
492
+ Example:
493
+ >>> from parrot.mcp.server import MCPServerConfig
494
+ >>>
495
+ >>> config = MCPServerConfig(
496
+ ... name="high-perf-mcp",
497
+ ... transport="quic",
498
+ ... host="0.0.0.0",
499
+ ... port=4433,
500
+ ... )
501
+ >>>
502
+ >>> server = QuicMCPServer(config)
503
+ >>> server.register_tool(MySearchTool())
504
+ >>> server.register_tool(MyDatabaseTool())
505
+ >>>
506
+ >>> await server.start()
507
+ """
508
+
509
+ def __init__(
510
+ self,
511
+ config: MCPServerConfig,
512
+ quic_config: Optional[QuicMCPConfig] = None,
513
+ ):
514
+ super().__init__(config)
515
+ # QUIC-specific config
516
+ self.quic_config = quic_config or QuicMCPConfig()
517
+ # Serializer
518
+ self.serializer = MCPSerializer(self.quic_config.serialization)
519
+ # Server state
520
+ self._server: Optional[QuicServer] = None
521
+ self._running = False
522
+ # Session ticket store for 0-RTT
523
+ self._session_tickets: Dict[bytes, SessionTicket] = {}
524
+ # Connected clients tracking
525
+ self._connected_clients: Set[str] = set()
526
+
527
+ async def handle_tools_call(self, params: Dict[str, Any]) -> Dict[str, Any]:
528
+ """Handle tools/call request."""
529
+ tool_name = params.get("name")
530
+ arguments = params.get("arguments", {})
531
+
532
+ self.logger.info(f"Calling tool: {tool_name}")
533
+
534
+ if tool_name not in self.tools:
535
+ raise RuntimeError(f"Tool not found: {tool_name}")
536
+
537
+ tool = self.tools[tool_name]
538
+
539
+ try:
540
+ # Execute tool
541
+ if hasattr(tool, '_execute'):
542
+ result = await tool._execute(**arguments)
543
+ elif hasattr(tool, 'execute'):
544
+ result = await tool.execute(**arguments)
545
+ else:
546
+ result = await tool(**arguments)
547
+
548
+ return {
549
+ "content": [{"type": "text", "text": str(result)}],
550
+ "isError": False
551
+ }
552
+
553
+ except Exception as e:
554
+ self.logger.error(f"Tool execution failed: {e}")
555
+ return {
556
+ "content": [{"type": "text", "text": f"Error: {e}"}],
557
+ "isError": True
558
+ }
559
+
560
+ # =========================================================================
561
+ # QUIC Server Lifecycle (abstract method implementation)
562
+ # =========================================================================
563
+
564
+ async def start(self) -> None:
565
+ """Start the QUIC MCP server."""
566
+ self.logger.info(
567
+ f"Starting QUIC MCP server on {self.config.host}:{self.config.port}"
568
+ )
569
+
570
+ # Build QUIC configuration
571
+ configuration = QuicConfiguration(
572
+ alpn_protocols=H3_ALPN,
573
+ is_client=False,
574
+ max_datagram_frame_size=self.quic_config.max_datagram_size,
575
+ idle_timeout=self.quic_config.idle_timeout,
576
+ congestion_control_algorithm=self.quic_config.congestion_control,
577
+ )
578
+
579
+ # Load TLS certificates
580
+ cert_path = Path(self.quic_config.cert_path)
581
+ key_path = Path(self.quic_config.key_path)
582
+
583
+ if not cert_path.exists():
584
+ raise FileNotFoundError(
585
+ f"TLS certificate not found: {cert_path}\n"
586
+ f"Generate with: openssl req -x509 -newkey rsa:4096 "
587
+ f"-keyout {key_path} -out {cert_path} -days 365 -nodes"
588
+ )
589
+
590
+ configuration.load_cert_chain(str(cert_path), str(key_path))
591
+
592
+ # Enable 0-RTT if configured
593
+ if self.quic_config.enable_0rtt:
594
+ configuration.max_early_data_size = 0xFFFF
595
+
596
+ # Session ticket handler for 0-RTT resumption
597
+ def session_ticket_handler(ticket: SessionTicket) -> None:
598
+ self._session_tickets[ticket.ticket] = ticket
599
+ self.logger.debug("Session ticket stored for 0-RTT resumption")
600
+
601
+ def session_ticket_fetcher(ticket: bytes) -> Optional[SessionTicket]:
602
+ return self._session_tickets.get(ticket)
603
+
604
+ # Create protocol factory
605
+ def create_protocol(*args, **kwargs):
606
+ return QuicMCPServerProtocol(
607
+ *args,
608
+ mcp_handler=self,
609
+ **kwargs
610
+ )
611
+
612
+ # Start server
613
+ self._server = await quic_serve(
614
+ host=self.config.host,
615
+ port=self.config.port,
616
+ configuration=configuration,
617
+ create_protocol=create_protocol,
618
+ session_ticket_fetcher=session_ticket_fetcher,
619
+ session_ticket_handler=session_ticket_handler,
620
+ )
621
+
622
+ self._running = True
623
+
624
+ self.logger.info(
625
+ f"QUIC MCP server started at https://{self.config.host}:{self.config.port}"
626
+ )
627
+ self.logger.info(f"Transport: QUIC/HTTP3 with WebTransport")
628
+ self.logger.info(f"Serialization: {self.quic_config.serialization.value}")
629
+ self.logger.info(f"0-RTT enabled: {self.quic_config.enable_0rtt}")
630
+ self.logger.info(f"Registered tools: {list(self.tools.keys())}")
631
+
632
+ # Keep server running
633
+ try:
634
+ while self._running:
635
+ await asyncio.sleep(1)
636
+ except asyncio.CancelledError:
637
+ pass
638
+
639
+ async def stop(self) -> None:
640
+ """Stop the QUIC MCP server."""
641
+ self._running = False
642
+
643
+ if self._server:
644
+ self._server.close()
645
+ self._server = None
646
+
647
+ self.logger.info("QUIC MCP server stopped")
648
+
649
+ # =========================================================================
650
+ # Utility Methods
651
+ # =========================================================================
652
+
653
+ def broadcast_datagram(self, data: bytes) -> None:
654
+ """Broadcast unreliable datagram to all connected clients."""
655
+ # This would iterate over active connections and send datagrams
656
+ pass
657
+
658
+ @property
659
+ def is_running(self) -> bool:
660
+ return self._running
661
+
662
+ # =============================================================================
663
+ # Client Session
664
+ # =============================================================================
665
+
666
+ class QuicMCPSession:
667
+ """
668
+ MCP session over QUIC/HTTP3 with WebTransport.
669
+
670
+ Features:
671
+ - 0-RTT connection for minimal latency
672
+ - Multiplexed streams for concurrent tool calls
673
+ - Binary serialization (MessagePack) for efficiency
674
+ - Unreliable datagrams for telemetry
675
+ - Connection migration support
676
+
677
+ Example:
678
+ >>> config = QuicMCPConfig(
679
+ ... host="tools.example.com",
680
+ ... port=4433,
681
+ ... serialization=SerializationFormat.MSGPACK,
682
+ ... )
683
+ >>> session = QuicMCPSession(mcp_config, logger)
684
+ >>> await session.connect()
685
+ >>> tools = await session.list_tools()
686
+ >>> result = await session.call_tool("search", {"query": "AI agents"})
687
+ """
688
+
689
+ def __init__(self, config: 'MCPServerConfig', logger: logging.Logger):
690
+ self.config = config
691
+ self.logger = logger
692
+
693
+ # Extract QUIC-specific config
694
+ self.quic_config = config.quic_config or QuicMCPConfig()
695
+
696
+ self._protocol: Optional[QuicMCPClientProtocol] = None
697
+ self._http: Optional[H3Connection] = None
698
+ self._request_id = 0
699
+ self._pending_requests: Dict[int, asyncio.Future] = {}
700
+ self._initialized = False
701
+ self._session_id: Optional[int] = None
702
+ self._webtransport_stream_id: Optional[int] = None
703
+
704
+ # Serializer
705
+ self.serializer = MCPSerializer(self.quic_config.serialization)
706
+
707
+ # Session ticket for 0-RTT
708
+ self._session_ticket: Optional[SessionTicket] = None
709
+ if self.quic_config.session_ticket_path:
710
+ self._load_session_ticket()
711
+
712
+ def _on_session_ticket(self, ticket: SessionTicket):
713
+ """Handle new session ticket from server."""
714
+ if self.quic_config.session_ticket_path:
715
+ try:
716
+ import pickle
717
+ path = Path(self.quic_config.session_ticket_path)
718
+ path.parent.mkdir(parents=True, exist_ok=True)
719
+ path.write_bytes(pickle.dumps(ticket))
720
+ except Exception as e:
721
+ self.logger.warning(f"Failed to save session ticket: {e}")
722
+
723
+ def _load_session_ticket(self) -> Optional[SessionTicket]:
724
+ """Load session ticket for 0-RTT."""
725
+ if self.quic_config.session_ticket_path:
726
+ path = Path(self.quic_config.session_ticket_path)
727
+ if path.exists():
728
+ try:
729
+ import pickle
730
+ return pickle.loads(path.read_bytes())
731
+ except Exception:
732
+ pass
733
+ return None
734
+
735
+ def _save_session_ticket(self, ticket: SessionTicket) -> None:
736
+ """Save session ticket for future 0-RTT connections."""
737
+ if self.quic_config.session_ticket_path:
738
+ try:
739
+ path = Path(self.quic_config.session_ticket_path)
740
+ path.parent.mkdir(parents=True, exist_ok=True)
741
+ path.write_bytes(pickle.dumps(ticket))
742
+ self.logger.info("Saved session ticket for 0-RTT")
743
+ except Exception as e:
744
+ self.logger.warning(f"Failed to save session ticket: {e}")
745
+
746
+ async def connect(self):
747
+ """Establish QUIC connection to MCP server."""
748
+ try:
749
+ # Build QUIC configuration
750
+ quic_configuration = QuicConfiguration(
751
+ alpn_protocols=H3_ALPN,
752
+ is_client=True,
753
+ max_datagram_frame_size=self.quic_config.max_datagram_size,
754
+ idle_timeout=self.quic_config.idle_timeout,
755
+ )
756
+
757
+ # TLS configuration
758
+ if self.quic_config.insecure:
759
+ quic_configuration.verify_mode = ssl.CERT_NONE
760
+ else:
761
+ quic_configuration.verify_mode = self.quic_config.verify_mode
762
+ if self.quic_config.ca_cert_path:
763
+ quic_configuration.cafile = self.quic_config.ca_cert_path
764
+
765
+ # Session ticket for 0-RTT
766
+ if self._session_ticket:
767
+ quic_configuration.session_ticket = self._session_ticket
768
+
769
+ # Connect
770
+ self.logger.info(
771
+ f"Connecting to {self.quic_config.host}:{self.quic_config.port} "
772
+ f"via QUIC/HTTP3..."
773
+ )
774
+
775
+ self._protocol = await quic_connect(
776
+ self.quic_config.host,
777
+ self.quic_config.port,
778
+ configuration=quic_configuration,
779
+ create_protocol=QuicMCPClientProtocol,
780
+ session_ticket_handler=self._on_session_ticket,
781
+ )
782
+ # now, configure the http/3
783
+ self._http = H3Connection(
784
+ self._protocol._quic,
785
+ enable_webtransport=self.quic_config.enable_webtransport
786
+ )
787
+ self._protocol._http = self._http
788
+ self._protocol.serializer = self.serializer
789
+
790
+ if self.quic_config.use_webtransport:
791
+ await self._establish_webtransport()
792
+
793
+ # Initialize MCP session
794
+ await self._initialize_session()
795
+ self._initialized = True
796
+
797
+ self.logger.info(
798
+ f"Connected to {self.config.name} via QUIC "
799
+ f"(serialization: {self.quic_config.serialization.value})"
800
+ )
801
+
802
+ except Exception as e:
803
+ await self.disconnect()
804
+ raise MCPConnectionError(f"QUIC connection failed: {e}") from e
805
+
806
+ async def _establish_webtransport(self):
807
+ """Establish WebTransport session over HTTP/3."""
808
+ # Send CONNECT request for WebTransport
809
+ stream_id = self._http._quic.get_next_available_stream_id()
810
+
811
+ headers = [
812
+ (b":method", b"CONNECT"),
813
+ (b":protocol", b"webtransport"),
814
+ (b":scheme", b"https"),
815
+ (b":authority", f"{self.quic_config.host}:{self.quic_config.port}".encode()),
816
+ (b":path", self.quic_config.webtransport_path.encode()),
817
+ (b"sec-webtransport-http3-draft", b"draft02"),
818
+ ]
819
+
820
+ self._http.send_headers(stream_id=stream_id, headers=headers)
821
+ self._protocol.transmit()
822
+ # Wait for response
823
+ response_future = asyncio.Future()
824
+ self._protocol._webtransport_futures[stream_id] = response_future
825
+
826
+ try:
827
+ status = await asyncio.wait_for(response_future, timeout=10.0)
828
+ if status != 200:
829
+ raise MCPConnectionError(f"WebTransport rejected: {status}")
830
+ self._session_id = stream_id
831
+ self.logger.debug(
832
+ f"WebTransport session established: {stream_id}"
833
+ )
834
+ except asyncio.TimeoutError:
835
+ raise MCPConnectionError("WebTransport handshake timeout")
836
+
837
+
838
+ async def _initialize_session(self):
839
+ """Initialize MCP session."""
840
+ response = await self.send_request("initialize", {
841
+ "protocolVersion": "2024-11-05",
842
+ "capabilities": {
843
+ "tools": {},
844
+ "experimental": {
845
+ "quicTransport": True,
846
+ "binarySerialization": self.quic_config.serialization.value,
847
+ }
848
+ },
849
+ "clientInfo": {
850
+ "name": "ai-parrot",
851
+ "version": "1.0.0",
852
+ "transport": "quic"
853
+ }
854
+ })
855
+
856
+ if "error" in response:
857
+ raise MCPConnectionError(f"Initialize failed: {response['error']}")
858
+
859
+ # Send initialized notification
860
+ await self.send_notification("notifications/initialized")
861
+
862
+ self.logger.info(f"MCP session initialized over QUIC")
863
+
864
+ async def send_request(
865
+ self,
866
+ method: str,
867
+ params: Optional[Dict] = None,
868
+ timeout: float = 30.0
869
+ ) -> Dict[str, Any]:
870
+ """Send JSON-RPC request over QUIC stream."""
871
+ self._request_id += 1
872
+
873
+ request = {
874
+ "jsonrpc": "2.0",
875
+ "id": self._request_id,
876
+ "method": method,
877
+ "params": params or {}
878
+ }
879
+
880
+ # Serialize
881
+ data = self.serializer.serialize(request)
882
+
883
+ # Create a new stream for this request (multiplexed!)
884
+ if self.quic_config.use_webtransport and self._session_id:
885
+ stream_id = self._http.create_webtransport_stream(
886
+ session_id=self._session_id,
887
+ is_unidirectional=False
888
+ )
889
+ else:
890
+ stream_id = self._http._quic.get_next_available_stream_id()
891
+
892
+ # Send data
893
+ self._http.send_data(stream_id=stream_id, data=data, end_stream=True)
894
+ self._protocol.transmit()
895
+
896
+ # Wait for response
897
+ future = asyncio.get_event_loop().create_future()
898
+ self._pending_requests[self._request_id] = future
899
+ self._protocol._pending_requests[self._request_id] = future
900
+
901
+ try:
902
+ response = await asyncio.wait_for(future, timeout=timeout)
903
+ return response
904
+ except asyncio.TimeoutError:
905
+ self._pending_requests.pop(self._request_id, None)
906
+ raise TimeoutError(f"Request {method} timed out after {timeout}s")
907
+
908
+ async def send_notification(self, method: str, params: Optional[Dict] = None):
909
+ """Send one-way notification (no response expected)."""
910
+ notification = {
911
+ "jsonrpc": "2.0",
912
+ "method": method,
913
+ "params": params or {}
914
+ }
915
+
916
+ data = self.serializer.serialize(notification)
917
+
918
+ # Use unidirectional stream for notifications
919
+ if self.quic_config.use_webtransport and self._session_id:
920
+ stream_id = self._http.create_webtransport_stream(
921
+ session_id=self._session_id,
922
+ is_unidirectional=True
923
+ )
924
+ else:
925
+ stream_id = self._http._quic.get_next_available_stream_id()
926
+
927
+ self._http.send_data(stream_id=stream_id, data=data, end_stream=True)
928
+ self._protocol.transmit()
929
+
930
+ def send_telemetry(self, data: bytes):
931
+ """Send unreliable telemetry via datagram."""
932
+ if self._protocol:
933
+ # Prefix with type byte
934
+ datagram = bytes([0x02]) + data
935
+ self._protocol._quic.send_datagram_frame(datagram)
936
+ self._protocol.transmit()
937
+
938
+ async def list_tools(self) -> List[Dict]:
939
+ """List available tools from MCP server."""
940
+ response = await self.send_request("tools/list")
941
+ return response.get("result", {}).get("tools", [])
942
+
943
+ async def call_tool(self, name: str, arguments: Dict) -> Dict:
944
+ """Call a tool on the MCP server."""
945
+ response = await self.send_request("tools/call", {
946
+ "name": name,
947
+ "arguments": arguments
948
+ })
949
+ return response.get("result", {})
950
+
951
+ async def disconnect(self):
952
+ """Disconnect from MCP server."""
953
+ self._initialized = False
954
+
955
+ if self._protocol:
956
+ self._protocol._quic.close()
957
+ self._protocol = None
958
+
959
+ self._http = None
960
+ self.logger.info("QUIC connection closed")
961
+
962
+ @property
963
+ def is_connected(self) -> bool:
964
+ return self._initialized and self._protocol is not None
965
+
966
+ async def __aenter__(self):
967
+ await self.connect()
968
+ return self
969
+
970
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
971
+ await self.disconnect()
972
+
973
+
974
+
975
+ # =============================================================================
976
+ # Factory Function
977
+ # =============================================================================
978
+ # Añadir a parrot/mcp/integration.py
979
+
980
+ def create_quic_mcp_server(
981
+ name: str,
982
+ host: str,
983
+ port: int = 4433,
984
+ *,
985
+ cert_path: Optional[str] = None,
986
+ ca_cert_path: Optional[str] = None,
987
+ insecure: bool = False,
988
+ serialization: str = "msgpack",
989
+ enable_0rtt: bool = True,
990
+ session_ticket_path: Optional[str] = None,
991
+ **kwargs
992
+ ) -> MCPServerConfig:
993
+ """
994
+ Create configuration for QUIC/HTTP3 MCP server.
995
+
996
+ This transport provides:
997
+ - ~40% lower latency than HTTP/SSE (0-RTT connection)
998
+ - ~60% smaller messages (MessagePack serialization)
999
+ - True multiplexing without head-of-line blocking
1000
+ - Connection migration for mobile agents
1001
+
1002
+ Args:
1003
+ name: Server name for tool prefixing
1004
+ host: Server hostname
1005
+ port: Server port (default 4433, standard QUIC port)
1006
+ cert_path: Path to TLS certificate
1007
+ ca_cert_path: Path to CA certificate for verification
1008
+ insecure: Skip certificate verification (dev only!)
1009
+ serialization: "json", "msgpack", or "protobuf"
1010
+ enable_0rtt: Enable 0-RTT fast reconnection
1011
+ session_ticket_path: Path to store session tickets for 0-RTT
1012
+
1013
+ Returns:
1014
+ MCPServerConfig configured for QUIC transport
1015
+
1016
+ Example:
1017
+ >>> # Production setup
1018
+ >>> config = create_quic_mcp_server(
1019
+ ... "ml-inference",
1020
+ ... host="ml-cluster.internal.example.com",
1021
+ ... port=4433,
1022
+ ... ca_cert_path="/etc/ssl/ca-bundle.crt",
1023
+ ... serialization="msgpack",
1024
+ ... )
1025
+ >>>
1026
+ >>> # Development with self-signed cert
1027
+ >>> config = create_quic_mcp_server(
1028
+ ... "local-tools",
1029
+ ... host="localhost",
1030
+ ... port=4433,
1031
+ ... insecure=True, # Only for development!
1032
+ ... )
1033
+ >>>
1034
+ >>> await agent.add_mcp_server(config)
1035
+ """
1036
+ quic_config = QuicMCPConfig(
1037
+ host=host,
1038
+ port=port,
1039
+ cert_path=cert_path,
1040
+ ca_cert_path=ca_cert_path,
1041
+ insecure=insecure,
1042
+ serialization=SerializationFormat(serialization),
1043
+ enable_0rtt=enable_0rtt,
1044
+ session_ticket_path=session_ticket_path,
1045
+ )
1046
+
1047
+ return MCPServerConfig(
1048
+ name=name,
1049
+ transport="quic",
1050
+ quic_config=quic_config,
1051
+ **kwargs
1052
+ )
1053
+
1054
+
1055
+ # =============================================================================
1056
+ # Certificate Generation Helper
1057
+ # =============================================================================
1058
+ def generate_self_signed_cert(
1059
+ cert_path: str = "cert.pem",
1060
+ key_path: str = "key.pem",
1061
+ hostname: str = "localhost",
1062
+ days: int = 365,
1063
+ ) -> None:
1064
+ """
1065
+ Generate self-signed certificate for development.
1066
+ For production, use proper certificates from a CA.
1067
+ """
1068
+ import subprocess
1069
+
1070
+ cmd = [
1071
+ "openssl", "req", "-x509",
1072
+ "-newkey", "rsa:4096",
1073
+ "-keyout", key_path,
1074
+ "-out", cert_path,
1075
+ "-days", str(days),
1076
+ "-nodes",
1077
+ "-subj", f"/CN={hostname}",
1078
+ "-addext", f"subjectAltName=DNS:{hostname},IP:127.0.0.1",
1079
+ ]
1080
+
1081
+ subprocess.run(cmd, check=True)
1082
+ print(f"Generated: {cert_path}, {key_path}")