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.
- agentui/.prettierrc +15 -0
- agentui/QUICKSTART.md +272 -0
- agentui/README.md +59 -0
- agentui/env.example +16 -0
- agentui/jsconfig.json +14 -0
- agentui/package-lock.json +4242 -0
- agentui/package.json +34 -0
- agentui/scripts/postinstall/apply-patches.mjs +260 -0
- agentui/src/app.css +61 -0
- agentui/src/app.d.ts +13 -0
- agentui/src/app.html +12 -0
- agentui/src/components/LoadingSpinner.svelte +64 -0
- agentui/src/components/ThemeSwitcher.svelte +159 -0
- agentui/src/components/index.js +4 -0
- agentui/src/lib/api/bots.ts +60 -0
- agentui/src/lib/api/chat.ts +22 -0
- agentui/src/lib/api/http.ts +25 -0
- agentui/src/lib/components/BotCard.svelte +33 -0
- agentui/src/lib/components/ChatBubble.svelte +63 -0
- agentui/src/lib/components/Toast.svelte +21 -0
- agentui/src/lib/config.ts +20 -0
- agentui/src/lib/stores/auth.svelte.ts +73 -0
- agentui/src/lib/stores/theme.svelte.js +64 -0
- agentui/src/lib/stores/toast.svelte.ts +31 -0
- agentui/src/lib/utils/conversation.ts +39 -0
- agentui/src/routes/+layout.svelte +20 -0
- agentui/src/routes/+page.svelte +232 -0
- agentui/src/routes/login/+page.svelte +200 -0
- agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
- agentui/src/routes/talk/[agentId]/+page.ts +7 -0
- agentui/static/README.md +1 -0
- agentui/svelte.config.js +11 -0
- agentui/tailwind.config.ts +53 -0
- agentui/tsconfig.json +3 -0
- agentui/vite.config.ts +10 -0
- ai_parrot-0.17.2.dist-info/METADATA +472 -0
- ai_parrot-0.17.2.dist-info/RECORD +535 -0
- ai_parrot-0.17.2.dist-info/WHEEL +6 -0
- ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
- ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
- ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
- crew-builder/.prettierrc +15 -0
- crew-builder/QUICKSTART.md +259 -0
- crew-builder/README.md +113 -0
- crew-builder/env.example +17 -0
- crew-builder/jsconfig.json +14 -0
- crew-builder/package-lock.json +4182 -0
- crew-builder/package.json +37 -0
- crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
- crew-builder/src/app.css +62 -0
- crew-builder/src/app.d.ts +13 -0
- crew-builder/src/app.html +12 -0
- crew-builder/src/components/LoadingSpinner.svelte +64 -0
- crew-builder/src/components/ThemeSwitcher.svelte +149 -0
- crew-builder/src/components/index.js +9 -0
- crew-builder/src/lib/api/bots.ts +60 -0
- crew-builder/src/lib/api/chat.ts +80 -0
- crew-builder/src/lib/api/client.ts +56 -0
- crew-builder/src/lib/api/crew/crew.ts +136 -0
- crew-builder/src/lib/api/index.ts +5 -0
- crew-builder/src/lib/api/o365/auth.ts +65 -0
- crew-builder/src/lib/auth/auth.ts +54 -0
- crew-builder/src/lib/components/AgentNode.svelte +43 -0
- crew-builder/src/lib/components/BotCard.svelte +33 -0
- crew-builder/src/lib/components/ChatBubble.svelte +67 -0
- crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
- crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
- crew-builder/src/lib/components/JsonViewer.svelte +24 -0
- crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
- crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
- crew-builder/src/lib/components/Toast.svelte +67 -0
- crew-builder/src/lib/components/Toolbar.svelte +157 -0
- crew-builder/src/lib/components/index.ts +10 -0
- crew-builder/src/lib/config.ts +8 -0
- crew-builder/src/lib/stores/auth.svelte.ts +228 -0
- crew-builder/src/lib/stores/crewStore.ts +369 -0
- crew-builder/src/lib/stores/theme.svelte.js +145 -0
- crew-builder/src/lib/stores/toast.svelte.ts +69 -0
- crew-builder/src/lib/utils/conversation.ts +39 -0
- crew-builder/src/lib/utils/markdown.ts +122 -0
- crew-builder/src/lib/utils/talkHistory.ts +47 -0
- crew-builder/src/routes/+layout.svelte +20 -0
- crew-builder/src/routes/+page.svelte +539 -0
- crew-builder/src/routes/agents/+page.svelte +247 -0
- crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
- crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
- crew-builder/src/routes/builder/+page.svelte +204 -0
- crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
- crew-builder/src/routes/crew/ask/+page.ts +1 -0
- crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
- crew-builder/src/routes/login/+page.svelte +197 -0
- crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
- crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
- crew-builder/static/README.md +1 -0
- crew-builder/svelte.config.js +11 -0
- crew-builder/tailwind.config.ts +53 -0
- crew-builder/tsconfig.json +3 -0
- crew-builder/vite.config.ts +10 -0
- mcp_servers/calculator_server.py +309 -0
- parrot/__init__.py +27 -0
- parrot/__pycache__/__init__.cpython-310.pyc +0 -0
- parrot/__pycache__/version.cpython-310.pyc +0 -0
- parrot/_version.py +34 -0
- parrot/a2a/__init__.py +48 -0
- parrot/a2a/client.py +658 -0
- parrot/a2a/discovery.py +89 -0
- parrot/a2a/mixin.py +257 -0
- parrot/a2a/models.py +376 -0
- parrot/a2a/server.py +770 -0
- parrot/agents/__init__.py +29 -0
- parrot/bots/__init__.py +12 -0
- parrot/bots/a2a_agent.py +19 -0
- parrot/bots/abstract.py +3139 -0
- parrot/bots/agent.py +1129 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/chatbot.py +669 -0
- parrot/bots/data.py +1618 -0
- parrot/bots/database/__init__.py +5 -0
- parrot/bots/database/abstract.py +3071 -0
- parrot/bots/database/cache.py +286 -0
- parrot/bots/database/models.py +468 -0
- parrot/bots/database/prompts.py +154 -0
- parrot/bots/database/retries.py +98 -0
- parrot/bots/database/router.py +269 -0
- parrot/bots/database/sql.py +41 -0
- parrot/bots/db/__init__.py +6 -0
- parrot/bots/db/abstract.py +556 -0
- parrot/bots/db/bigquery.py +602 -0
- parrot/bots/db/cache.py +85 -0
- parrot/bots/db/documentdb.py +668 -0
- parrot/bots/db/elastic.py +1014 -0
- parrot/bots/db/influx.py +898 -0
- parrot/bots/db/mock.py +96 -0
- parrot/bots/db/multi.py +783 -0
- parrot/bots/db/prompts.py +185 -0
- parrot/bots/db/sql.py +1255 -0
- parrot/bots/db/tools.py +212 -0
- parrot/bots/document.py +680 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/kb.py +170 -0
- parrot/bots/mcp.py +36 -0
- parrot/bots/orchestration/README.md +463 -0
- parrot/bots/orchestration/__init__.py +1 -0
- parrot/bots/orchestration/agent.py +155 -0
- parrot/bots/orchestration/crew.py +3330 -0
- parrot/bots/orchestration/fsm.py +1179 -0
- parrot/bots/orchestration/hr.py +434 -0
- parrot/bots/orchestration/storage/__init__.py +4 -0
- parrot/bots/orchestration/storage/memory.py +100 -0
- parrot/bots/orchestration/storage/mixin.py +119 -0
- parrot/bots/orchestration/verify.py +202 -0
- parrot/bots/product.py +204 -0
- parrot/bots/prompts/__init__.py +96 -0
- parrot/bots/prompts/agents.py +155 -0
- parrot/bots/prompts/data.py +216 -0
- parrot/bots/prompts/output_generation.py +8 -0
- parrot/bots/scraper/__init__.py +3 -0
- parrot/bots/scraper/models.py +122 -0
- parrot/bots/scraper/scraper.py +1173 -0
- parrot/bots/scraper/templates.py +115 -0
- parrot/bots/stores/__init__.py +5 -0
- parrot/bots/stores/local.py +172 -0
- parrot/bots/webdev.py +81 -0
- parrot/cli.py +17 -0
- parrot/clients/__init__.py +16 -0
- parrot/clients/base.py +1491 -0
- parrot/clients/claude.py +1191 -0
- parrot/clients/factory.py +129 -0
- parrot/clients/google.py +4567 -0
- parrot/clients/gpt.py +1975 -0
- parrot/clients/grok.py +432 -0
- parrot/clients/groq.py +986 -0
- parrot/clients/hf.py +582 -0
- parrot/clients/models.py +18 -0
- parrot/conf.py +395 -0
- parrot/embeddings/__init__.py +9 -0
- parrot/embeddings/base.py +157 -0
- parrot/embeddings/google.py +98 -0
- parrot/embeddings/huggingface.py +74 -0
- parrot/embeddings/openai.py +84 -0
- parrot/embeddings/processor.py +88 -0
- parrot/exceptions.c +13868 -0
- parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/exceptions.pxd +22 -0
- parrot/exceptions.pxi +15 -0
- parrot/exceptions.pyx +44 -0
- parrot/generators/__init__.py +29 -0
- parrot/generators/base.py +200 -0
- parrot/generators/html.py +293 -0
- parrot/generators/react.py +205 -0
- parrot/generators/streamlit.py +203 -0
- parrot/generators/template.py +105 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agent.py +861 -0
- parrot/handlers/agents/__init__.py +1 -0
- parrot/handlers/agents/abstract.py +900 -0
- parrot/handlers/bots.py +338 -0
- parrot/handlers/chat.py +915 -0
- parrot/handlers/creation.sql +192 -0
- parrot/handlers/crew/ARCHITECTURE.md +362 -0
- parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
- parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
- parrot/handlers/crew/__init__.py +0 -0
- parrot/handlers/crew/handler.py +801 -0
- parrot/handlers/crew/models.py +229 -0
- parrot/handlers/crew/redis_persistence.py +523 -0
- parrot/handlers/jobs/__init__.py +10 -0
- parrot/handlers/jobs/job.py +384 -0
- parrot/handlers/jobs/mixin.py +627 -0
- parrot/handlers/jobs/models.py +115 -0
- parrot/handlers/jobs/worker.py +31 -0
- parrot/handlers/models.py +596 -0
- parrot/handlers/o365_auth.py +105 -0
- parrot/handlers/stream.py +337 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/aws.py +143 -0
- parrot/interfaces/credentials.py +113 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/google.py +1123 -0
- parrot/interfaces/hierarchy.py +1227 -0
- parrot/interfaces/http.py +651 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +24 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/analisys.py +148 -0
- parrot/interfaces/images/plugins/classify.py +150 -0
- parrot/interfaces/images/plugins/classifybase.py +182 -0
- parrot/interfaces/images/plugins/detect.py +150 -0
- parrot/interfaces/images/plugins/exif.py +1103 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/interfaces/o365.py +978 -0
- parrot/interfaces/onedrive.py +822 -0
- parrot/interfaces/sharepoint.py +1435 -0
- parrot/interfaces/soap.py +257 -0
- parrot/loaders/__init__.py +8 -0
- parrot/loaders/abstract.py +1131 -0
- parrot/loaders/audio.py +199 -0
- parrot/loaders/basepdf.py +53 -0
- parrot/loaders/basevideo.py +1568 -0
- parrot/loaders/csv.py +409 -0
- parrot/loaders/docx.py +116 -0
- parrot/loaders/epubloader.py +316 -0
- parrot/loaders/excel.py +199 -0
- parrot/loaders/factory.py +55 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/html.py +26 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/html.py +152 -0
- parrot/loaders/markdown.py +442 -0
- parrot/loaders/pdf.py +373 -0
- parrot/loaders/pdfmark.py +320 -0
- parrot/loaders/pdftables.py +506 -0
- parrot/loaders/ppt.py +476 -0
- parrot/loaders/qa.py +63 -0
- parrot/loaders/splitters/__init__.py +10 -0
- parrot/loaders/splitters/base.py +138 -0
- parrot/loaders/splitters/md.py +228 -0
- parrot/loaders/splitters/token.py +143 -0
- parrot/loaders/txt.py +26 -0
- parrot/loaders/video.py +89 -0
- parrot/loaders/videolocal.py +218 -0
- parrot/loaders/videounderstanding.py +377 -0
- parrot/loaders/vimeo.py +167 -0
- parrot/loaders/web.py +599 -0
- parrot/loaders/youtube.py +504 -0
- parrot/manager/__init__.py +5 -0
- parrot/manager/manager.py +1030 -0
- parrot/mcp/__init__.py +28 -0
- parrot/mcp/adapter.py +105 -0
- parrot/mcp/cli.py +174 -0
- parrot/mcp/client.py +119 -0
- parrot/mcp/config.py +75 -0
- parrot/mcp/integration.py +842 -0
- parrot/mcp/oauth.py +933 -0
- parrot/mcp/server.py +225 -0
- parrot/mcp/transports/__init__.py +3 -0
- parrot/mcp/transports/base.py +279 -0
- parrot/mcp/transports/grpc_session.py +163 -0
- parrot/mcp/transports/http.py +312 -0
- parrot/mcp/transports/mcp.proto +108 -0
- parrot/mcp/transports/quic.py +1082 -0
- parrot/mcp/transports/sse.py +330 -0
- parrot/mcp/transports/stdio.py +309 -0
- parrot/mcp/transports/unix.py +395 -0
- parrot/mcp/transports/websocket.py +547 -0
- parrot/memory/__init__.py +16 -0
- parrot/memory/abstract.py +209 -0
- parrot/memory/agent.py +32 -0
- parrot/memory/cache.py +175 -0
- parrot/memory/core.py +555 -0
- parrot/memory/file.py +153 -0
- parrot/memory/mem.py +131 -0
- parrot/memory/redis.py +613 -0
- parrot/models/__init__.py +46 -0
- parrot/models/basic.py +118 -0
- parrot/models/compliance.py +208 -0
- parrot/models/crew.py +395 -0
- parrot/models/detections.py +654 -0
- parrot/models/generation.py +85 -0
- parrot/models/google.py +223 -0
- parrot/models/groq.py +23 -0
- parrot/models/openai.py +30 -0
- parrot/models/outputs.py +285 -0
- parrot/models/responses.py +938 -0
- parrot/notifications/__init__.py +743 -0
- parrot/openapi/__init__.py +3 -0
- parrot/openapi/components.yaml +641 -0
- parrot/openapi/config.py +322 -0
- parrot/outputs/__init__.py +32 -0
- parrot/outputs/formats/__init__.py +108 -0
- parrot/outputs/formats/altair.py +359 -0
- parrot/outputs/formats/application.py +122 -0
- parrot/outputs/formats/base.py +351 -0
- parrot/outputs/formats/bokeh.py +356 -0
- parrot/outputs/formats/card.py +424 -0
- parrot/outputs/formats/chart.py +436 -0
- parrot/outputs/formats/d3.py +255 -0
- parrot/outputs/formats/echarts.py +310 -0
- parrot/outputs/formats/generators/__init__.py +0 -0
- parrot/outputs/formats/generators/abstract.py +61 -0
- parrot/outputs/formats/generators/panel.py +145 -0
- parrot/outputs/formats/generators/streamlit.py +86 -0
- parrot/outputs/formats/generators/terminal.py +63 -0
- parrot/outputs/formats/holoviews.py +310 -0
- parrot/outputs/formats/html.py +147 -0
- parrot/outputs/formats/jinja2.py +46 -0
- parrot/outputs/formats/json.py +87 -0
- parrot/outputs/formats/map.py +933 -0
- parrot/outputs/formats/markdown.py +172 -0
- parrot/outputs/formats/matplotlib.py +237 -0
- parrot/outputs/formats/mixins/__init__.py +0 -0
- parrot/outputs/formats/mixins/emaps.py +855 -0
- parrot/outputs/formats/plotly.py +341 -0
- parrot/outputs/formats/seaborn.py +310 -0
- parrot/outputs/formats/table.py +397 -0
- parrot/outputs/formats/template_report.py +138 -0
- parrot/outputs/formats/yaml.py +125 -0
- parrot/outputs/formatter.py +152 -0
- parrot/outputs/templates/__init__.py +95 -0
- parrot/pipelines/__init__.py +0 -0
- parrot/pipelines/abstract.py +210 -0
- parrot/pipelines/detector.py +124 -0
- parrot/pipelines/models.py +90 -0
- parrot/pipelines/planogram.py +3002 -0
- parrot/pipelines/table.sql +97 -0
- parrot/plugins/__init__.py +106 -0
- parrot/plugins/importer.py +80 -0
- parrot/py.typed +0 -0
- parrot/registry/__init__.py +18 -0
- parrot/registry/registry.py +594 -0
- parrot/scheduler/__init__.py +1189 -0
- parrot/scheduler/models.py +60 -0
- parrot/security/__init__.py +16 -0
- parrot/security/prompt_injection.py +268 -0
- parrot/security/security_events.sql +25 -0
- parrot/services/__init__.py +1 -0
- parrot/services/mcp/__init__.py +8 -0
- parrot/services/mcp/config.py +13 -0
- parrot/services/mcp/server.py +295 -0
- parrot/services/o365_remote_auth.py +235 -0
- parrot/stores/__init__.py +7 -0
- parrot/stores/abstract.py +352 -0
- parrot/stores/arango.py +1090 -0
- parrot/stores/bigquery.py +1377 -0
- parrot/stores/cache.py +106 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss_store.py +1157 -0
- parrot/stores/kb/__init__.py +9 -0
- parrot/stores/kb/abstract.py +68 -0
- parrot/stores/kb/cache.py +165 -0
- parrot/stores/kb/doc.py +325 -0
- parrot/stores/kb/hierarchy.py +346 -0
- parrot/stores/kb/local.py +457 -0
- parrot/stores/kb/prompt.py +28 -0
- parrot/stores/kb/redis.py +659 -0
- parrot/stores/kb/store.py +115 -0
- parrot/stores/kb/user.py +374 -0
- parrot/stores/models.py +59 -0
- parrot/stores/pgvector.py +3 -0
- parrot/stores/postgres.py +2853 -0
- parrot/stores/utils/__init__.py +0 -0
- parrot/stores/utils/chunking.py +197 -0
- parrot/telemetry/__init__.py +3 -0
- parrot/telemetry/mixin.py +111 -0
- parrot/template/__init__.py +3 -0
- parrot/template/engine.py +259 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +644 -0
- parrot/tools/agent.py +363 -0
- parrot/tools/arangodbsearch.py +537 -0
- parrot/tools/arxiv_tool.py +188 -0
- parrot/tools/calculator/__init__.py +3 -0
- parrot/tools/calculator/operations/__init__.py +38 -0
- parrot/tools/calculator/operations/calculus.py +80 -0
- parrot/tools/calculator/operations/statistics.py +76 -0
- parrot/tools/calculator/tool.py +150 -0
- parrot/tools/cloudwatch.py +988 -0
- parrot/tools/codeinterpreter/__init__.py +127 -0
- parrot/tools/codeinterpreter/executor.py +371 -0
- parrot/tools/codeinterpreter/internals.py +473 -0
- parrot/tools/codeinterpreter/models.py +643 -0
- parrot/tools/codeinterpreter/prompts.py +224 -0
- parrot/tools/codeinterpreter/tool.py +664 -0
- parrot/tools/company_info/__init__.py +6 -0
- parrot/tools/company_info/tool.py +1138 -0
- parrot/tools/correlationanalysis.py +437 -0
- parrot/tools/database/abstract.py +286 -0
- parrot/tools/database/bq.py +115 -0
- parrot/tools/database/cache.py +284 -0
- parrot/tools/database/models.py +95 -0
- parrot/tools/database/pg.py +343 -0
- parrot/tools/databasequery.py +1159 -0
- parrot/tools/db.py +1800 -0
- parrot/tools/ddgo.py +370 -0
- parrot/tools/decorators.py +271 -0
- parrot/tools/dftohtml.py +282 -0
- parrot/tools/document.py +549 -0
- parrot/tools/ecs.py +819 -0
- parrot/tools/edareport.py +368 -0
- parrot/tools/elasticsearch.py +1049 -0
- parrot/tools/employees.py +462 -0
- parrot/tools/epson/__init__.py +96 -0
- parrot/tools/excel.py +683 -0
- parrot/tools/file/__init__.py +13 -0
- parrot/tools/file/abstract.py +76 -0
- parrot/tools/file/gcs.py +378 -0
- parrot/tools/file/local.py +284 -0
- parrot/tools/file/s3.py +511 -0
- parrot/tools/file/tmp.py +309 -0
- parrot/tools/file/tool.py +501 -0
- parrot/tools/file_reader.py +129 -0
- parrot/tools/flowtask/__init__.py +19 -0
- parrot/tools/flowtask/tool.py +761 -0
- parrot/tools/gittoolkit.py +508 -0
- parrot/tools/google/__init__.py +18 -0
- parrot/tools/google/base.py +169 -0
- parrot/tools/google/tools.py +1251 -0
- parrot/tools/googlelocation.py +5 -0
- parrot/tools/googleroutes.py +5 -0
- parrot/tools/googlesearch.py +5 -0
- parrot/tools/googlesitesearch.py +5 -0
- parrot/tools/googlevoice.py +2 -0
- parrot/tools/gvoice.py +695 -0
- parrot/tools/ibisworld/README.md +225 -0
- parrot/tools/ibisworld/__init__.py +11 -0
- parrot/tools/ibisworld/tool.py +366 -0
- parrot/tools/jiratoolkit.py +1718 -0
- parrot/tools/manager.py +1098 -0
- parrot/tools/math.py +152 -0
- parrot/tools/metadata.py +476 -0
- parrot/tools/msteams.py +1621 -0
- parrot/tools/msword.py +635 -0
- parrot/tools/multidb.py +580 -0
- parrot/tools/multistoresearch.py +369 -0
- parrot/tools/networkninja.py +167 -0
- parrot/tools/nextstop/__init__.py +4 -0
- parrot/tools/nextstop/base.py +286 -0
- parrot/tools/nextstop/employee.py +733 -0
- parrot/tools/nextstop/store.py +462 -0
- parrot/tools/notification.py +435 -0
- parrot/tools/o365/__init__.py +42 -0
- parrot/tools/o365/base.py +295 -0
- parrot/tools/o365/bundle.py +522 -0
- parrot/tools/o365/events.py +554 -0
- parrot/tools/o365/mail.py +992 -0
- parrot/tools/o365/onedrive.py +497 -0
- parrot/tools/o365/sharepoint.py +641 -0
- parrot/tools/openapi_toolkit.py +904 -0
- parrot/tools/openweather.py +527 -0
- parrot/tools/pdfprint.py +1001 -0
- parrot/tools/powerbi.py +518 -0
- parrot/tools/powerpoint.py +1113 -0
- parrot/tools/pricestool.py +146 -0
- parrot/tools/products/__init__.py +246 -0
- parrot/tools/prophet_tool.py +171 -0
- parrot/tools/pythonpandas.py +630 -0
- parrot/tools/pythonrepl.py +910 -0
- parrot/tools/qsource.py +436 -0
- parrot/tools/querytoolkit.py +395 -0
- parrot/tools/quickeda.py +827 -0
- parrot/tools/resttool.py +553 -0
- parrot/tools/retail/__init__.py +0 -0
- parrot/tools/retail/bby.py +528 -0
- parrot/tools/sandboxtool.py +703 -0
- parrot/tools/sassie/__init__.py +352 -0
- parrot/tools/scraping/__init__.py +7 -0
- parrot/tools/scraping/docs/select.md +466 -0
- parrot/tools/scraping/documentation.md +1278 -0
- parrot/tools/scraping/driver.py +436 -0
- parrot/tools/scraping/models.py +576 -0
- parrot/tools/scraping/options.py +85 -0
- parrot/tools/scraping/orchestrator.py +517 -0
- parrot/tools/scraping/readme.md +740 -0
- parrot/tools/scraping/tool.py +3115 -0
- parrot/tools/seasonaldetection.py +642 -0
- parrot/tools/shell_tool/__init__.py +5 -0
- parrot/tools/shell_tool/actions.py +408 -0
- parrot/tools/shell_tool/engine.py +155 -0
- parrot/tools/shell_tool/models.py +322 -0
- parrot/tools/shell_tool/tool.py +442 -0
- parrot/tools/site_search.py +214 -0
- parrot/tools/textfile.py +418 -0
- parrot/tools/think.py +378 -0
- parrot/tools/toolkit.py +298 -0
- parrot/tools/webapp_tool.py +187 -0
- parrot/tools/whatif.py +1279 -0
- parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
- parrot/tools/workday/__init__.py +6 -0
- parrot/tools/workday/models.py +1389 -0
- parrot/tools/workday/tool.py +1293 -0
- parrot/tools/yfinance_tool.py +306 -0
- parrot/tools/zipcode.py +217 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/helpers.py +73 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.c +12078 -0
- parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/parsers/toml.pyx +21 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpp +20936 -0
- parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/types.pyx +213 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- parrot/yaml-rs/Cargo.lock +350 -0
- parrot/yaml-rs/Cargo.toml +19 -0
- parrot/yaml-rs/pyproject.toml +19 -0
- parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
- parrot/yaml-rs/src/lib.rs +222 -0
- requirements/docker-compose.yml +24 -0
- requirements/requirements-dev.txt +21 -0
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, List, Any, Optional, Union
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
# AI-Parrot imports
|
|
6
|
+
from ..tools.abstract import AbstractTool, ToolResult
|
|
7
|
+
from ..tools.manager import ToolManager
|
|
8
|
+
from .oauth import (
|
|
9
|
+
OAuthManager,
|
|
10
|
+
InMemoryTokenStore,
|
|
11
|
+
RedisTokenStore
|
|
12
|
+
)
|
|
13
|
+
from .client import (
|
|
14
|
+
MCPClientConfig as MCPServerConfig,
|
|
15
|
+
MCPConnectionError
|
|
16
|
+
)
|
|
17
|
+
from .transports.stdio import StdioMCPSession
|
|
18
|
+
from .transports.unix import UnixMCPSession
|
|
19
|
+
from .transports.http import HttpMCPSession
|
|
20
|
+
from .transports.websocket import WebSocketMCPSession
|
|
21
|
+
from .transports.sse import SseMCPSession
|
|
22
|
+
from .transports.quic import (
|
|
23
|
+
QuicMCPSession,
|
|
24
|
+
QuicMCPConfig,
|
|
25
|
+
SerializationFormat
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MCPToolProxy(AbstractTool):
|
|
30
|
+
"""Proxy tool that wraps an individual MCP tool."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
mcp_tool_def: Dict[str, Any],
|
|
35
|
+
mcp_client: 'MCPClient',
|
|
36
|
+
server_name: str,
|
|
37
|
+
**kwargs
|
|
38
|
+
):
|
|
39
|
+
super().__init__(**kwargs)
|
|
40
|
+
|
|
41
|
+
self.mcp_tool_def = mcp_tool_def
|
|
42
|
+
self.mcp_client = mcp_client
|
|
43
|
+
self.server_name = server_name
|
|
44
|
+
|
|
45
|
+
self.name = f"mcp_{server_name}_{mcp_tool_def['name']}"
|
|
46
|
+
self.description = mcp_tool_def.get('description', f"MCP tool: {mcp_tool_def['name']}")
|
|
47
|
+
self.input_schema = mcp_tool_def.get('inputSchema', {})
|
|
48
|
+
|
|
49
|
+
self.logger = logging.getLogger(f"MCPTool.{self.name}")
|
|
50
|
+
|
|
51
|
+
async def _execute(self, **kwargs) -> ToolResult:
|
|
52
|
+
"""Execute the MCP tool."""
|
|
53
|
+
try:
|
|
54
|
+
result = await self.mcp_client.call_tool(
|
|
55
|
+
self.mcp_tool_def['name'],
|
|
56
|
+
kwargs
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
result_text = self._extract_result_text(result)
|
|
60
|
+
|
|
61
|
+
return ToolResult(
|
|
62
|
+
status="success",
|
|
63
|
+
result=result_text,
|
|
64
|
+
metadata={
|
|
65
|
+
"server": self.server_name,
|
|
66
|
+
"tool": self.mcp_tool_def['name'],
|
|
67
|
+
"transport": self.mcp_client.config.transport,
|
|
68
|
+
"mcp_response_type": type(result).__name__
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
except Exception as e:
|
|
73
|
+
self.logger.error(f"Error executing MCP tool {self.name}: {e}")
|
|
74
|
+
return ToolResult(
|
|
75
|
+
status="error",
|
|
76
|
+
result=None,
|
|
77
|
+
error=str(e),
|
|
78
|
+
metadata={
|
|
79
|
+
"server": self.server_name,
|
|
80
|
+
"tool": self.mcp_tool_def['name']
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _extract_result_text(self, result) -> str:
|
|
85
|
+
"""Extract text content from MCP response."""
|
|
86
|
+
if hasattr(result, 'content') and result.content:
|
|
87
|
+
content_parts = []
|
|
88
|
+
for item in result.content:
|
|
89
|
+
if hasattr(item, 'text'):
|
|
90
|
+
content_parts.append(item.text)
|
|
91
|
+
elif isinstance(item, dict):
|
|
92
|
+
content_parts.append(item.get('text', str(item)))
|
|
93
|
+
else:
|
|
94
|
+
content_parts.append(str(item))
|
|
95
|
+
return "\n".join(content_parts) if content_parts else str(result)
|
|
96
|
+
return str(result)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class MCPClient:
|
|
100
|
+
"""Complete MCP client with stdio and HTTP transport support."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, config: MCPServerConfig):
|
|
103
|
+
self.config = config
|
|
104
|
+
self.logger = logging.getLogger(f"MCPClient.{config.name}")
|
|
105
|
+
self._session = None
|
|
106
|
+
self._connected = False
|
|
107
|
+
self._available_tools = []
|
|
108
|
+
|
|
109
|
+
def _detect_transport(self) -> str:
|
|
110
|
+
"""Auto-detect transport type."""
|
|
111
|
+
if self.config.transport != "auto":
|
|
112
|
+
return self.config.transport
|
|
113
|
+
|
|
114
|
+
if self.config.socket_path:
|
|
115
|
+
return "unix"
|
|
116
|
+
if self.config.url:
|
|
117
|
+
# Check if URL looks like SSE endpoint
|
|
118
|
+
if "events" in self.config.url or "sse" in self.config.url:
|
|
119
|
+
return "sse"
|
|
120
|
+
else:
|
|
121
|
+
return "http"
|
|
122
|
+
elif self.config.command:
|
|
123
|
+
return "stdio"
|
|
124
|
+
else:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
"Cannot auto-detect transport. "
|
|
127
|
+
"Please specify socket_path, url, or command."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async def connect(self):
|
|
131
|
+
"""Connect to MCP server using appropriate transport."""
|
|
132
|
+
if self._connected:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
transport = self._detect_transport()
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
if transport == "stdio":
|
|
139
|
+
self._session = StdioMCPSession(self.config, self.logger)
|
|
140
|
+
elif transport == "http":
|
|
141
|
+
self._session = HttpMCPSession(self.config, self.logger)
|
|
142
|
+
elif transport == "sse":
|
|
143
|
+
self._session = SseMCPSession(self.config, self.logger)
|
|
144
|
+
elif transport == "unix":
|
|
145
|
+
self._session = UnixMCPSession(self.config, self.logger)
|
|
146
|
+
elif transport == "websocket":
|
|
147
|
+
|
|
148
|
+
self._session = WebSocketMCPSession(self.config, self.logger)
|
|
149
|
+
elif transport == "quic":
|
|
150
|
+
try:
|
|
151
|
+
self._session = QuicMCPSession(self.config, self.logger)
|
|
152
|
+
except ImportError as e:
|
|
153
|
+
raise ImportError(
|
|
154
|
+
"QUIC transport requires 'aioquic' package. Install with: pip install aioquic msgpack"
|
|
155
|
+
) from e
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f"Unsupported transport: {transport}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
await self._session.connect()
|
|
162
|
+
self._available_tools = await self._session.list_tools()
|
|
163
|
+
self._connected = True
|
|
164
|
+
|
|
165
|
+
self.logger.info(
|
|
166
|
+
f"Connected to MCP server {self.config.name} "
|
|
167
|
+
f"via {transport} with {len(self._available_tools)} tools"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
self.logger.error(f"Failed to connect: {e}")
|
|
172
|
+
await self.disconnect()
|
|
173
|
+
raise
|
|
174
|
+
|
|
175
|
+
async def call_tool(self, tool_name: str, arguments: Dict[str, Any]):
|
|
176
|
+
"""Call an MCP tool."""
|
|
177
|
+
if not self._connected:
|
|
178
|
+
raise MCPConnectionError("Not connected to MCP server")
|
|
179
|
+
|
|
180
|
+
return await self._session.call_tool(tool_name, arguments)
|
|
181
|
+
|
|
182
|
+
def get_available_tools(self) -> List[Dict[str, Any]]:
|
|
183
|
+
"""Get available tools as dictionaries."""
|
|
184
|
+
tools = []
|
|
185
|
+
for tool in self._available_tools:
|
|
186
|
+
tool_dict = {
|
|
187
|
+
'name': getattr(tool, 'name', 'unknown'),
|
|
188
|
+
'description': getattr(tool, 'description', ''),
|
|
189
|
+
'inputSchema': getattr(tool, 'inputSchema', {})
|
|
190
|
+
}
|
|
191
|
+
tools.append(tool_dict)
|
|
192
|
+
return tools
|
|
193
|
+
|
|
194
|
+
async def disconnect(self):
|
|
195
|
+
"""Disconnect from MCP server."""
|
|
196
|
+
if not self._connected:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
self._connected = False
|
|
200
|
+
|
|
201
|
+
if self._session:
|
|
202
|
+
await self._session.disconnect()
|
|
203
|
+
self._session = None
|
|
204
|
+
|
|
205
|
+
self._available_tools = []
|
|
206
|
+
self.logger.info(f"Disconnected from {self.config.name}")
|
|
207
|
+
|
|
208
|
+
async def __aenter__(self):
|
|
209
|
+
await self.connect()
|
|
210
|
+
return self
|
|
211
|
+
|
|
212
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
213
|
+
await self.disconnect()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class MCPToolManager:
|
|
217
|
+
"""Manages multiple MCP servers and their tools."""
|
|
218
|
+
|
|
219
|
+
def __init__(self, tool_manager: ToolManager):
|
|
220
|
+
self.tool_manager = tool_manager
|
|
221
|
+
self.mcp_clients: Dict[str, MCPClient] = {}
|
|
222
|
+
self.logger = logging.getLogger("MCPToolManager")
|
|
223
|
+
|
|
224
|
+
async def add_mcp_server(self, config: MCPServerConfig) -> List[str]:
|
|
225
|
+
"""Add an MCP server and register its tools."""
|
|
226
|
+
client = MCPClient(config)
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
await client.connect()
|
|
230
|
+
self.mcp_clients[config.name] = client
|
|
231
|
+
|
|
232
|
+
available_tools = client.get_available_tools()
|
|
233
|
+
registered_tools = []
|
|
234
|
+
|
|
235
|
+
for tool_def in available_tools:
|
|
236
|
+
tool_name = tool_def.get('name', 'unknown')
|
|
237
|
+
|
|
238
|
+
if self._should_skip_tool(tool_name, config):
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
proxy_tool = MCPToolProxy(
|
|
242
|
+
mcp_tool_def=tool_def,
|
|
243
|
+
mcp_client=client,
|
|
244
|
+
server_name=config.name
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
self.tool_manager.register_tool(proxy_tool)
|
|
248
|
+
registered_tools.append(proxy_tool.name)
|
|
249
|
+
self.logger.info(f"Registered MCP tool: {proxy_tool.name}")
|
|
250
|
+
|
|
251
|
+
transport = getattr(client, '_session', None)
|
|
252
|
+
transport_type = config.transport if config.transport != "auto" else "detected"
|
|
253
|
+
|
|
254
|
+
self.logger.info(
|
|
255
|
+
f"Successfully added MCP server {config.name} "
|
|
256
|
+
f"({transport_type} transport) with {len(registered_tools)} tools"
|
|
257
|
+
)
|
|
258
|
+
return registered_tools
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
self.logger.error(f"Failed to add MCP server {config.name}: {e}")
|
|
262
|
+
await self._cleanup_failed_client(config.name, client)
|
|
263
|
+
raise
|
|
264
|
+
|
|
265
|
+
def _should_skip_tool(self, tool_name: str, config: MCPServerConfig) -> bool:
|
|
266
|
+
"""Check if tool should be skipped based on filtering rules."""
|
|
267
|
+
if config.allowed_tools and tool_name not in config.allowed_tools:
|
|
268
|
+
self.logger.debug(f"Skipping tool {tool_name} (not in allowed_tools)")
|
|
269
|
+
return True
|
|
270
|
+
if config.blocked_tools and tool_name in config.blocked_tools:
|
|
271
|
+
self.logger.debug(f"Skipping tool {tool_name} (in blocked_tools)")
|
|
272
|
+
return True
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
async def _cleanup_failed_client(self, server_name: str, client: MCPClient):
|
|
276
|
+
"""Clean up a failed client connection."""
|
|
277
|
+
if server_name in self.mcp_clients:
|
|
278
|
+
del self.mcp_clients[server_name]
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
await client.disconnect()
|
|
282
|
+
except Exception:
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
async def remove_mcp_server(self, server_name: str):
|
|
286
|
+
"""Remove an MCP server and unregister its tools."""
|
|
287
|
+
if server_name not in self.mcp_clients:
|
|
288
|
+
self.logger.warning(f"MCP server {server_name} not found")
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
client = self.mcp_clients[server_name]
|
|
292
|
+
|
|
293
|
+
tools_to_remove = [
|
|
294
|
+
tool_name for tool_name in self.tool_manager.list_tools()
|
|
295
|
+
if tool_name.startswith(f"mcp_{server_name}_")
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
for tool_name in tools_to_remove:
|
|
299
|
+
self.tool_manager.unregister_tool(tool_name)
|
|
300
|
+
self.logger.info(f"Unregistered MCP tool: {tool_name}")
|
|
301
|
+
|
|
302
|
+
await client.disconnect()
|
|
303
|
+
del self.mcp_clients[server_name]
|
|
304
|
+
|
|
305
|
+
async def reconfigure_mcp_server(self, config: MCPServerConfig) -> List[str]:
|
|
306
|
+
"""Reconfigure an existing MCP server with new configuration.
|
|
307
|
+
|
|
308
|
+
This method removes the existing server connection and re-adds it with the
|
|
309
|
+
new configuration. Useful for updating credentials or connection parameters.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
config: New MCPServerConfig with updated parameters
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
List of registered tool names
|
|
316
|
+
|
|
317
|
+
Example:
|
|
318
|
+
>>> # Update Fireflies API key for a different user
|
|
319
|
+
>>> new_config = create_fireflies_mcp_server(api_key="new-user-api-key")
|
|
320
|
+
>>> tools = await manager.reconfigure_mcp_server(new_config)
|
|
321
|
+
"""
|
|
322
|
+
server_name = config.name
|
|
323
|
+
|
|
324
|
+
# Remove existing server if it exists
|
|
325
|
+
if server_name in self.mcp_clients:
|
|
326
|
+
self.logger.info(f"Reconfiguring MCP server: {server_name}")
|
|
327
|
+
await self.remove_mcp_server(server_name)
|
|
328
|
+
else:
|
|
329
|
+
self.logger.info(f"Adding new MCP server: {server_name}")
|
|
330
|
+
|
|
331
|
+
# Add with new configuration
|
|
332
|
+
return await self.add_mcp_server(config)
|
|
333
|
+
|
|
334
|
+
async def disconnect_all(self):
|
|
335
|
+
"""Disconnect all MCP clients."""
|
|
336
|
+
for client in list(self.mcp_clients.values()):
|
|
337
|
+
await client.disconnect()
|
|
338
|
+
self.mcp_clients.clear()
|
|
339
|
+
|
|
340
|
+
def list_mcp_servers(self) -> List[str]:
|
|
341
|
+
return list(self.mcp_clients.keys())
|
|
342
|
+
|
|
343
|
+
def get_mcp_client(self, server_name: str) -> Optional[MCPClient]:
|
|
344
|
+
return self.mcp_clients.get(server_name)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# Convenience functions for different server types
|
|
348
|
+
def create_local_mcp_server(
|
|
349
|
+
name: str,
|
|
350
|
+
script_path: Union[str, Path],
|
|
351
|
+
interpreter: str = "python",
|
|
352
|
+
**kwargs
|
|
353
|
+
) -> MCPServerConfig:
|
|
354
|
+
"""Create configuration for local stdio MCP server."""
|
|
355
|
+
script_path = Path(script_path)
|
|
356
|
+
if not script_path.exists():
|
|
357
|
+
raise FileNotFoundError(f"MCP server script not found: {script_path}")
|
|
358
|
+
|
|
359
|
+
return MCPServerConfig(
|
|
360
|
+
name=name,
|
|
361
|
+
command=interpreter,
|
|
362
|
+
args=[str(script_path)],
|
|
363
|
+
transport="stdio",
|
|
364
|
+
**kwargs
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def create_http_mcp_server(
|
|
369
|
+
name: str,
|
|
370
|
+
url: str,
|
|
371
|
+
auth_type: Optional[str] = None,
|
|
372
|
+
auth_config: Optional[Dict[str, Any]] = None,
|
|
373
|
+
headers: Optional[Dict[str, str]] = None,
|
|
374
|
+
**kwargs
|
|
375
|
+
) -> MCPServerConfig:
|
|
376
|
+
"""Create configuration for HTTP MCP server."""
|
|
377
|
+
return MCPServerConfig(
|
|
378
|
+
name=name,
|
|
379
|
+
url=url,
|
|
380
|
+
transport="http",
|
|
381
|
+
auth_type=auth_type,
|
|
382
|
+
auth_config=auth_config or {},
|
|
383
|
+
headers=headers or {},
|
|
384
|
+
**kwargs
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
def create_oauth_mcp_server(
|
|
388
|
+
*,
|
|
389
|
+
name: str,
|
|
390
|
+
url: str,
|
|
391
|
+
user_id: str,
|
|
392
|
+
client_id: str,
|
|
393
|
+
auth_url: str,
|
|
394
|
+
token_url: str,
|
|
395
|
+
scopes: list[str],
|
|
396
|
+
client_secret: str | None = None,
|
|
397
|
+
redis=None, # pass an aioredis client if you have it; else None -> in-memory
|
|
398
|
+
redirect_host: str = "127.0.0.1",
|
|
399
|
+
redirect_port: int = 8765,
|
|
400
|
+
redirect_path: str = "/mcp/oauth/callback",
|
|
401
|
+
extra_token_params: dict | None = None,
|
|
402
|
+
headers: dict | None = None,
|
|
403
|
+
) -> MCPServerConfig:
|
|
404
|
+
token_store = RedisTokenStore(redis) if redis else InMemoryTokenStore()
|
|
405
|
+
oauth = OAuthManager(
|
|
406
|
+
user_id=user_id,
|
|
407
|
+
server_name=name,
|
|
408
|
+
client_id=client_id,
|
|
409
|
+
client_secret=client_secret,
|
|
410
|
+
auth_url=auth_url,
|
|
411
|
+
token_url=token_url,
|
|
412
|
+
scopes=scopes,
|
|
413
|
+
redirect_host=redirect_host,
|
|
414
|
+
redirect_port=redirect_port,
|
|
415
|
+
redirect_path=redirect_path,
|
|
416
|
+
token_store=token_store,
|
|
417
|
+
extra_token_params=extra_token_params,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
cfg = MCPServerConfig(
|
|
421
|
+
name=name,
|
|
422
|
+
transport="http",
|
|
423
|
+
url=url,
|
|
424
|
+
headers=headers or {"Content-Type": "application/json"},
|
|
425
|
+
auth_type="oauth",
|
|
426
|
+
auth_config={
|
|
427
|
+
"auth_url": auth_url,
|
|
428
|
+
"token_url": token_url,
|
|
429
|
+
"scopes": scopes,
|
|
430
|
+
"client_id": client_id,
|
|
431
|
+
"client_secret": bool(client_secret),
|
|
432
|
+
"redirect_uri": oauth.redirect_uri,
|
|
433
|
+
},
|
|
434
|
+
token_supplier=oauth.token_supplier, # this is called before each request
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# Attach a small helper so the client can ensure token before using the server.
|
|
438
|
+
cfg._ensure_oauth_token = oauth.ensure_token # attribute on purpose
|
|
439
|
+
return cfg
|
|
440
|
+
|
|
441
|
+
def create_unix_mcp_server(
|
|
442
|
+
name: str,
|
|
443
|
+
socket_path: str,
|
|
444
|
+
**kwargs
|
|
445
|
+
) -> MCPServerConfig:
|
|
446
|
+
"""Create a Unix socket MCP server configuration.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
name: Server name
|
|
450
|
+
socket_path: Path to Unix socket
|
|
451
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
MCPServerConfig configured for Unix socket transport
|
|
455
|
+
|
|
456
|
+
Example:
|
|
457
|
+
>>> config = create_unix_mcp_server(
|
|
458
|
+
... "workday",
|
|
459
|
+
... "/tmp/parrot-mcp-workday.sock"
|
|
460
|
+
... )
|
|
461
|
+
>>> async with MCPClient(config) as client:
|
|
462
|
+
... tools = await client.list_tools()
|
|
463
|
+
"""
|
|
464
|
+
return MCPServerConfig(
|
|
465
|
+
name=name,
|
|
466
|
+
transport="unix",
|
|
467
|
+
socket_path=socket_path,
|
|
468
|
+
**kwargs
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def create_websocket_mcp_server(
|
|
473
|
+
name: str,
|
|
474
|
+
url: str,
|
|
475
|
+
auth_type: Optional[str] = None,
|
|
476
|
+
auth_config: Optional[Dict[str, Any]] = None,
|
|
477
|
+
headers: Optional[Dict[str, str]] = None,
|
|
478
|
+
**kwargs
|
|
479
|
+
) -> MCPServerConfig:
|
|
480
|
+
"""Create a WebSocket MCP server configuration.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
name: Server name
|
|
484
|
+
url: WebSocket URL (ws:// or wss://)
|
|
485
|
+
auth_type: Authentication type ("bearer", "api_key", "oauth", or None)
|
|
486
|
+
auth_config: Authentication configuration dict
|
|
487
|
+
headers: Additional HTTP headers for WebSocket upgrade
|
|
488
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
MCPServerConfig configured for WebSocket transport
|
|
492
|
+
|
|
493
|
+
Example:
|
|
494
|
+
>>> config = create_websocket_mcp_server(
|
|
495
|
+
... "my-ws-server",
|
|
496
|
+
... "ws://localhost:8766/mcp/ws",
|
|
497
|
+
... auth_type="bearer",
|
|
498
|
+
... auth_config={"token": "my-secret-token"}
|
|
499
|
+
... )
|
|
500
|
+
>>> async with MCPClient(config) as client:
|
|
501
|
+
... tools = await client.list_tools()
|
|
502
|
+
"""
|
|
503
|
+
return MCPServerConfig(
|
|
504
|
+
name=name,
|
|
505
|
+
url=url,
|
|
506
|
+
transport="websocket",
|
|
507
|
+
auth_type=auth_type,
|
|
508
|
+
auth_config=auth_config or {},
|
|
509
|
+
headers=headers or {},
|
|
510
|
+
**kwargs
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def create_api_key_mcp_server(
|
|
515
|
+
name: str,
|
|
516
|
+
url: str,
|
|
517
|
+
api_key: str,
|
|
518
|
+
header_name: str = "X-API-Key",
|
|
519
|
+
use_bearer_prefix: bool = False,
|
|
520
|
+
**kwargs
|
|
521
|
+
) -> MCPServerConfig:
|
|
522
|
+
"""Create configuration for API key authenticated MCP server.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
name: Unique name for the MCP server
|
|
526
|
+
url: Base URL of the MCP server
|
|
527
|
+
api_key: API key for authentication
|
|
528
|
+
header_name: Header name for the API key (default: "X-API-Key")
|
|
529
|
+
use_bearer_prefix: If True, prepend "Bearer " to the API key value (default: False)
|
|
530
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
MCPServerConfig instance
|
|
534
|
+
"""
|
|
535
|
+
return create_http_mcp_server(
|
|
536
|
+
name=name,
|
|
537
|
+
url=url,
|
|
538
|
+
auth_type="api_key",
|
|
539
|
+
auth_config={
|
|
540
|
+
"api_key": api_key,
|
|
541
|
+
"header_name": header_name,
|
|
542
|
+
"use_bearer_prefix": use_bearer_prefix
|
|
543
|
+
},
|
|
544
|
+
**kwargs
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def create_fireflies_mcp_server(
|
|
549
|
+
*,
|
|
550
|
+
api_key: str,
|
|
551
|
+
api_base: str = "https://api.fireflies.ai/mcp",
|
|
552
|
+
**kwargs
|
|
553
|
+
) -> MCPServerConfig:
|
|
554
|
+
"""Create configuration for Fireflies MCP server using stdio transport.
|
|
555
|
+
|
|
556
|
+
Fireflies MCP requires using npx mcp-remote as a command-line proxy.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
api_key: Fireflies API key
|
|
560
|
+
api_base: Base URL of the Fireflies MCP endpoint
|
|
561
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
MCPServerConfig instance configured for stdio transport
|
|
565
|
+
"""
|
|
566
|
+
return MCPServerConfig(
|
|
567
|
+
name="fireflies",
|
|
568
|
+
command="npx",
|
|
569
|
+
args=[
|
|
570
|
+
"mcp-remote",
|
|
571
|
+
api_base,
|
|
572
|
+
"--header",
|
|
573
|
+
f"Authorization: Bearer {api_key}"
|
|
574
|
+
],
|
|
575
|
+
transport="stdio",
|
|
576
|
+
**kwargs
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def create_perplexity_mcp_server(
|
|
581
|
+
api_key: str,
|
|
582
|
+
*,
|
|
583
|
+
name: str = "perplexity",
|
|
584
|
+
timeout_ms: int = 600000,
|
|
585
|
+
**kwargs
|
|
586
|
+
) -> MCPServerConfig:
|
|
587
|
+
"""Create configuration for Perplexity MCP server.
|
|
588
|
+
|
|
589
|
+
The Perplexity MCP server provides 4 tools:
|
|
590
|
+
- perplexity_search: Direct web search via Search API
|
|
591
|
+
- perplexity_ask: Conversational AI with sonar-pro model
|
|
592
|
+
- perplexity_research: Deep research with sonar-deep-research
|
|
593
|
+
- perplexity_reason: Advanced reasoning with sonar-reasoning-pro
|
|
594
|
+
|
|
595
|
+
Args:
|
|
596
|
+
api_key: Perplexity API key (get from perplexity.ai/account/api)
|
|
597
|
+
name: Server name for tool prefixing
|
|
598
|
+
timeout_ms: Request timeout (default 600000ms for deep research)
|
|
599
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
MCPServerConfig configured for Perplexity
|
|
603
|
+
|
|
604
|
+
Example:
|
|
605
|
+
>>> config = create_perplexity_mcp_server(
|
|
606
|
+
... api_key=os.environ["PERPLEXITY_API_KEY"]
|
|
607
|
+
... )
|
|
608
|
+
>>> await agent.add_mcp_server(config)
|
|
609
|
+
"""
|
|
610
|
+
return MCPServerConfig(
|
|
611
|
+
name=name,
|
|
612
|
+
transport="stdio",
|
|
613
|
+
command="npx",
|
|
614
|
+
args=["-y", "@perplexity-ai/mcp-server"],
|
|
615
|
+
env={
|
|
616
|
+
"PERPLEXITY_API_KEY": api_key or os.environ.get("PERPLEXITY_API_KEY"),
|
|
617
|
+
"PERPLEXITY_TIMEOUT_MS": str(timeout_ms),
|
|
618
|
+
},
|
|
619
|
+
startup_delay=3.0, # npx needs time to fetch/start
|
|
620
|
+
**kwargs
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
def create_quic_mcp_server(
|
|
624
|
+
name: str,
|
|
625
|
+
host: str,
|
|
626
|
+
port: int,
|
|
627
|
+
cert_path: Optional[str] = None,
|
|
628
|
+
serialization: str = "msgpack",
|
|
629
|
+
**kwargs
|
|
630
|
+
) -> MCPServerConfig:
|
|
631
|
+
"""Create configuration for QUIC MCP server.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
name: Server name
|
|
635
|
+
host: Server hostname
|
|
636
|
+
port: Server port
|
|
637
|
+
cert_path: Path to TLS certificate (optional for client if trusted)
|
|
638
|
+
serialization: Serialization format ("msgpack" or "json")
|
|
639
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
640
|
+
|
|
641
|
+
Returns:
|
|
642
|
+
MCPServerConfig configured for QUIC transport
|
|
643
|
+
"""
|
|
644
|
+
quic_fmt = SerializationFormat.MSGPACK
|
|
645
|
+
if serialization.lower() == "json":
|
|
646
|
+
quic_fmt = SerializationFormat.JSON
|
|
647
|
+
|
|
648
|
+
quic_conf = QuicMCPConfig(
|
|
649
|
+
host=host,
|
|
650
|
+
port=port,
|
|
651
|
+
cert_path=cert_path,
|
|
652
|
+
serialization=quic_fmt,
|
|
653
|
+
# Default efficient settings
|
|
654
|
+
enable_0rtt=True,
|
|
655
|
+
enable_webtransport=True
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
return MCPServerConfig(
|
|
659
|
+
name=name,
|
|
660
|
+
transport="quic",
|
|
661
|
+
quic_config=quic_conf,
|
|
662
|
+
**kwargs
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
# Extension for BaseAgent
|
|
666
|
+
class MCPEnabledMixin:
|
|
667
|
+
"""Mixin to add complete MCP capabilities to agents."""
|
|
668
|
+
|
|
669
|
+
def __init__(self, *args, **kwargs):
|
|
670
|
+
super().__init__(*args, **kwargs)
|
|
671
|
+
self.mcp_manager = MCPToolManager(self.tool_manager)
|
|
672
|
+
|
|
673
|
+
async def add_mcp_server(self, config: MCPServerConfig) -> List[str]:
|
|
674
|
+
"""Add an MCP server with full feature support."""
|
|
675
|
+
return await self.mcp_manager.add_mcp_server(config)
|
|
676
|
+
|
|
677
|
+
async def add_local_mcp_server(
|
|
678
|
+
self,
|
|
679
|
+
name: str,
|
|
680
|
+
script_path: Union[str, Path],
|
|
681
|
+
interpreter: str = "python",
|
|
682
|
+
**kwargs
|
|
683
|
+
) -> List[str]:
|
|
684
|
+
"""Add a local stdio MCP server."""
|
|
685
|
+
config = create_local_mcp_server(name, script_path, interpreter, **kwargs)
|
|
686
|
+
return await self.add_mcp_server(config)
|
|
687
|
+
|
|
688
|
+
async def add_http_mcp_server(
|
|
689
|
+
self,
|
|
690
|
+
name: str,
|
|
691
|
+
url: str,
|
|
692
|
+
auth_type: Optional[str] = None,
|
|
693
|
+
auth_config: Optional[Dict[str, Any]] = None,
|
|
694
|
+
headers: Optional[Dict[str, str]] = None,
|
|
695
|
+
**kwargs
|
|
696
|
+
) -> List[str]:
|
|
697
|
+
"""Add an HTTP MCP server."""
|
|
698
|
+
config = create_http_mcp_server(name, url, auth_type, auth_config, headers, **kwargs)
|
|
699
|
+
return await self.add_mcp_server(config)
|
|
700
|
+
|
|
701
|
+
async def add_perplexity_mcp_server(
|
|
702
|
+
self,
|
|
703
|
+
api_key: str,
|
|
704
|
+
name: str = "perplexity",
|
|
705
|
+
**kwargs
|
|
706
|
+
) -> List[str]:
|
|
707
|
+
"""Add a Perplexity MCP server capability."""
|
|
708
|
+
config = create_perplexity_mcp_server(api_key, name=name, **kwargs)
|
|
709
|
+
return await self.add_mcp_server(config)
|
|
710
|
+
|
|
711
|
+
async def add_fireflies_mcp_server(
|
|
712
|
+
self,
|
|
713
|
+
api_key: str,
|
|
714
|
+
**kwargs
|
|
715
|
+
) -> List[str]:
|
|
716
|
+
"""Add Fireflies.ai MCP server capability.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
api_key: Fireflies API key from Settings > Developer Settings
|
|
720
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
List of registered tool names
|
|
724
|
+
|
|
725
|
+
Example:
|
|
726
|
+
>>> tools = await agent.add_fireflies_mcp_server(
|
|
727
|
+
... api_key="your-fireflies-api-key"
|
|
728
|
+
... )
|
|
729
|
+
"""
|
|
730
|
+
config = create_fireflies_mcp_server(api_key=api_key, **kwargs)
|
|
731
|
+
return await self.add_mcp_server(config)
|
|
732
|
+
|
|
733
|
+
async def add_quic_mcp_server(
|
|
734
|
+
self,
|
|
735
|
+
name: str,
|
|
736
|
+
host: str,
|
|
737
|
+
port: int,
|
|
738
|
+
cert_path: Optional[str] = None,
|
|
739
|
+
**kwargs
|
|
740
|
+
) -> List[str]:
|
|
741
|
+
"""Add a QUIC/HTTP3 MCP server connection."""
|
|
742
|
+
config = create_quic_mcp_server(name, host, port, cert_path, **kwargs)
|
|
743
|
+
return await self.add_mcp_server(config)
|
|
744
|
+
|
|
745
|
+
async def add_websocket_mcp_server(
|
|
746
|
+
self,
|
|
747
|
+
name: str,
|
|
748
|
+
url: str,
|
|
749
|
+
auth_type: Optional[str] = None,
|
|
750
|
+
auth_config: Optional[Dict[str, Any]] = None,
|
|
751
|
+
headers: Optional[Dict[str, str]] = None,
|
|
752
|
+
**kwargs
|
|
753
|
+
) -> List[str]:
|
|
754
|
+
"""Add a WebSocket MCP server connection.
|
|
755
|
+
|
|
756
|
+
Args:
|
|
757
|
+
name: Server name
|
|
758
|
+
url: WebSocket URL (ws:// or wss://)
|
|
759
|
+
auth_type: Authentication type ("bearer", "api_key", "oauth")
|
|
760
|
+
auth_config: Authentication configuration
|
|
761
|
+
headers: Additional headers for WebSocket upgrade
|
|
762
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
763
|
+
|
|
764
|
+
Returns:
|
|
765
|
+
List of registered tool names
|
|
766
|
+
|
|
767
|
+
Example:
|
|
768
|
+
>>> await agent.add_websocket_mcp_server(
|
|
769
|
+
... "my-ws-server",
|
|
770
|
+
... "ws://localhost:8766/mcp/ws",
|
|
771
|
+
... auth_type="bearer",
|
|
772
|
+
... auth_config={"token": "my-token"}
|
|
773
|
+
... )
|
|
774
|
+
"""
|
|
775
|
+
config = create_websocket_mcp_server(
|
|
776
|
+
name, url, auth_type, auth_config, headers, **kwargs
|
|
777
|
+
)
|
|
778
|
+
return await self.add_mcp_server(config)
|
|
779
|
+
|
|
780
|
+
async def remove_mcp_server(self, server_name: str):
|
|
781
|
+
await self.mcp_manager.remove_mcp_server(server_name)
|
|
782
|
+
|
|
783
|
+
async def reconfigure_mcp_server(self, config: MCPServerConfig) -> List[str]:
|
|
784
|
+
"""Reconfigure an existing MCP server with new configuration.
|
|
785
|
+
|
|
786
|
+
Args:
|
|
787
|
+
config: New MCPServerConfig with updated parameters
|
|
788
|
+
|
|
789
|
+
Returns:
|
|
790
|
+
List of registered tool names
|
|
791
|
+
"""
|
|
792
|
+
return await self.mcp_manager.reconfigure_mcp_server(config)
|
|
793
|
+
|
|
794
|
+
async def reconfigure_fireflies_mcp_server(self, api_key: str, **kwargs) -> List[str]:
|
|
795
|
+
"""Reconfigure Fireflies MCP server with a new API key.
|
|
796
|
+
|
|
797
|
+
This is useful in multi-user scenarios where each user provides their own
|
|
798
|
+
Fireflies API key. The method will disconnect the existing connection and
|
|
799
|
+
reconnect with the new credentials.
|
|
800
|
+
|
|
801
|
+
Args:
|
|
802
|
+
api_key: New Fireflies API key
|
|
803
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
804
|
+
|
|
805
|
+
Returns:
|
|
806
|
+
List of registered tool names
|
|
807
|
+
|
|
808
|
+
Example:
|
|
809
|
+
>>> # Initial setup with user 1's API key
|
|
810
|
+
>>> await agent.add_fireflies_mcp_server(api_key="user1-api-key")
|
|
811
|
+
|
|
812
|
+
>>> # Later, reconfigure with user 2's API key
|
|
813
|
+
>>> await agent.reconfigure_fireflies_mcp_server(api_key="user2-api-key")
|
|
814
|
+
"""
|
|
815
|
+
config = create_fireflies_mcp_server(api_key=api_key, **kwargs)
|
|
816
|
+
return await self.reconfigure_mcp_server(config)
|
|
817
|
+
|
|
818
|
+
async def reconfigure_perplexity_mcp_server(self, api_key: str, name: str = "perplexity", **kwargs) -> List[str]:
|
|
819
|
+
"""Reconfigure Perplexity MCP server with a new API key.
|
|
820
|
+
|
|
821
|
+
Useful for updating the API key without restarting the agent.
|
|
822
|
+
|
|
823
|
+
Args:
|
|
824
|
+
api_key: New Perplexity API key
|
|
825
|
+
name: Server name (default: "perplexity")
|
|
826
|
+
**kwargs: Additional MCPServerConfig parameters
|
|
827
|
+
|
|
828
|
+
Returns:
|
|
829
|
+
List of registered tool names
|
|
830
|
+
"""
|
|
831
|
+
config = create_perplexity_mcp_server(api_key, name=name, **kwargs)
|
|
832
|
+
return await self.reconfigure_mcp_server(config)
|
|
833
|
+
|
|
834
|
+
def list_mcp_servers(self) -> List[str]:
|
|
835
|
+
return self.mcp_manager.list_mcp_servers()
|
|
836
|
+
|
|
837
|
+
async def shutdown(self, **kwargs):
|
|
838
|
+
if hasattr(self, 'mcp_manager'):
|
|
839
|
+
await self.mcp_manager.disconnect_all()
|
|
840
|
+
|
|
841
|
+
if hasattr(super(), 'shutdown'):
|
|
842
|
+
await super().shutdown(**kwargs)
|