docagent-cli 0.0.35__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docagent_cli/__init__.py +36 -0
- docagent_cli/__main__.py +6 -0
- docagent_cli/_ask_user_types.py +90 -0
- docagent_cli/_cli_context.py +27 -0
- docagent_cli/_debug.py +52 -0
- docagent_cli/_env_vars.py +56 -0
- docagent_cli/_server_config.py +352 -0
- docagent_cli/_session_stats.py +114 -0
- docagent_cli/_testing_models.py +144 -0
- docagent_cli/_version.py +17 -0
- docagent_cli/agent.py +1193 -0
- docagent_cli/app.py +4979 -0
- docagent_cli/app.tcss +283 -0
- docagent_cli/ask_user.py +301 -0
- docagent_cli/built_in_skills/__init__.py +5 -0
- docagent_cli/built_in_skills/doc-coauthoring/SKILL.md +375 -0
- docagent_cli/built_in_skills/docx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/docx/SKILL.md +590 -0
- docagent_cli/built_in_skills/docx/scripts/__init__.py +1 -0
- docagent_cli/built_in_skills/docx/scripts/accept_changes.py +135 -0
- docagent_cli/built_in_skills/docx/scripts/comment.py +318 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/docx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/docx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/docx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/docx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/docx/scripts/templates/comments.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtended.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsIds.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/people.xml +3 -0
- docagent_cli/built_in_skills/pdf/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pdf/SKILL.md +314 -0
- docagent_cli/built_in_skills/pdf/forms.md +294 -0
- docagent_cli/built_in_skills/pdf/reference.md +612 -0
- docagent_cli/built_in_skills/pdf/scripts/check_bounding_boxes.py +65 -0
- docagent_cli/built_in_skills/pdf/scripts/check_fillable_fields.py +11 -0
- docagent_cli/built_in_skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- docagent_cli/built_in_skills/pdf/scripts/create_validation_image.py +37 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_field_info.py +122 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_structure.py +115 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_fillable_fields.py +98 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- docagent_cli/built_in_skills/pptx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pptx/SKILL.md +232 -0
- docagent_cli/built_in_skills/pptx/editing.md +205 -0
- docagent_cli/built_in_skills/pptx/pptxgenjs.md +420 -0
- docagent_cli/built_in_skills/pptx/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/add_slide.py +195 -0
- docagent_cli/built_in_skills/pptx/scripts/clean.py +286 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/pptx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/pptx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/pptx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/pptx/scripts/thumbnail.py +289 -0
- docagent_cli/built_in_skills/remember/SKILL.md +118 -0
- docagent_cli/built_in_skills/skill-creator/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/skill-creator/SKILL.md +485 -0
- docagent_cli/built_in_skills/skill-creator/agents/analyzer.md +274 -0
- docagent_cli/built_in_skills/skill-creator/agents/comparator.md +202 -0
- docagent_cli/built_in_skills/skill-creator/agents/grader.md +223 -0
- docagent_cli/built_in_skills/skill-creator/assets/eval_review.html +146 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/generate_review.py +471 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/viewer.html +1325 -0
- docagent_cli/built_in_skills/skill-creator/references/schemas.md +430 -0
- docagent_cli/built_in_skills/skill-creator/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- docagent_cli/built_in_skills/skill-creator/scripts/generate_report.py +326 -0
- docagent_cli/built_in_skills/skill-creator/scripts/improve_description.py +247 -0
- docagent_cli/built_in_skills/skill-creator/scripts/package_skill.py +136 -0
- docagent_cli/built_in_skills/skill-creator/scripts/quick_validate.py +103 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_eval.py +310 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_loop.py +328 -0
- docagent_cli/built_in_skills/skill-creator/scripts/utils.py +47 -0
- docagent_cli/built_in_skills/theme-factory/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/theme-factory/SKILL.md +59 -0
- docagent_cli/built_in_skills/theme-factory/theme-showcase.pdf +0 -0
- docagent_cli/built_in_skills/theme-factory/themes/arctic-frost.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/botanical-garden.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/desert-rose.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/forest-canopy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/golden-hour.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/midnight-galaxy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/modern-minimalist.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/ocean-depths.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/sunset-boulevard.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/tech-innovation.md +19 -0
- docagent_cli/built_in_skills/xlsx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/xlsx/SKILL.md +292 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/xlsx/scripts/recalc.py +184 -0
- docagent_cli/clipboard.py +128 -0
- docagent_cli/command_registry.py +284 -0
- docagent_cli/config.py +2418 -0
- docagent_cli/configurable_model.py +162 -0
- docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli/editor.py +142 -0
- docagent_cli/file_ops.py +473 -0
- docagent_cli/formatting.py +28 -0
- docagent_cli/hooks.py +206 -0
- docagent_cli/input.py +787 -0
- docagent_cli/integrations/__init__.py +1 -0
- docagent_cli/integrations/sandbox_factory.py +873 -0
- docagent_cli/integrations/sandbox_provider.py +71 -0
- docagent_cli/local_context.py +718 -0
- docagent_cli/main.py +1641 -0
- docagent_cli/mcp_tools.py +707 -0
- docagent_cli/mcp_trust.py +168 -0
- docagent_cli/media_utils.py +478 -0
- docagent_cli/model_config.py +1620 -0
- docagent_cli/non_interactive.py +948 -0
- docagent_cli/offload.py +371 -0
- docagent_cli/output.py +69 -0
- docagent_cli/project_utils.py +188 -0
- docagent_cli/py.typed +0 -0
- docagent_cli/remote_client.py +515 -0
- docagent_cli/server.py +520 -0
- docagent_cli/server_graph.py +196 -0
- docagent_cli/server_manager.py +365 -0
- docagent_cli/sessions.py +1262 -0
- docagent_cli/skills/__init__.py +18 -0
- docagent_cli/skills/commands.py +1090 -0
- docagent_cli/skills/load.py +192 -0
- docagent_cli/subagents.py +173 -0
- docagent_cli/system_prompt.md +247 -0
- docagent_cli/textual_adapter.py +1352 -0
- docagent_cli/theme.py +842 -0
- docagent_cli/token_state.py +31 -0
- docagent_cli/tool_display.py +298 -0
- docagent_cli/tools.py +236 -0
- docagent_cli/ui.py +420 -0
- docagent_cli/unicode_security.py +516 -0
- docagent_cli/update_check.py +454 -0
- docagent_cli/widgets/__init__.py +9 -0
- docagent_cli/widgets/_links.py +63 -0
- docagent_cli/widgets/approval.py +442 -0
- docagent_cli/widgets/ask_user.py +398 -0
- docagent_cli/widgets/autocomplete.py +691 -0
- docagent_cli/widgets/chat_input.py +1827 -0
- docagent_cli/widgets/diff.py +248 -0
- docagent_cli/widgets/history.py +188 -0
- docagent_cli/widgets/loading.py +177 -0
- docagent_cli/widgets/mcp_viewer.py +362 -0
- docagent_cli/widgets/message_store.py +675 -0
- docagent_cli/widgets/messages.py +1751 -0
- docagent_cli/widgets/model_selector.py +964 -0
- docagent_cli/widgets/status.py +372 -0
- docagent_cli/widgets/theme_selector.py +164 -0
- docagent_cli/widgets/thread_selector.py +1905 -0
- docagent_cli/widgets/tool_renderers.py +148 -0
- docagent_cli/widgets/tool_widgets.py +274 -0
- docagent_cli/widgets/welcome.py +339 -0
- docagent_cli-0.0.35.data/data/docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli-0.0.35.dist-info/METADATA +200 -0
- docagent_cli-0.0.35.dist-info/RECORD +300 -0
- docagent_cli-0.0.35.dist-info/WHEEL +4 -0
- docagent_cli-0.0.35.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) tools loader for docagent CLI.
|
|
2
|
+
|
|
3
|
+
This module provides async functions to load and manage MCP servers using
|
|
4
|
+
`langchain-mcp-adapters`, supporting Claude Desktop style JSON configs.
|
|
5
|
+
It also supports automatic discovery of `.mcp.json` files from user-level
|
|
6
|
+
and project-level locations.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import shutil
|
|
14
|
+
from contextlib import AsyncExitStack
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from langchain_core.tools import BaseTool
|
|
21
|
+
from langchain_mcp_adapters.client import Connection, MultiServerMCPClient
|
|
22
|
+
|
|
23
|
+
from docagent_cli.project_utils import ProjectContext
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class MCPToolInfo:
|
|
30
|
+
"""Metadata for a single MCP tool."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
"""Tool name (may include server name prefix)."""
|
|
34
|
+
|
|
35
|
+
description: str
|
|
36
|
+
"""Human-readable description of what the tool does."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class MCPServerInfo:
|
|
41
|
+
"""Metadata for a connected MCP server and its tools."""
|
|
42
|
+
|
|
43
|
+
name: str
|
|
44
|
+
"""Server name from the MCP configuration."""
|
|
45
|
+
|
|
46
|
+
transport: str
|
|
47
|
+
"""Transport type (`stdio`, `sse`, or `http`)."""
|
|
48
|
+
|
|
49
|
+
tools: list[MCPToolInfo] = field(default_factory=list)
|
|
50
|
+
"""Tools exposed by this server."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_SUPPORTED_REMOTE_TYPES = {"sse", "http"}
|
|
54
|
+
"""Supported transport types for remote MCP servers (SSE and HTTP)."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _resolve_server_type(server_config: dict[str, Any]) -> str:
|
|
58
|
+
"""Determine the transport type for a server config.
|
|
59
|
+
|
|
60
|
+
Supports both `type` and `transport` field names, defaulting to `stdio`.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
server_config: Server configuration dictionary.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Transport type string (`stdio`, `sse`, or `http`).
|
|
67
|
+
"""
|
|
68
|
+
t = server_config.get("type")
|
|
69
|
+
if t is not None:
|
|
70
|
+
return t
|
|
71
|
+
return server_config.get("transport", "stdio")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _validate_server_config(server_name: str, server_config: dict[str, Any]) -> None:
|
|
75
|
+
"""Validate a single server configuration.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
server_name: Name of the server.
|
|
79
|
+
server_config: Server configuration dictionary.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
TypeError: If config fields have wrong types.
|
|
83
|
+
ValueError: If required fields are missing or server type is unsupported.
|
|
84
|
+
"""
|
|
85
|
+
if not isinstance(server_config, dict):
|
|
86
|
+
error_msg = f"Server '{server_name}' config must be a dictionary"
|
|
87
|
+
raise TypeError(error_msg)
|
|
88
|
+
|
|
89
|
+
server_type = _resolve_server_type(server_config)
|
|
90
|
+
|
|
91
|
+
if server_type in _SUPPORTED_REMOTE_TYPES:
|
|
92
|
+
# SSE/HTTP server validation - requires url field
|
|
93
|
+
if "url" not in server_config:
|
|
94
|
+
error_msg = (
|
|
95
|
+
f"Server '{server_name}' with type '{server_type}'"
|
|
96
|
+
" missing required 'url' field"
|
|
97
|
+
)
|
|
98
|
+
raise ValueError(error_msg)
|
|
99
|
+
|
|
100
|
+
# headers is optional but must be correct type if present
|
|
101
|
+
headers = server_config.get("headers")
|
|
102
|
+
if headers is not None and not isinstance(headers, dict):
|
|
103
|
+
error_msg = f"Server '{server_name}' 'headers' must be a dictionary"
|
|
104
|
+
raise TypeError(error_msg)
|
|
105
|
+
elif server_type == "stdio":
|
|
106
|
+
# stdio server validation
|
|
107
|
+
if "command" not in server_config:
|
|
108
|
+
error_msg = f"Server '{server_name}' missing required 'command' field"
|
|
109
|
+
raise ValueError(error_msg)
|
|
110
|
+
|
|
111
|
+
# args and env are optional but must be correct type if present
|
|
112
|
+
if "args" in server_config and not isinstance(server_config["args"], list):
|
|
113
|
+
error_msg = f"Server '{server_name}' 'args' must be a list"
|
|
114
|
+
raise TypeError(error_msg)
|
|
115
|
+
|
|
116
|
+
if "env" in server_config and not isinstance(server_config["env"], dict):
|
|
117
|
+
error_msg = f"Server '{server_name}' 'env' must be a dictionary"
|
|
118
|
+
raise TypeError(error_msg)
|
|
119
|
+
else:
|
|
120
|
+
error_msg = (
|
|
121
|
+
f"Server '{server_name}' has unsupported transport type '{server_type}'. "
|
|
122
|
+
"Supported types: stdio, sse, http"
|
|
123
|
+
)
|
|
124
|
+
raise ValueError(error_msg)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def load_mcp_config(config_path: str) -> dict[str, Any]:
|
|
128
|
+
"""Load and validate MCP configuration from JSON file.
|
|
129
|
+
|
|
130
|
+
Supports multiple server types:
|
|
131
|
+
|
|
132
|
+
- stdio: Process-based servers with `command`, `args`, `env` fields (default)
|
|
133
|
+
- sse: Server-Sent Events servers with `type: "sse"`, `url`, and optional `headers`
|
|
134
|
+
- http: HTTP-based servers with `type: "http"`, `url`, and optional `headers`
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
config_path: Path to MCP JSON configuration file (Claude Desktop format).
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Parsed configuration dictionary.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
FileNotFoundError: If config file doesn't exist.
|
|
144
|
+
json.JSONDecodeError: If config file contains invalid JSON.
|
|
145
|
+
TypeError: If config fields have wrong types.
|
|
146
|
+
ValueError: If config is missing required fields.
|
|
147
|
+
"""
|
|
148
|
+
path = Path(config_path)
|
|
149
|
+
|
|
150
|
+
if not path.exists():
|
|
151
|
+
error_msg = f"MCP config file not found: {config_path}"
|
|
152
|
+
raise FileNotFoundError(error_msg)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
with path.open(encoding="utf-8") as f:
|
|
156
|
+
config = json.load(f)
|
|
157
|
+
except json.JSONDecodeError as e:
|
|
158
|
+
error_msg = f"Invalid JSON in MCP config file: {e.msg}"
|
|
159
|
+
raise json.JSONDecodeError(error_msg, e.doc, e.pos) from e
|
|
160
|
+
|
|
161
|
+
# Validate required fields
|
|
162
|
+
if "mcpServers" not in config:
|
|
163
|
+
error_msg = (
|
|
164
|
+
"MCP config must contain 'mcpServers' field. "
|
|
165
|
+
'Expected format: {"mcpServers": {"server-name": {...}}}'
|
|
166
|
+
)
|
|
167
|
+
raise ValueError(error_msg)
|
|
168
|
+
|
|
169
|
+
if not isinstance(config["mcpServers"], dict):
|
|
170
|
+
error_msg = "'mcpServers' field must be a dictionary"
|
|
171
|
+
raise TypeError(error_msg)
|
|
172
|
+
|
|
173
|
+
if not config["mcpServers"]:
|
|
174
|
+
error_msg = "'mcpServers' field is empty - no servers configured"
|
|
175
|
+
raise ValueError(error_msg)
|
|
176
|
+
|
|
177
|
+
# Validate each server config
|
|
178
|
+
for server_name, server_config in config["mcpServers"].items():
|
|
179
|
+
_validate_server_config(server_name, server_config)
|
|
180
|
+
|
|
181
|
+
return config
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _resolve_project_config_base(project_context: ProjectContext | None) -> Path:
|
|
185
|
+
"""Resolve the base directory for project-level MCP configuration lookup.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
project_context: Explicit project path context, if available.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Project root when one exists, otherwise the user working directory.
|
|
192
|
+
"""
|
|
193
|
+
if project_context is not None:
|
|
194
|
+
return project_context.project_root or project_context.user_cwd
|
|
195
|
+
|
|
196
|
+
from docagent_cli.project_utils import find_project_root
|
|
197
|
+
|
|
198
|
+
return find_project_root() or Path.cwd()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def discover_mcp_configs(
|
|
202
|
+
*, project_context: ProjectContext | None = None
|
|
203
|
+
) -> list[Path]:
|
|
204
|
+
"""Find MCP config files from standard locations.
|
|
205
|
+
|
|
206
|
+
Checks three paths in precedence order (lowest to highest):
|
|
207
|
+
|
|
208
|
+
1. `~/.docagent/.mcp.json` (user-level global)
|
|
209
|
+
2. `<project-root>/.docagent/.mcp.json` (project subdir)
|
|
210
|
+
3. `<project-root>/.mcp.json` (project root, Claude Code compat)
|
|
211
|
+
|
|
212
|
+
Project root is determined from `project_context` when provided, otherwise
|
|
213
|
+
by `find_project_root()`, falling back to CWD.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
List of existing config file paths, ordered lowest-to-highest precedence.
|
|
217
|
+
"""
|
|
218
|
+
user_dir = Path.home() / ".docagent"
|
|
219
|
+
project_root = _resolve_project_config_base(project_context)
|
|
220
|
+
|
|
221
|
+
candidates = [
|
|
222
|
+
user_dir / ".mcp.json",
|
|
223
|
+
project_root / ".docagent" / ".mcp.json",
|
|
224
|
+
project_root / ".mcp.json",
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
found: list[Path] = []
|
|
228
|
+
for path in candidates:
|
|
229
|
+
try:
|
|
230
|
+
if path.is_file():
|
|
231
|
+
found.append(path)
|
|
232
|
+
except OSError:
|
|
233
|
+
logger.warning("Could not check MCP config %s", path, exc_info=True)
|
|
234
|
+
return found
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def classify_discovered_configs(
|
|
238
|
+
config_paths: list[Path],
|
|
239
|
+
) -> tuple[list[Path], list[Path]]:
|
|
240
|
+
"""Split discovered config paths into user-level and project-level.
|
|
241
|
+
|
|
242
|
+
User-level configs live under `~/.docagent/`. Everything else is
|
|
243
|
+
considered project-level.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
config_paths: Paths returned by `discover_mcp_configs`.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Tuple of `(user_configs, project_configs)`.
|
|
250
|
+
"""
|
|
251
|
+
user_dir = Path.home() / ".docagent"
|
|
252
|
+
user: list[Path] = []
|
|
253
|
+
project: list[Path] = []
|
|
254
|
+
for path in config_paths:
|
|
255
|
+
try:
|
|
256
|
+
if path.resolve().is_relative_to(user_dir.resolve()):
|
|
257
|
+
user.append(path)
|
|
258
|
+
else:
|
|
259
|
+
project.append(path)
|
|
260
|
+
except (OSError, ValueError):
|
|
261
|
+
project.append(path)
|
|
262
|
+
return user, project
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def extract_stdio_server_commands(
|
|
266
|
+
config: dict[str, Any],
|
|
267
|
+
) -> list[tuple[str, str, list[str]]]:
|
|
268
|
+
"""Extract stdio server entries from a parsed MCP config.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
config: Parsed MCP config dict with `mcpServers` key.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
List of `(server_name, command, args)` for each stdio server.
|
|
275
|
+
"""
|
|
276
|
+
results: list[tuple[str, str, list[str]]] = []
|
|
277
|
+
servers = config.get("mcpServers", {})
|
|
278
|
+
if not isinstance(servers, dict):
|
|
279
|
+
return results
|
|
280
|
+
for name, srv in servers.items():
|
|
281
|
+
if not isinstance(srv, dict):
|
|
282
|
+
continue
|
|
283
|
+
if _resolve_server_type(srv) == "stdio":
|
|
284
|
+
results.append((name, srv.get("command", ""), srv.get("args", [])))
|
|
285
|
+
return results
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _filter_project_stdio_servers(config: dict[str, Any]) -> dict[str, Any]:
|
|
289
|
+
"""Return a copy of *config* with stdio servers removed.
|
|
290
|
+
|
|
291
|
+
Remote (SSE/HTTP) servers are kept because they don't execute local code.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
config: Parsed MCP config dict.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Filtered config dict.
|
|
298
|
+
"""
|
|
299
|
+
servers = config.get("mcpServers", {})
|
|
300
|
+
if not isinstance(servers, dict):
|
|
301
|
+
return config
|
|
302
|
+
filtered = {
|
|
303
|
+
name: srv
|
|
304
|
+
for name, srv in servers.items()
|
|
305
|
+
if isinstance(srv, dict) and _resolve_server_type(srv) != "stdio"
|
|
306
|
+
}
|
|
307
|
+
return {"mcpServers": filtered}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def merge_mcp_configs(configs: list[dict[str, Any]]) -> dict[str, Any]:
|
|
311
|
+
"""Merge multiple MCP config dicts by server name.
|
|
312
|
+
|
|
313
|
+
Later entries override earlier ones for the same server name
|
|
314
|
+
(simple `dict.update` on `mcpServers`).
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
configs: Ordered list of parsed config dicts (each with `mcpServers` key).
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Merged config with combined `mcpServers`.
|
|
321
|
+
"""
|
|
322
|
+
merged: dict[str, Any] = {}
|
|
323
|
+
for cfg in configs:
|
|
324
|
+
servers = cfg.get("mcpServers")
|
|
325
|
+
if isinstance(servers, dict):
|
|
326
|
+
merged.update(servers)
|
|
327
|
+
return {"mcpServers": merged}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def load_mcp_config_lenient(config_path: Path) -> dict[str, Any] | None:
|
|
331
|
+
"""Load an MCP config file, returning None on any error.
|
|
332
|
+
|
|
333
|
+
Wraps `load_mcp_config` with lenient error handling suitable for
|
|
334
|
+
auto-discovery. Missing files are skipped silently; parse and validation
|
|
335
|
+
errors are logged as warnings.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
config_path: Path to the MCP config file.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Parsed config dict, or None if the file is missing or invalid.
|
|
342
|
+
"""
|
|
343
|
+
try:
|
|
344
|
+
return load_mcp_config(str(config_path))
|
|
345
|
+
except FileNotFoundError:
|
|
346
|
+
return None
|
|
347
|
+
except OSError as e:
|
|
348
|
+
logger.warning("Skipping unreadable MCP config %s: %s", config_path, e)
|
|
349
|
+
return None
|
|
350
|
+
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
|
351
|
+
logger.warning("Skipping invalid MCP config %s: %s", config_path, e)
|
|
352
|
+
return None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class MCPSessionManager:
|
|
356
|
+
"""Manages persistent MCP sessions for stateful stdio servers.
|
|
357
|
+
|
|
358
|
+
This manager creates and maintains persistent sessions for stdio MCP
|
|
359
|
+
servers, preventing server restarts on every tool call. Sessions are kept
|
|
360
|
+
alive until explicitly cleaned up.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
def __init__(self) -> None:
|
|
364
|
+
"""Initialize the session manager."""
|
|
365
|
+
self.client: MultiServerMCPClient | None = None
|
|
366
|
+
self.exit_stack = AsyncExitStack()
|
|
367
|
+
|
|
368
|
+
async def cleanup(self) -> None:
|
|
369
|
+
"""Clean up all managed sessions and close connections."""
|
|
370
|
+
await self.exit_stack.aclose()
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _check_stdio_server(server_name: str, server_config: dict[str, Any]) -> None:
|
|
374
|
+
"""Verify that a stdio server's command exists on PATH.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
server_name: Name of the server (for error messages).
|
|
378
|
+
server_config: Server configuration dictionary with `command` key.
|
|
379
|
+
|
|
380
|
+
Raises:
|
|
381
|
+
RuntimeError: If the command is missing from config or not found on PATH.
|
|
382
|
+
"""
|
|
383
|
+
command = server_config.get("command")
|
|
384
|
+
if command is None:
|
|
385
|
+
msg = f"MCP server '{server_name}': missing 'command' in config."
|
|
386
|
+
raise RuntimeError(msg)
|
|
387
|
+
if shutil.which(command) is None:
|
|
388
|
+
msg = (
|
|
389
|
+
f"MCP server '{server_name}': command '{command}' not found on PATH. "
|
|
390
|
+
"Install it or check your MCP config."
|
|
391
|
+
)
|
|
392
|
+
raise RuntimeError(msg)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
async def _check_remote_server(server_name: str, server_config: dict[str, Any]) -> None:
|
|
396
|
+
"""Check network connectivity to a remote MCP server URL.
|
|
397
|
+
|
|
398
|
+
Sends a lightweight HEAD request with a 2-second timeout to detect DNS
|
|
399
|
+
failures, refused connections, and network timeouts early, before the MCP
|
|
400
|
+
session handshake. HTTP error responses (4xx, 5xx) are not treated as
|
|
401
|
+
failures — only transport errors, invalid URLs, and OS-level socket
|
|
402
|
+
errors raise.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
server_name: Name of the server (for error messages).
|
|
406
|
+
server_config: Server configuration dictionary with `url` key.
|
|
407
|
+
|
|
408
|
+
Raises:
|
|
409
|
+
RuntimeError: If the server URL is unreachable or invalid.
|
|
410
|
+
"""
|
|
411
|
+
import httpx
|
|
412
|
+
|
|
413
|
+
url = server_config.get("url")
|
|
414
|
+
if url is None:
|
|
415
|
+
msg = f"MCP server '{server_name}': missing 'url' in config."
|
|
416
|
+
raise RuntimeError(msg)
|
|
417
|
+
try:
|
|
418
|
+
async with httpx.AsyncClient() as client:
|
|
419
|
+
await client.head(url, timeout=2)
|
|
420
|
+
except (httpx.TransportError, httpx.InvalidURL, OSError) as exc:
|
|
421
|
+
msg = (
|
|
422
|
+
f"MCP server '{server_name}': URL '{url}' is unreachable: {exc}. "
|
|
423
|
+
"Check that the URL is correct and the server is running."
|
|
424
|
+
)
|
|
425
|
+
raise RuntimeError(msg) from exc
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
async def _load_tools_from_config(
|
|
429
|
+
config: dict[str, Any],
|
|
430
|
+
) -> tuple[list[BaseTool], MCPSessionManager, list[MCPServerInfo]]:
|
|
431
|
+
"""Build MCP connections from a validated config and load tools.
|
|
432
|
+
|
|
433
|
+
This is the shared implementation used by both `get_mcp_tools` (explicit
|
|
434
|
+
path) and `resolve_and_load_mcp_tools` (auto-discovery).
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
config: Validated MCP configuration dict with `mcpServers` key.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
Tuple of `(tools_list, session_manager, server_infos)`.
|
|
441
|
+
|
|
442
|
+
Raises:
|
|
443
|
+
RuntimeError: If MCP server fails to spawn or connect.
|
|
444
|
+
"""
|
|
445
|
+
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
446
|
+
from langchain_mcp_adapters.sessions import (
|
|
447
|
+
SSEConnection,
|
|
448
|
+
StdioConnection,
|
|
449
|
+
StreamableHttpConnection,
|
|
450
|
+
)
|
|
451
|
+
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
452
|
+
|
|
453
|
+
# Pre-flight health checks (best-effort early detection; the session setup
|
|
454
|
+
# below has its own error handling for TOCTOU races).
|
|
455
|
+
errors: list[str] = []
|
|
456
|
+
for server_name, server_config in config["mcpServers"].items():
|
|
457
|
+
server_type = _resolve_server_type(server_config)
|
|
458
|
+
try:
|
|
459
|
+
if server_type in _SUPPORTED_REMOTE_TYPES:
|
|
460
|
+
await _check_remote_server(server_name, server_config)
|
|
461
|
+
elif server_type == "stdio":
|
|
462
|
+
_check_stdio_server(server_name, server_config)
|
|
463
|
+
except RuntimeError as exc:
|
|
464
|
+
errors.append(str(exc))
|
|
465
|
+
if errors:
|
|
466
|
+
msg = "Pre-flight health check(s) failed:\n" + "\n".join(
|
|
467
|
+
f" - {e}" for e in errors
|
|
468
|
+
)
|
|
469
|
+
raise RuntimeError(msg)
|
|
470
|
+
|
|
471
|
+
# Create connections dict for MultiServerMCPClient
|
|
472
|
+
# Convert Claude Desktop format to langchain-mcp-adapters format
|
|
473
|
+
connections: dict[str, Connection] = {}
|
|
474
|
+
for server_name, server_config in config["mcpServers"].items():
|
|
475
|
+
server_type = _resolve_server_type(server_config)
|
|
476
|
+
|
|
477
|
+
if server_type in _SUPPORTED_REMOTE_TYPES:
|
|
478
|
+
# langchain-mcp-adapters uses "streamable_http" for HTTP transport
|
|
479
|
+
if server_type == "http":
|
|
480
|
+
conn: Connection = StreamableHttpConnection(
|
|
481
|
+
transport="streamable_http",
|
|
482
|
+
url=server_config["url"],
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
conn = SSEConnection(
|
|
486
|
+
transport="sse",
|
|
487
|
+
url=server_config["url"],
|
|
488
|
+
)
|
|
489
|
+
if "headers" in server_config:
|
|
490
|
+
conn["headers"] = server_config["headers"]
|
|
491
|
+
connections[server_name] = conn
|
|
492
|
+
else:
|
|
493
|
+
# stdio server connection (default)
|
|
494
|
+
connections[server_name] = StdioConnection(
|
|
495
|
+
command=server_config["command"],
|
|
496
|
+
args=server_config.get("args", []),
|
|
497
|
+
env=server_config.get("env") or None,
|
|
498
|
+
transport="stdio",
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
# Create session manager to track persistent sessions
|
|
502
|
+
manager = MCPSessionManager()
|
|
503
|
+
|
|
504
|
+
try:
|
|
505
|
+
client = MultiServerMCPClient(connections=connections)
|
|
506
|
+
manager.client = client
|
|
507
|
+
except Exception as e:
|
|
508
|
+
await manager.cleanup()
|
|
509
|
+
error_msg = f"Failed to initialize MCP client: {e}"
|
|
510
|
+
raise RuntimeError(error_msg) from e
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
all_tools: list[BaseTool] = []
|
|
514
|
+
server_infos: list[MCPServerInfo] = []
|
|
515
|
+
for server_name, server_config in config["mcpServers"].items():
|
|
516
|
+
session = await manager.exit_stack.enter_async_context(
|
|
517
|
+
client.session(server_name)
|
|
518
|
+
)
|
|
519
|
+
tools = await load_mcp_tools(
|
|
520
|
+
session, server_name=server_name, tool_name_prefix=True
|
|
521
|
+
)
|
|
522
|
+
all_tools.extend(tools)
|
|
523
|
+
server_infos.append(
|
|
524
|
+
MCPServerInfo(
|
|
525
|
+
name=server_name,
|
|
526
|
+
transport=_resolve_server_type(server_config),
|
|
527
|
+
tools=[
|
|
528
|
+
MCPToolInfo(name=t.name, description=t.description or "")
|
|
529
|
+
for t in tools
|
|
530
|
+
],
|
|
531
|
+
)
|
|
532
|
+
)
|
|
533
|
+
except Exception as e:
|
|
534
|
+
await manager.cleanup()
|
|
535
|
+
error_msg = (
|
|
536
|
+
f"Failed to load tools from MCP server '{server_name}': {e}\n"
|
|
537
|
+
"For stdio servers: Check that the command and args are correct,"
|
|
538
|
+
" and that the MCP server is installed"
|
|
539
|
+
" (e.g., run 'npx -y <package>' manually to test).\n"
|
|
540
|
+
"For sse/http servers: Check that the URL is correct"
|
|
541
|
+
" and the server is running."
|
|
542
|
+
)
|
|
543
|
+
raise RuntimeError(error_msg) from e
|
|
544
|
+
|
|
545
|
+
return all_tools, manager, server_infos
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
async def get_mcp_tools(
|
|
549
|
+
config_path: str,
|
|
550
|
+
) -> tuple[list[BaseTool], MCPSessionManager, list[MCPServerInfo]]:
|
|
551
|
+
"""Load MCP tools from configuration file with stateful sessions.
|
|
552
|
+
|
|
553
|
+
Supports multiple server types:
|
|
554
|
+
- stdio: Spawns MCP servers as subprocesses with persistent sessions
|
|
555
|
+
- sse/http: Connects to remote MCP servers via URL
|
|
556
|
+
|
|
557
|
+
For stdio servers, this creates persistent sessions that remain active
|
|
558
|
+
across tool calls, avoiding server restarts. Sessions are managed by
|
|
559
|
+
`MCPSessionManager` and should be cleaned up with
|
|
560
|
+
`session_manager.cleanup()` when done.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
config_path: Path to MCP JSON configuration file.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
Tuple of `(tools_list, session_manager, server_infos)` where:
|
|
567
|
+
- tools_list: List of LangChain `BaseTool` objects
|
|
568
|
+
- session_manager: `MCPSessionManager` instance
|
|
569
|
+
(call `cleanup()` when done)
|
|
570
|
+
- server_infos: List of `MCPServerInfo` with per-server metadata
|
|
571
|
+
"""
|
|
572
|
+
config = load_mcp_config(config_path)
|
|
573
|
+
return await _load_tools_from_config(config)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
async def resolve_and_load_mcp_tools(
|
|
577
|
+
*,
|
|
578
|
+
explicit_config_path: str | None = None,
|
|
579
|
+
no_mcp: bool = False,
|
|
580
|
+
trust_project_mcp: bool | None = None,
|
|
581
|
+
project_context: ProjectContext | None = None,
|
|
582
|
+
) -> tuple[list[BaseTool], MCPSessionManager | None, list[MCPServerInfo]]:
|
|
583
|
+
"""Resolve MCP config and load tools.
|
|
584
|
+
|
|
585
|
+
Auto-discovers configs from standard locations and merges them.
|
|
586
|
+
When `explicit_config_path` is provided it is added as the
|
|
587
|
+
highest-precedence source (errors in that file are fatal).
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
explicit_config_path: Extra config file to layer on top of
|
|
591
|
+
auto-discovered configs (highest precedence). Errors are
|
|
592
|
+
fatal.
|
|
593
|
+
no_mcp: If True, disable all MCP loading.
|
|
594
|
+
trust_project_mcp: Controls project-level stdio server trust:
|
|
595
|
+
|
|
596
|
+
- `True`: allow all project stdio servers (flag/prompt approved).
|
|
597
|
+
- `False`: filter out project stdio servers, log warning.
|
|
598
|
+
- `None` (default): check the persistent trust store; if the
|
|
599
|
+
fingerprint matches, allow; otherwise filter + warn.
|
|
600
|
+
project_context: Explicit project path context for config discovery
|
|
601
|
+
and trust resolution.
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
Tuple of `(tools_list, session_manager, server_infos)`.
|
|
605
|
+
|
|
606
|
+
When no tools are loaded, returns `([], None, [])`.
|
|
607
|
+
|
|
608
|
+
Raises:
|
|
609
|
+
RuntimeError: If an MCP server config is invalid or fails to
|
|
610
|
+
spawn/connect.
|
|
611
|
+
"""
|
|
612
|
+
if no_mcp:
|
|
613
|
+
return [], None, []
|
|
614
|
+
|
|
615
|
+
# Auto-discovery
|
|
616
|
+
try:
|
|
617
|
+
config_paths = discover_mcp_configs(project_context=project_context)
|
|
618
|
+
except (OSError, RuntimeError):
|
|
619
|
+
logger.warning("MCP config auto-discovery failed", exc_info=True)
|
|
620
|
+
config_paths = []
|
|
621
|
+
|
|
622
|
+
# Classify discovered configs and apply trust filtering
|
|
623
|
+
user_configs, project_configs = classify_discovered_configs(config_paths)
|
|
624
|
+
|
|
625
|
+
configs: list[dict[str, Any]] = []
|
|
626
|
+
|
|
627
|
+
# User-level configs are always trusted
|
|
628
|
+
for path in user_configs:
|
|
629
|
+
cfg = load_mcp_config_lenient(path)
|
|
630
|
+
if cfg is not None:
|
|
631
|
+
configs.append(cfg)
|
|
632
|
+
|
|
633
|
+
# Project-level configs need trust gating for stdio servers
|
|
634
|
+
for path in project_configs:
|
|
635
|
+
cfg = load_mcp_config_lenient(path)
|
|
636
|
+
if cfg is None:
|
|
637
|
+
continue
|
|
638
|
+
|
|
639
|
+
stdio_servers = extract_stdio_server_commands(cfg)
|
|
640
|
+
if not stdio_servers:
|
|
641
|
+
# No stdio servers — safe to load (remote only)
|
|
642
|
+
configs.append(cfg)
|
|
643
|
+
continue
|
|
644
|
+
|
|
645
|
+
if trust_project_mcp is True:
|
|
646
|
+
configs.append(cfg)
|
|
647
|
+
elif trust_project_mcp is False:
|
|
648
|
+
filtered = _filter_project_stdio_servers(cfg)
|
|
649
|
+
if filtered.get("mcpServers"):
|
|
650
|
+
configs.append(filtered)
|
|
651
|
+
skipped = [
|
|
652
|
+
f"{name}: {cmd} {' '.join(args)}" for name, cmd, args in stdio_servers
|
|
653
|
+
]
|
|
654
|
+
logger.warning(
|
|
655
|
+
"Skipped untrusted project stdio MCP servers: %s",
|
|
656
|
+
"; ".join(skipped),
|
|
657
|
+
)
|
|
658
|
+
else:
|
|
659
|
+
# None — check trust store
|
|
660
|
+
from docagent_cli.mcp_trust import (
|
|
661
|
+
compute_config_fingerprint,
|
|
662
|
+
is_project_mcp_trusted,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
project_root = str(_resolve_project_config_base(project_context).resolve())
|
|
666
|
+
fingerprint = compute_config_fingerprint(project_configs)
|
|
667
|
+
if is_project_mcp_trusted(project_root, fingerprint):
|
|
668
|
+
configs.append(cfg)
|
|
669
|
+
else:
|
|
670
|
+
filtered = _filter_project_stdio_servers(cfg)
|
|
671
|
+
if filtered.get("mcpServers"):
|
|
672
|
+
configs.append(filtered)
|
|
673
|
+
skipped = [
|
|
674
|
+
f"{name}: {cmd} {' '.join(args)}"
|
|
675
|
+
for name, cmd, args in stdio_servers
|
|
676
|
+
]
|
|
677
|
+
logger.warning(
|
|
678
|
+
"Skipped untrusted project stdio MCP servers "
|
|
679
|
+
"(config changed or not yet approved): %s",
|
|
680
|
+
"; ".join(skipped),
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
# Explicit path is highest precedence — errors are fatal
|
|
684
|
+
if explicit_config_path:
|
|
685
|
+
config_path = (
|
|
686
|
+
str(project_context.resolve_user_path(explicit_config_path))
|
|
687
|
+
if project_context is not None
|
|
688
|
+
else explicit_config_path
|
|
689
|
+
)
|
|
690
|
+
configs.append(load_mcp_config(config_path))
|
|
691
|
+
|
|
692
|
+
if not configs:
|
|
693
|
+
return [], None, []
|
|
694
|
+
|
|
695
|
+
merged = merge_mcp_configs(configs)
|
|
696
|
+
if not merged.get("mcpServers"):
|
|
697
|
+
return [], None, []
|
|
698
|
+
|
|
699
|
+
# Validate each server in the merged config
|
|
700
|
+
try:
|
|
701
|
+
for server_name, server_config in merged["mcpServers"].items():
|
|
702
|
+
_validate_server_config(server_name, server_config)
|
|
703
|
+
except (TypeError, ValueError) as e:
|
|
704
|
+
msg = f"Invalid MCP server configuration: {e}"
|
|
705
|
+
raise RuntimeError(msg) from e
|
|
706
|
+
|
|
707
|
+
return await _load_tools_from_config(merged)
|