attune-ai 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
"""MCP Server for Socratic Agent Generation System.
|
|
2
|
+
|
|
3
|
+
Exposes the Socratic workflow builder as MCP tools for Claude Desktop/Code.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python -m attune.socratic.mcp_server
|
|
7
|
+
|
|
8
|
+
Or add to Claude Desktop config:
|
|
9
|
+
{
|
|
10
|
+
"mcpServers": {
|
|
11
|
+
"socratic": {
|
|
12
|
+
"command": "python",
|
|
13
|
+
"args": ["-m", "attune.socratic.mcp_server"],
|
|
14
|
+
"env": {
|
|
15
|
+
"ANTHROPIC_API_KEY": "your-key"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Copyright 2026 Smart-AI-Memory
|
|
22
|
+
Licensed under Fair Source License 0.9
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import os
|
|
29
|
+
import sys
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# MCP Protocol Types
|
|
35
|
+
MCP_VERSION = "2024-11-05"
|
|
36
|
+
|
|
37
|
+
# Tool definitions for the Socratic system
|
|
38
|
+
SOCRATIC_TOOLS = [
|
|
39
|
+
{
|
|
40
|
+
"name": "socratic_start_session",
|
|
41
|
+
"description": "Start a new Socratic workflow builder session. Returns a session ID and initial state.",
|
|
42
|
+
"inputSchema": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"goal": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Optional initial goal. If not provided, session starts in AWAITING_GOAL state.",
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"required": [],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "socratic_set_goal",
|
|
55
|
+
"description": "Set or update the goal for a session. Triggers goal analysis and domain detection.",
|
|
56
|
+
"inputSchema": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {
|
|
59
|
+
"session_id": {"type": "string", "description": "The session ID to update"},
|
|
60
|
+
"goal": {"type": "string", "description": "The user's goal in free-form text"},
|
|
61
|
+
},
|
|
62
|
+
"required": ["session_id", "goal"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "socratic_get_questions",
|
|
67
|
+
"description": "Get the next set of clarifying questions for a session.",
|
|
68
|
+
"inputSchema": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {"session_id": {"type": "string", "description": "The session ID"}},
|
|
71
|
+
"required": ["session_id"],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "socratic_submit_answers",
|
|
76
|
+
"description": "Submit answers to clarifying questions.",
|
|
77
|
+
"inputSchema": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"session_id": {"type": "string", "description": "The session ID"},
|
|
81
|
+
"answers": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"description": "Dictionary of field_id -> answer value",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
"required": ["session_id", "answers"],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "socratic_generate_workflow",
|
|
91
|
+
"description": "Generate the workflow once all questions are answered. Returns agent blueprints and success criteria.",
|
|
92
|
+
"inputSchema": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {"session_id": {"type": "string", "description": "The session ID"}},
|
|
95
|
+
"required": ["session_id"],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "socratic_list_sessions",
|
|
100
|
+
"description": "List all saved Socratic sessions.",
|
|
101
|
+
"inputSchema": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": {
|
|
104
|
+
"status_filter": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"enum": ["all", "active", "completed"],
|
|
107
|
+
"description": "Filter sessions by status",
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"required": [],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "socratic_get_session",
|
|
115
|
+
"description": "Get details of a specific session.",
|
|
116
|
+
"inputSchema": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"properties": {
|
|
119
|
+
"session_id": {"type": "string", "description": "The session ID to retrieve"}
|
|
120
|
+
},
|
|
121
|
+
"required": ["session_id"],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "socratic_list_blueprints",
|
|
126
|
+
"description": "List all saved workflow blueprints.",
|
|
127
|
+
"inputSchema": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"properties": {
|
|
130
|
+
"domain_filter": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"description": "Optional domain to filter by (e.g., 'code_review', 'security')",
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"required": [],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "socratic_analyze_goal",
|
|
140
|
+
"description": "Analyze a goal using LLM to detect domains, requirements, and ambiguities without starting a full session.",
|
|
141
|
+
"inputSchema": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"properties": {"goal": {"type": "string", "description": "The goal to analyze"}},
|
|
144
|
+
"required": ["goal"],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "socratic_recommend_agents",
|
|
149
|
+
"description": "Get agent recommendations based on requirements and historical success data.",
|
|
150
|
+
"inputSchema": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"properties": {
|
|
153
|
+
"domains": {
|
|
154
|
+
"type": "array",
|
|
155
|
+
"items": {"type": "string"},
|
|
156
|
+
"description": "List of domains (e.g., ['code_review', 'security'])",
|
|
157
|
+
},
|
|
158
|
+
"languages": {
|
|
159
|
+
"type": "array",
|
|
160
|
+
"items": {"type": "string"},
|
|
161
|
+
"description": "Programming languages involved",
|
|
162
|
+
},
|
|
163
|
+
"quality_focus": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {"type": "string"},
|
|
166
|
+
"description": "Quality focus areas (e.g., ['security', 'performance'])",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
"required": ["domains"],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class SocraticMCPServer:
|
|
176
|
+
"""MCP Server exposing Socratic workflow builder tools."""
|
|
177
|
+
|
|
178
|
+
def __init__(self):
|
|
179
|
+
"""Initialize the MCP server."""
|
|
180
|
+
self._sessions: dict[str, Any] = {}
|
|
181
|
+
self._builder = None
|
|
182
|
+
self._storage = None
|
|
183
|
+
self._llm_analyzer = None
|
|
184
|
+
self._feedback_loop = None
|
|
185
|
+
self._initialized = False
|
|
186
|
+
|
|
187
|
+
async def _ensure_initialized(self):
|
|
188
|
+
"""Lazily initialize components."""
|
|
189
|
+
if self._initialized:
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
from .engine import SocraticWorkflowBuilder
|
|
194
|
+
from .feedback import FeedbackLoop
|
|
195
|
+
from .llm_analyzer import LLMGoalAnalyzer
|
|
196
|
+
from .storage import JSONFileStorage, set_default_storage
|
|
197
|
+
|
|
198
|
+
# Initialize storage
|
|
199
|
+
self._storage = JSONFileStorage()
|
|
200
|
+
set_default_storage(self._storage)
|
|
201
|
+
|
|
202
|
+
# Initialize builder
|
|
203
|
+
self._builder = SocraticWorkflowBuilder()
|
|
204
|
+
|
|
205
|
+
# Initialize LLM analyzer if API key available
|
|
206
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
207
|
+
if api_key:
|
|
208
|
+
self._llm_analyzer = LLMGoalAnalyzer(api_key=api_key)
|
|
209
|
+
|
|
210
|
+
# Initialize feedback loop (uses default storage path)
|
|
211
|
+
self._feedback_loop = FeedbackLoop()
|
|
212
|
+
|
|
213
|
+
self._initialized = True
|
|
214
|
+
logger.info("Socratic MCP Server initialized successfully")
|
|
215
|
+
|
|
216
|
+
except ImportError as e:
|
|
217
|
+
logger.warning(f"Some components not available: {e}")
|
|
218
|
+
self._initialized = True
|
|
219
|
+
|
|
220
|
+
async def handle_tool_call(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
221
|
+
"""Handle a tool call from Claude.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
name: Tool name
|
|
225
|
+
arguments: Tool arguments
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Tool result as dictionary
|
|
229
|
+
"""
|
|
230
|
+
await self._ensure_initialized()
|
|
231
|
+
|
|
232
|
+
handlers = {
|
|
233
|
+
"socratic_start_session": self._handle_start_session,
|
|
234
|
+
"socratic_set_goal": self._handle_set_goal,
|
|
235
|
+
"socratic_get_questions": self._handle_get_questions,
|
|
236
|
+
"socratic_submit_answers": self._handle_submit_answers,
|
|
237
|
+
"socratic_generate_workflow": self._handle_generate_workflow,
|
|
238
|
+
"socratic_list_sessions": self._handle_list_sessions,
|
|
239
|
+
"socratic_get_session": self._handle_get_session,
|
|
240
|
+
"socratic_list_blueprints": self._handle_list_blueprints,
|
|
241
|
+
"socratic_analyze_goal": self._handle_analyze_goal,
|
|
242
|
+
"socratic_recommend_agents": self._handle_recommend_agents,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
handler = handlers.get(name)
|
|
246
|
+
if not handler:
|
|
247
|
+
return {"error": f"Unknown tool: {name}"}
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
return await handler(arguments)
|
|
251
|
+
except Exception as e:
|
|
252
|
+
logger.exception(f"Error handling {name}")
|
|
253
|
+
return {"error": str(e)}
|
|
254
|
+
|
|
255
|
+
async def _handle_start_session(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
256
|
+
"""Start a new Socratic session."""
|
|
257
|
+
goal = args.get("goal")
|
|
258
|
+
|
|
259
|
+
session = self._builder.start_session(goal)
|
|
260
|
+
self._sessions[session.session_id] = session
|
|
261
|
+
|
|
262
|
+
# Persist session
|
|
263
|
+
if self._storage:
|
|
264
|
+
self._storage.save_session(session)
|
|
265
|
+
|
|
266
|
+
result = {
|
|
267
|
+
"session_id": session.session_id,
|
|
268
|
+
"state": session.state.value,
|
|
269
|
+
"message": "Session started successfully",
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if goal:
|
|
273
|
+
result["goal"] = goal
|
|
274
|
+
# Get domain from goal_analysis if available
|
|
275
|
+
if session.goal_analysis:
|
|
276
|
+
result["detected_domain"] = session.goal_analysis.domain
|
|
277
|
+
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
async def _handle_set_goal(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
281
|
+
"""Set goal for a session."""
|
|
282
|
+
session_id = args["session_id"]
|
|
283
|
+
goal = args["goal"]
|
|
284
|
+
|
|
285
|
+
session = self._get_session(session_id)
|
|
286
|
+
if not session:
|
|
287
|
+
return {"error": f"Session not found: {session_id}"}
|
|
288
|
+
|
|
289
|
+
# Use LLM analyzer if available
|
|
290
|
+
analysis = None
|
|
291
|
+
if self._llm_analyzer:
|
|
292
|
+
try:
|
|
293
|
+
analysis = await self._llm_analyzer.analyze_goal(goal)
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logger.warning(f"LLM analysis failed, using fallback: {e}")
|
|
296
|
+
|
|
297
|
+
session = self._builder.set_goal(session, goal)
|
|
298
|
+
self._sessions[session_id] = session
|
|
299
|
+
|
|
300
|
+
if self._storage:
|
|
301
|
+
self._storage.save_session(session)
|
|
302
|
+
|
|
303
|
+
result = {
|
|
304
|
+
"session_id": session_id,
|
|
305
|
+
"state": session.state.value,
|
|
306
|
+
"goal": goal,
|
|
307
|
+
}
|
|
308
|
+
# Add domain from goal_analysis if available
|
|
309
|
+
if session.goal_analysis:
|
|
310
|
+
result["detected_domain"] = session.goal_analysis.domain
|
|
311
|
+
|
|
312
|
+
if analysis:
|
|
313
|
+
result["llm_analysis"] = {
|
|
314
|
+
"primary_domain": analysis.primary_domain,
|
|
315
|
+
"confidence": analysis.confidence,
|
|
316
|
+
"ambiguities": analysis.ambiguities,
|
|
317
|
+
"suggested_questions": analysis.suggested_questions[:3],
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return result
|
|
321
|
+
|
|
322
|
+
async def _handle_get_questions(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
323
|
+
"""Get clarifying questions for a session."""
|
|
324
|
+
session_id = args["session_id"]
|
|
325
|
+
|
|
326
|
+
session = self._get_session(session_id)
|
|
327
|
+
if not session:
|
|
328
|
+
return {"error": f"Session not found: {session_id}"}
|
|
329
|
+
|
|
330
|
+
form = self._builder.get_next_questions(session)
|
|
331
|
+
if not form:
|
|
332
|
+
return {
|
|
333
|
+
"session_id": session_id,
|
|
334
|
+
"state": session.state.value,
|
|
335
|
+
"questions": [],
|
|
336
|
+
"message": "No more questions - ready to generate workflow",
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
# Convert form to JSON-serializable format
|
|
340
|
+
questions = []
|
|
341
|
+
for field in form.fields:
|
|
342
|
+
q = {
|
|
343
|
+
"field_id": field.id,
|
|
344
|
+
"type": field.field_type.value,
|
|
345
|
+
"label": field.label,
|
|
346
|
+
"required": field.validation.required if field.validation else False,
|
|
347
|
+
}
|
|
348
|
+
if field.options:
|
|
349
|
+
# Serialize FieldOption objects
|
|
350
|
+
q["options"] = [
|
|
351
|
+
{"value": opt.value, "label": opt.label, "description": opt.description}
|
|
352
|
+
for opt in field.options
|
|
353
|
+
]
|
|
354
|
+
if field.placeholder:
|
|
355
|
+
q["placeholder"] = field.placeholder
|
|
356
|
+
if field.help_text:
|
|
357
|
+
q["help_text"] = field.help_text
|
|
358
|
+
if field.default is not None:
|
|
359
|
+
q["default"] = field.default
|
|
360
|
+
questions.append(q)
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
"session_id": session_id,
|
|
364
|
+
"state": session.state.value,
|
|
365
|
+
"form_id": form.id,
|
|
366
|
+
"form_title": form.title,
|
|
367
|
+
"questions": questions,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async def _handle_submit_answers(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
371
|
+
"""Submit answers to questions."""
|
|
372
|
+
session_id = args["session_id"]
|
|
373
|
+
answers = args["answers"]
|
|
374
|
+
|
|
375
|
+
session = self._get_session(session_id)
|
|
376
|
+
if not session:
|
|
377
|
+
return {"error": f"Session not found: {session_id}"}
|
|
378
|
+
|
|
379
|
+
session = self._builder.submit_answers(session, answers)
|
|
380
|
+
self._sessions[session_id] = session
|
|
381
|
+
|
|
382
|
+
if self._storage:
|
|
383
|
+
self._storage.save_session(session)
|
|
384
|
+
|
|
385
|
+
ready = self._builder.is_ready_to_generate(session)
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
"session_id": session_id,
|
|
389
|
+
"state": session.state.value,
|
|
390
|
+
"ready_to_generate": ready,
|
|
391
|
+
"message": "Ready to generate workflow" if ready else "More questions available",
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async def _handle_generate_workflow(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
395
|
+
"""Generate workflow from session."""
|
|
396
|
+
session_id = args["session_id"]
|
|
397
|
+
|
|
398
|
+
session = self._get_session(session_id)
|
|
399
|
+
if not session:
|
|
400
|
+
return {"error": f"Session not found: {session_id}"}
|
|
401
|
+
|
|
402
|
+
if not self._builder.is_ready_to_generate(session):
|
|
403
|
+
return {"error": "Session not ready for generation", "state": session.state.value}
|
|
404
|
+
|
|
405
|
+
workflow = self._builder.generate_workflow(session)
|
|
406
|
+
|
|
407
|
+
# Save blueprint
|
|
408
|
+
if self._storage:
|
|
409
|
+
self._storage.save_blueprint(workflow.blueprint)
|
|
410
|
+
self._storage.save_session(session)
|
|
411
|
+
|
|
412
|
+
# Convert to JSON-serializable format
|
|
413
|
+
agents = []
|
|
414
|
+
for agent in workflow.blueprint.agents:
|
|
415
|
+
agents.append(
|
|
416
|
+
{
|
|
417
|
+
"agent_id": agent.agent_id,
|
|
418
|
+
"name": agent.name,
|
|
419
|
+
"role": agent.role.value,
|
|
420
|
+
"description": agent.description,
|
|
421
|
+
"tools": [t.tool_id for t in agent.tools],
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
stages = []
|
|
426
|
+
for stage in workflow.blueprint.stages:
|
|
427
|
+
stages.append(
|
|
428
|
+
{
|
|
429
|
+
"stage_id": stage.stage_id,
|
|
430
|
+
"name": stage.name,
|
|
431
|
+
"agent_ids": stage.agent_ids,
|
|
432
|
+
"dependencies": stage.dependencies,
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
metrics = []
|
|
437
|
+
for metric in workflow.success_criteria.metrics:
|
|
438
|
+
metrics.append(
|
|
439
|
+
{
|
|
440
|
+
"metric_id": metric.metric_id,
|
|
441
|
+
"name": metric.name,
|
|
442
|
+
"description": metric.description,
|
|
443
|
+
"type": metric.metric_type.value,
|
|
444
|
+
"target": metric.target_value,
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
"session_id": session_id,
|
|
450
|
+
"blueprint_id": workflow.blueprint.blueprint_id,
|
|
451
|
+
"workflow_name": workflow.blueprint.name,
|
|
452
|
+
"agents": agents,
|
|
453
|
+
"stages": stages,
|
|
454
|
+
"success_metrics": metrics,
|
|
455
|
+
"state": session.state.value,
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async def _handle_list_sessions(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
459
|
+
"""List all sessions."""
|
|
460
|
+
status_filter = args.get("status_filter", "all")
|
|
461
|
+
|
|
462
|
+
sessions = []
|
|
463
|
+
|
|
464
|
+
# Get from storage
|
|
465
|
+
if self._storage:
|
|
466
|
+
stored = self._storage.list_sessions()
|
|
467
|
+
for s in stored:
|
|
468
|
+
if status_filter == "all":
|
|
469
|
+
sessions.append(s)
|
|
470
|
+
elif status_filter == "active" and s.get("state") != "completed":
|
|
471
|
+
sessions.append(s)
|
|
472
|
+
elif status_filter == "completed" and s.get("state") == "completed":
|
|
473
|
+
sessions.append(s)
|
|
474
|
+
|
|
475
|
+
# Add in-memory sessions not yet persisted
|
|
476
|
+
for sid, session in self._sessions.items():
|
|
477
|
+
if not any(s.get("session_id") == sid for s in sessions):
|
|
478
|
+
sessions.append(
|
|
479
|
+
{
|
|
480
|
+
"session_id": session.session_id,
|
|
481
|
+
"state": session.state.value,
|
|
482
|
+
"goal": session.goal,
|
|
483
|
+
"created_at": (
|
|
484
|
+
session.created_at.isoformat() if session.created_at else None
|
|
485
|
+
),
|
|
486
|
+
}
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
return {"sessions": sessions, "count": len(sessions)}
|
|
490
|
+
|
|
491
|
+
async def _handle_get_session(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
492
|
+
"""Get session details."""
|
|
493
|
+
session_id = args["session_id"]
|
|
494
|
+
|
|
495
|
+
session = self._get_session(session_id)
|
|
496
|
+
if not session:
|
|
497
|
+
return {"error": f"Session not found: {session_id}"}
|
|
498
|
+
|
|
499
|
+
result = {
|
|
500
|
+
"session_id": session.session_id,
|
|
501
|
+
"state": session.state.value,
|
|
502
|
+
"goal": session.goal,
|
|
503
|
+
"question_rounds": session.question_rounds,
|
|
504
|
+
"created_at": session.created_at.isoformat() if session.created_at else None,
|
|
505
|
+
"updated_at": session.updated_at.isoformat() if session.updated_at else None,
|
|
506
|
+
}
|
|
507
|
+
# Add domain from goal_analysis if available
|
|
508
|
+
if session.goal_analysis:
|
|
509
|
+
result["detected_domain"] = session.goal_analysis.domain
|
|
510
|
+
return result
|
|
511
|
+
|
|
512
|
+
async def _handle_list_blueprints(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
513
|
+
"""List all blueprints."""
|
|
514
|
+
domain_filter = args.get("domain_filter")
|
|
515
|
+
|
|
516
|
+
blueprints = []
|
|
517
|
+
|
|
518
|
+
if self._storage:
|
|
519
|
+
stored = self._storage.list_blueprints()
|
|
520
|
+
for bp in stored:
|
|
521
|
+
if domain_filter:
|
|
522
|
+
domains = bp.get("domains", [])
|
|
523
|
+
if domain_filter not in domains:
|
|
524
|
+
continue
|
|
525
|
+
blueprints.append(bp)
|
|
526
|
+
|
|
527
|
+
return {"blueprints": blueprints, "count": len(blueprints)}
|
|
528
|
+
|
|
529
|
+
async def _handle_analyze_goal(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
530
|
+
"""Analyze a goal without starting a session."""
|
|
531
|
+
goal = args["goal"]
|
|
532
|
+
|
|
533
|
+
# Use LLM analyzer if available
|
|
534
|
+
if self._llm_analyzer:
|
|
535
|
+
try:
|
|
536
|
+
analysis = await self._llm_analyzer.analyze_goal(goal)
|
|
537
|
+
return {
|
|
538
|
+
"goal": goal,
|
|
539
|
+
"primary_domain": analysis.primary_domain,
|
|
540
|
+
"secondary_domains": analysis.secondary_domains,
|
|
541
|
+
"confidence": analysis.confidence,
|
|
542
|
+
"detected_requirements": analysis.detected_requirements,
|
|
543
|
+
"ambiguities": analysis.ambiguities,
|
|
544
|
+
"suggested_questions": analysis.suggested_questions,
|
|
545
|
+
"suggested_agents": analysis.suggested_agents,
|
|
546
|
+
"analysis_method": "llm",
|
|
547
|
+
}
|
|
548
|
+
except Exception as e:
|
|
549
|
+
logger.warning(f"LLM analysis failed: {e}")
|
|
550
|
+
|
|
551
|
+
# Fallback to keyword-based analysis
|
|
552
|
+
domains = self._builder._detect_domains(goal)
|
|
553
|
+
return {"goal": goal, "detected_domains": list(domains), "analysis_method": "keyword"}
|
|
554
|
+
|
|
555
|
+
async def _handle_recommend_agents(self, args: dict[str, Any]) -> dict[str, Any]:
|
|
556
|
+
"""Get agent recommendations."""
|
|
557
|
+
domains = args["domains"]
|
|
558
|
+
languages = args.get("languages", [])
|
|
559
|
+
quality_focus = args.get("quality_focus", [])
|
|
560
|
+
|
|
561
|
+
from .feedback import AdaptiveAgentGenerator
|
|
562
|
+
from .generator import AgentGenerator
|
|
563
|
+
|
|
564
|
+
# Use adaptive generator if feedback available
|
|
565
|
+
if self._feedback_loop:
|
|
566
|
+
adaptive_gen = AdaptiveAgentGenerator(self._feedback_loop.collector)
|
|
567
|
+
context = {"domains": domains, "languages": languages, "quality_focus": quality_focus}
|
|
568
|
+
recommendations = adaptive_gen.recommend_agents(context)
|
|
569
|
+
else:
|
|
570
|
+
# Use basic generator
|
|
571
|
+
generator = AgentGenerator()
|
|
572
|
+
recommendations = []
|
|
573
|
+
for domain in domains:
|
|
574
|
+
templates = generator._get_templates_for_domain(domain)
|
|
575
|
+
for t in templates:
|
|
576
|
+
recommendations.append({"template_id": t, "domain": domain, "confidence": 0.8})
|
|
577
|
+
|
|
578
|
+
return {"recommendations": recommendations, "count": len(recommendations)}
|
|
579
|
+
|
|
580
|
+
def _get_session(self, session_id: str):
|
|
581
|
+
"""Get session from memory or storage."""
|
|
582
|
+
# Check memory first
|
|
583
|
+
if session_id in self._sessions:
|
|
584
|
+
return self._sessions[session_id]
|
|
585
|
+
|
|
586
|
+
# Try loading from storage
|
|
587
|
+
if self._storage:
|
|
588
|
+
session = self._storage.load_session(session_id)
|
|
589
|
+
if session:
|
|
590
|
+
self._sessions[session_id] = session
|
|
591
|
+
return session
|
|
592
|
+
|
|
593
|
+
return None
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
async def run_mcp_server():
|
|
597
|
+
"""Run the MCP server using stdio transport."""
|
|
598
|
+
server = SocraticMCPServer()
|
|
599
|
+
|
|
600
|
+
# Read from stdin, write to stdout
|
|
601
|
+
reader = asyncio.StreamReader()
|
|
602
|
+
protocol = asyncio.StreamReaderProtocol(reader)
|
|
603
|
+
await asyncio.get_event_loop().connect_read_pipe(lambda: protocol, sys.stdin)
|
|
604
|
+
|
|
605
|
+
writer_transport, writer_protocol = await asyncio.get_event_loop().connect_write_pipe(
|
|
606
|
+
asyncio.streams.FlowControlMixin, sys.stdout
|
|
607
|
+
)
|
|
608
|
+
writer = asyncio.StreamWriter(
|
|
609
|
+
writer_transport, writer_protocol, reader, asyncio.get_event_loop()
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
async def send_response(response: dict):
|
|
613
|
+
"""Send a JSON-RPC response."""
|
|
614
|
+
data = json.dumps(response) + "\n"
|
|
615
|
+
writer.write(data.encode())
|
|
616
|
+
await writer.drain()
|
|
617
|
+
|
|
618
|
+
async def handle_message(message: dict):
|
|
619
|
+
"""Handle an incoming JSON-RPC message."""
|
|
620
|
+
method = message.get("method")
|
|
621
|
+
params = message.get("params", {})
|
|
622
|
+
msg_id = message.get("id")
|
|
623
|
+
|
|
624
|
+
if method == "initialize":
|
|
625
|
+
# MCP initialization
|
|
626
|
+
response = {
|
|
627
|
+
"jsonrpc": "2.0",
|
|
628
|
+
"id": msg_id,
|
|
629
|
+
"result": {
|
|
630
|
+
"protocolVersion": MCP_VERSION,
|
|
631
|
+
"capabilities": {"tools": {}},
|
|
632
|
+
"serverInfo": {"name": "socratic-workflow-builder", "version": "1.0.0"},
|
|
633
|
+
},
|
|
634
|
+
}
|
|
635
|
+
await send_response(response)
|
|
636
|
+
|
|
637
|
+
elif method == "tools/list":
|
|
638
|
+
# List available tools
|
|
639
|
+
response = {"jsonrpc": "2.0", "id": msg_id, "result": {"tools": SOCRATIC_TOOLS}}
|
|
640
|
+
await send_response(response)
|
|
641
|
+
|
|
642
|
+
elif method == "tools/call":
|
|
643
|
+
# Execute a tool
|
|
644
|
+
tool_name = params.get("name")
|
|
645
|
+
tool_args = params.get("arguments", {})
|
|
646
|
+
|
|
647
|
+
result = await server.handle_tool_call(tool_name, tool_args)
|
|
648
|
+
|
|
649
|
+
response = {
|
|
650
|
+
"jsonrpc": "2.0",
|
|
651
|
+
"id": msg_id,
|
|
652
|
+
"result": {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]},
|
|
653
|
+
}
|
|
654
|
+
await send_response(response)
|
|
655
|
+
|
|
656
|
+
elif method == "notifications/initialized":
|
|
657
|
+
# Client initialized notification - no response needed
|
|
658
|
+
pass
|
|
659
|
+
|
|
660
|
+
else:
|
|
661
|
+
# Unknown method
|
|
662
|
+
response = {
|
|
663
|
+
"jsonrpc": "2.0",
|
|
664
|
+
"id": msg_id,
|
|
665
|
+
"error": {"code": -32601, "message": f"Method not found: {method}"},
|
|
666
|
+
}
|
|
667
|
+
await send_response(response)
|
|
668
|
+
|
|
669
|
+
# Main message loop
|
|
670
|
+
logger.info("Socratic MCP Server starting...")
|
|
671
|
+
|
|
672
|
+
while True:
|
|
673
|
+
try:
|
|
674
|
+
line = await reader.readline()
|
|
675
|
+
if not line:
|
|
676
|
+
break
|
|
677
|
+
|
|
678
|
+
message = json.loads(line.decode().strip())
|
|
679
|
+
await handle_message(message)
|
|
680
|
+
|
|
681
|
+
except json.JSONDecodeError as e:
|
|
682
|
+
logger.error(f"Invalid JSON: {e}")
|
|
683
|
+
except Exception as e:
|
|
684
|
+
logger.exception(f"Error processing message: {e}")
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def main():
|
|
688
|
+
"""Entry point for MCP server."""
|
|
689
|
+
logging.basicConfig(
|
|
690
|
+
level=logging.INFO,
|
|
691
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
692
|
+
stream=sys.stderr, # Log to stderr, MCP uses stdout
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
try:
|
|
696
|
+
asyncio.run(run_mcp_server())
|
|
697
|
+
except KeyboardInterrupt:
|
|
698
|
+
logger.info("Server shutting down...")
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
if __name__ == "__main__":
|
|
702
|
+
main()
|