kubiya-control-plane-api 0.9.15__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.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Resource Discovery
|
|
3
|
+
|
|
4
|
+
⚠️ WORKAROUND for Claude SDK Bug #3426
|
|
5
|
+
|
|
6
|
+
The Claude Agent SDK has a known bug where it fails to properly expose MCP tools
|
|
7
|
+
from SSE servers to Claude during runtime, even though the documentation says it
|
|
8
|
+
should discover them automatically.
|
|
9
|
+
|
|
10
|
+
This module provides a workaround by:
|
|
11
|
+
1. Pre-discovering tools from MCP servers using the MCP Python SDK
|
|
12
|
+
2. Returning discovered tools so they can be added to allowedTools explicitly
|
|
13
|
+
3. Building MCP context metadata for system prompt injection
|
|
14
|
+
|
|
15
|
+
For SSE servers: This pre-discovery is REQUIRED until the SDK bug is fixed.
|
|
16
|
+
For stdio servers: Pre-discovery ensures consistency across transport types.
|
|
17
|
+
|
|
18
|
+
See: https://github.com/anthropics/claude-code/issues/3426
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Dict, Any, List, Optional
|
|
22
|
+
import structlog
|
|
23
|
+
import asyncio
|
|
24
|
+
from contextlib import asynccontextmanager
|
|
25
|
+
|
|
26
|
+
logger = structlog.get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@asynccontextmanager
|
|
30
|
+
async def connect_to_mcp_server(server_name: str, config: Dict[str, Any]):
|
|
31
|
+
"""
|
|
32
|
+
Connect to an MCP server (stdio or SSE) and yield a session.
|
|
33
|
+
|
|
34
|
+
IMPORTANT: For STDIO servers, the server MUST output only JSONRPC messages to stdout.
|
|
35
|
+
Any other output (logs, debug prints, etc.) MUST go to stderr to avoid breaking
|
|
36
|
+
the MCP protocol. The MCP SDK will log parsing errors for non-JSONRPC output but
|
|
37
|
+
will continue operating. However, excessive parsing errors may cause the SDK to
|
|
38
|
+
report execution errors.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
server_name: Name of the MCP server
|
|
42
|
+
config: Server configuration (stdio or SSE format)
|
|
43
|
+
|
|
44
|
+
Yields:
|
|
45
|
+
ClientSession connected to the server
|
|
46
|
+
"""
|
|
47
|
+
import logging
|
|
48
|
+
from mcp import ClientSession
|
|
49
|
+
from mcp.client.stdio import stdio_client
|
|
50
|
+
from mcp.client.sse import sse_client
|
|
51
|
+
|
|
52
|
+
# Suppress verbose parsing error logs from MCP SDK's stdio client
|
|
53
|
+
# These errors are non-fatal and happen when servers incorrectly log to stdout
|
|
54
|
+
# instead of stderr. The connection continues to work despite these errors.
|
|
55
|
+
mcp_stdio_logger = logging.getLogger("mcp.client.stdio")
|
|
56
|
+
original_level = mcp_stdio_logger.level
|
|
57
|
+
mcp_stdio_logger.setLevel(logging.ERROR) # Only show critical errors
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Determine transport type
|
|
61
|
+
is_stdio = "command" in config
|
|
62
|
+
is_sse = "type" in config and config["type"] == "sse"
|
|
63
|
+
|
|
64
|
+
if is_stdio:
|
|
65
|
+
# Stdio transport
|
|
66
|
+
from mcp import StdioServerParameters
|
|
67
|
+
|
|
68
|
+
server_params = StdioServerParameters(
|
|
69
|
+
command=config["command"],
|
|
70
|
+
args=config.get("args", []),
|
|
71
|
+
env=config.get("env", {})
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
logger.info(
|
|
75
|
+
"connecting_to_stdio_mcp_server",
|
|
76
|
+
server_name=server_name,
|
|
77
|
+
command=config["command"],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
async with stdio_client(server_params) as (read, write):
|
|
81
|
+
async with ClientSession(read, write) as session:
|
|
82
|
+
await session.initialize()
|
|
83
|
+
logger.info("stdio_mcp_server_connected", server_name=server_name)
|
|
84
|
+
yield session
|
|
85
|
+
|
|
86
|
+
elif is_sse:
|
|
87
|
+
# SSE transport
|
|
88
|
+
url = config["url"]
|
|
89
|
+
headers = config.get("headers", {})
|
|
90
|
+
|
|
91
|
+
logger.info(
|
|
92
|
+
"connecting_to_sse_mcp_server",
|
|
93
|
+
server_name=server_name,
|
|
94
|
+
url=url,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# SSE connections might not support bidirectional messaging required for discovery
|
|
98
|
+
# Use longer timeouts to prevent background task exceptions
|
|
99
|
+
try:
|
|
100
|
+
async with sse_client(url, headers=headers, timeout=30, sse_read_timeout=60) as (read, write):
|
|
101
|
+
async with ClientSession(read, write) as session:
|
|
102
|
+
await session.initialize()
|
|
103
|
+
logger.info("sse_mcp_server_connected", server_name=server_name)
|
|
104
|
+
yield session
|
|
105
|
+
except Exception as eg:
|
|
106
|
+
# Handle both regular exceptions and ExceptionGroups (Python 3.10 compatible)
|
|
107
|
+
# Check if it's an ExceptionGroup by type name (for Python 3.10 compatibility)
|
|
108
|
+
if type(eg).__name__ == "ExceptionGroup" and hasattr(eg, 'exceptions'):
|
|
109
|
+
logger.error(
|
|
110
|
+
"sse_connection_exception_group",
|
|
111
|
+
server_name=server_name,
|
|
112
|
+
error=str(eg),
|
|
113
|
+
sub_exception_count=len(eg.exceptions),
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
logger.error(
|
|
117
|
+
"sse_connection_error",
|
|
118
|
+
server_name=server_name,
|
|
119
|
+
error=str(eg),
|
|
120
|
+
error_type=type(eg).__name__,
|
|
121
|
+
)
|
|
122
|
+
raise
|
|
123
|
+
else:
|
|
124
|
+
raise ValueError(f"Invalid MCP server config for {server_name}: {config}")
|
|
125
|
+
finally:
|
|
126
|
+
# Restore original log level
|
|
127
|
+
mcp_stdio_logger.setLevel(original_level)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def discover_mcp_resources(server_name: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
131
|
+
"""
|
|
132
|
+
Connect to an MCP server and discover all available tools, resources, and prompts.
|
|
133
|
+
|
|
134
|
+
⚠️ IMPORTANT: Pre-discovery is ONLY needed for SSE servers (bug #3426 workaround)
|
|
135
|
+
|
|
136
|
+
For HTTP servers: Claude SDK handles discovery natively - skip pre-discovery!
|
|
137
|
+
For SSE servers: SDK bug requires manual pre-discovery as workaround
|
|
138
|
+
For stdio servers: SDK handles discovery but we pre-discover for verification
|
|
139
|
+
For SDK MCP servers: These are Python objects, not configs - skip pre-discovery!
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
server_name: Name of the MCP server
|
|
143
|
+
config: Server configuration (or SDK MCP server object for skill-based servers)
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Dictionary with discovered capabilities:
|
|
147
|
+
{
|
|
148
|
+
"server_name": str,
|
|
149
|
+
"tools": [...],
|
|
150
|
+
"resources": [...],
|
|
151
|
+
"prompts": [...],
|
|
152
|
+
"connected": bool,
|
|
153
|
+
"error": str | None
|
|
154
|
+
}
|
|
155
|
+
"""
|
|
156
|
+
transport_type = config.get("type") if isinstance(config, dict) else None
|
|
157
|
+
|
|
158
|
+
# SDK MCP servers: Skip pre-discovery - these are created from Python Toolkits via create_sdk_mcp_server()
|
|
159
|
+
# They're wrapped in a dict with type='sdk' and instance=<Server object>
|
|
160
|
+
if transport_type == "sdk" or not isinstance(config, dict):
|
|
161
|
+
logger.info(
|
|
162
|
+
"skipping_sdk_mcp_server_prediscovery",
|
|
163
|
+
server_name=server_name,
|
|
164
|
+
config_type=str(type(config)),
|
|
165
|
+
has_type_sdk=(transport_type == "sdk"),
|
|
166
|
+
note="SDK MCP servers (from Python Toolkits) use native SDK discovery (no pre-discovery needed)"
|
|
167
|
+
)
|
|
168
|
+
return {
|
|
169
|
+
"server_name": server_name,
|
|
170
|
+
"tools": [],
|
|
171
|
+
"resources": [],
|
|
172
|
+
"prompts": [],
|
|
173
|
+
"connected": True, # Assume connection will work
|
|
174
|
+
"error": None,
|
|
175
|
+
"skipped": True, # Mark as skipped for logging
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# HTTP servers: Skip pre-discovery - SDK handles it natively
|
|
179
|
+
if transport_type == "http":
|
|
180
|
+
logger.info(
|
|
181
|
+
"skipping_http_server_prediscovery",
|
|
182
|
+
server_name=server_name,
|
|
183
|
+
url=config.get("url"),
|
|
184
|
+
note="HTTP servers use native SDK discovery (no workaround needed)"
|
|
185
|
+
)
|
|
186
|
+
return {
|
|
187
|
+
"server_name": server_name,
|
|
188
|
+
"tools": [],
|
|
189
|
+
"resources": [],
|
|
190
|
+
"prompts": [],
|
|
191
|
+
"connected": True, # Assume connection will work
|
|
192
|
+
"error": None,
|
|
193
|
+
"skipped": True, # Mark as skipped for logging
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# SSE servers: Check for explicit tool configuration first
|
|
197
|
+
is_sse = transport_type == "sse"
|
|
198
|
+
explicit_tools = config.get("tools", [])
|
|
199
|
+
if is_sse and explicit_tools:
|
|
200
|
+
logger.info(
|
|
201
|
+
"using_explicit_sse_tool_config",
|
|
202
|
+
server_name=server_name,
|
|
203
|
+
url=config.get("url"),
|
|
204
|
+
tool_count=len(explicit_tools),
|
|
205
|
+
tools=explicit_tools,
|
|
206
|
+
note="Using explicit tool configuration (skipping discovery for SSE server)",
|
|
207
|
+
)
|
|
208
|
+
# Return mock discovery result with explicit tools
|
|
209
|
+
return {
|
|
210
|
+
"server_name": server_name,
|
|
211
|
+
"tools": [{"name": tool, "description": f"{tool} from {server_name}", "inputSchema": {}} for tool in explicit_tools],
|
|
212
|
+
"resources": [],
|
|
213
|
+
"prompts": [],
|
|
214
|
+
"connected": True,
|
|
215
|
+
"error": None,
|
|
216
|
+
}
|
|
217
|
+
elif is_sse:
|
|
218
|
+
# WORKAROUND: SSE servers require pre-discovery due to Claude SDK bug #3426
|
|
219
|
+
# The MCP Python SDK will attempt to connect and discover tools
|
|
220
|
+
# If discovery fails, we'll catch the exception and log it properly
|
|
221
|
+
logger.info(
|
|
222
|
+
"attempting_sse_mcp_discovery",
|
|
223
|
+
server_name=server_name,
|
|
224
|
+
url=config.get("url"),
|
|
225
|
+
note="⚠️ SSE WORKAROUND: Pre-discovering tools (SDK bug #3426)",
|
|
226
|
+
recommendation="If discovery fails repeatedly, add explicit tools to config: "
|
|
227
|
+
f"mcp_servers.{server_name}.tools = ['list_tables', 'query_data', ...]"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
async with connect_to_mcp_server(server_name, config) as session:
|
|
232
|
+
# List all capabilities - handle each separately for resilience
|
|
233
|
+
tools = []
|
|
234
|
+
resources = []
|
|
235
|
+
prompts = []
|
|
236
|
+
|
|
237
|
+
# Try to list tools (most critical)
|
|
238
|
+
try:
|
|
239
|
+
tools_result = await session.list_tools()
|
|
240
|
+
tools = [
|
|
241
|
+
{
|
|
242
|
+
"name": tool.name,
|
|
243
|
+
"description": tool.description,
|
|
244
|
+
"inputSchema": (
|
|
245
|
+
# Handle both Pydantic models and plain dicts
|
|
246
|
+
tool.inputSchema.model_dump() if hasattr(tool.inputSchema, "model_dump")
|
|
247
|
+
else tool.inputSchema if hasattr(tool, "inputSchema")
|
|
248
|
+
else None
|
|
249
|
+
),
|
|
250
|
+
}
|
|
251
|
+
for tool in tools_result.tools
|
|
252
|
+
]
|
|
253
|
+
logger.info(
|
|
254
|
+
"tools_listed_successfully",
|
|
255
|
+
server_name=server_name,
|
|
256
|
+
tool_count=len(tools),
|
|
257
|
+
tool_names=[t["name"] for t in tools],
|
|
258
|
+
)
|
|
259
|
+
except Exception as tool_error:
|
|
260
|
+
logger.warning(
|
|
261
|
+
"failed_to_list_tools",
|
|
262
|
+
server_name=server_name,
|
|
263
|
+
error=str(tool_error)[:200],
|
|
264
|
+
error_type=type(tool_error).__name__,
|
|
265
|
+
exc_info=True,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Try to list resources (optional)
|
|
269
|
+
try:
|
|
270
|
+
resources_result = await session.list_resources()
|
|
271
|
+
resources = [
|
|
272
|
+
{
|
|
273
|
+
"uri": resource.uri,
|
|
274
|
+
"name": resource.name,
|
|
275
|
+
"description": resource.description,
|
|
276
|
+
"mimeType": getattr(resource, "mimeType", None),
|
|
277
|
+
}
|
|
278
|
+
for resource in resources_result.resources
|
|
279
|
+
]
|
|
280
|
+
except Exception as resource_error:
|
|
281
|
+
logger.debug(
|
|
282
|
+
"failed_to_list_resources",
|
|
283
|
+
server_name=server_name,
|
|
284
|
+
error=str(resource_error)[:100]
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Try to list prompts (optional)
|
|
288
|
+
try:
|
|
289
|
+
prompts_result = await session.list_prompts()
|
|
290
|
+
prompts = [
|
|
291
|
+
{
|
|
292
|
+
"name": prompt.name,
|
|
293
|
+
"description": prompt.description,
|
|
294
|
+
"arguments": [
|
|
295
|
+
{
|
|
296
|
+
"name": arg.name,
|
|
297
|
+
"description": arg.description,
|
|
298
|
+
"required": arg.required,
|
|
299
|
+
}
|
|
300
|
+
for arg in (prompt.arguments or [])
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
for prompt in prompts_result.prompts
|
|
304
|
+
]
|
|
305
|
+
except Exception as prompt_error:
|
|
306
|
+
logger.debug(
|
|
307
|
+
"failed_to_list_prompts",
|
|
308
|
+
server_name=server_name,
|
|
309
|
+
error=str(prompt_error)[:100]
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Special logging for SSE servers since this is a workaround for SDK bug
|
|
313
|
+
if is_sse:
|
|
314
|
+
logger.info(
|
|
315
|
+
"sse_mcp_resources_discovered",
|
|
316
|
+
server_name=server_name,
|
|
317
|
+
tools_count=len(tools),
|
|
318
|
+
resources_count=len(resources),
|
|
319
|
+
prompts_count=len(prompts),
|
|
320
|
+
tool_names=[t["name"] for t in tools],
|
|
321
|
+
resource_uris=[r["uri"] for r in resources],
|
|
322
|
+
prompt_names=[p["name"] for p in prompts],
|
|
323
|
+
note="Pre-discovery successful! Tools will be added to allowedTools (SDK bug #3426 workaround)",
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
logger.info(
|
|
327
|
+
"mcp_resources_discovered",
|
|
328
|
+
server_name=server_name,
|
|
329
|
+
tools_count=len(tools),
|
|
330
|
+
resources_count=len(resources),
|
|
331
|
+
prompts_count=len(prompts),
|
|
332
|
+
tool_names=[t["name"] for t in tools],
|
|
333
|
+
resource_uris=[r["uri"] for r in resources],
|
|
334
|
+
prompt_names=[p["name"] for p in prompts],
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
"server_name": server_name,
|
|
339
|
+
"tools": tools,
|
|
340
|
+
"resources": resources,
|
|
341
|
+
"prompts": prompts,
|
|
342
|
+
"connected": True,
|
|
343
|
+
"error": None,
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
except Exception as e:
|
|
347
|
+
error_str = str(e)
|
|
348
|
+
is_sse = config.get("type") == "sse"
|
|
349
|
+
is_timeout = "timeout" in error_str.lower() or "timed out" in error_str.lower()
|
|
350
|
+
is_404 = "404" in error_str or "/messages" in error_str
|
|
351
|
+
|
|
352
|
+
# For ExceptionGroup, try to extract sub-exceptions
|
|
353
|
+
sub_errors = []
|
|
354
|
+
if type(e).__name__ == "ExceptionGroup":
|
|
355
|
+
try:
|
|
356
|
+
# ExceptionGroup has an 'exceptions' attribute
|
|
357
|
+
if hasattr(e, 'exceptions'):
|
|
358
|
+
sub_errors = [
|
|
359
|
+
f"{type(ex).__name__}: {str(ex)[:100]}"
|
|
360
|
+
for ex in e.exceptions[:3] # First 3 errors
|
|
361
|
+
]
|
|
362
|
+
except:
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
# Special handling for SSE-only servers (404 on /messages endpoint)
|
|
366
|
+
if is_sse and is_404:
|
|
367
|
+
logger.error(
|
|
368
|
+
"sse_server_incompatible_with_mcp_discovery",
|
|
369
|
+
server_name=server_name,
|
|
370
|
+
url=config.get("url"),
|
|
371
|
+
error="Server doesn't support bidirectional MCP protocol (404 on /messages endpoint)",
|
|
372
|
+
solution=f"Add explicit tools to agent config:\n"
|
|
373
|
+
f" mcp_servers:\n"
|
|
374
|
+
f" {server_name}:\n"
|
|
375
|
+
f" type: sse\n"
|
|
376
|
+
f" url: {config.get('url')}\n"
|
|
377
|
+
f" tools:\n"
|
|
378
|
+
f" - list_tables\n"
|
|
379
|
+
f" - query_data\n"
|
|
380
|
+
f" - get_schema\n"
|
|
381
|
+
f" - create_visualization\n"
|
|
382
|
+
f" # ... add all tool names here",
|
|
383
|
+
note="SSE-only servers require explicit tool configuration for Claude to see them",
|
|
384
|
+
)
|
|
385
|
+
else:
|
|
386
|
+
# Log appropriately based on error type
|
|
387
|
+
logger.error(
|
|
388
|
+
"mcp_resource_discovery_failed",
|
|
389
|
+
server_name=server_name,
|
|
390
|
+
error=error_str[:200],
|
|
391
|
+
error_type=type(e).__name__,
|
|
392
|
+
sub_errors=sub_errors if sub_errors else None,
|
|
393
|
+
is_sse_server=is_sse,
|
|
394
|
+
is_timeout=is_timeout,
|
|
395
|
+
note="Check SSE server connectivity and authentication.",
|
|
396
|
+
exc_info=True, # Include full traceback for debugging
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
"server_name": server_name,
|
|
401
|
+
"tools": [],
|
|
402
|
+
"resources": [],
|
|
403
|
+
"prompts": [],
|
|
404
|
+
"connected": False,
|
|
405
|
+
"error": error_str[:200],
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
async def discover_all_mcp_resources(mcp_servers: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
|
|
410
|
+
"""
|
|
411
|
+
Discover resources from configured MCP servers (verification and workaround).
|
|
412
|
+
|
|
413
|
+
⚠️ IMPORTANT: Different behavior per transport type:
|
|
414
|
+
|
|
415
|
+
- HTTP servers: SKIPPED - SDK handles discovery natively (no workaround needed)
|
|
416
|
+
- SSE servers: PRE-DISCOVERED - SDK bug #3426 requires manual discovery workaround
|
|
417
|
+
- stdio servers: PRE-DISCOVERED - For verification/consistency
|
|
418
|
+
|
|
419
|
+
⚠️ WORKAROUND for Claude SDK Bug #3426:
|
|
420
|
+
The Claude Agent SDK has a known bug where it fails to properly expose MCP tools
|
|
421
|
+
from SSE servers to Claude during runtime. This function pre-discovers tools from
|
|
422
|
+
SSE servers as a workaround.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
mcp_servers: Dictionary of MCP server configurations {server_name: config}
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Dictionary mapping server names to their discovered resources
|
|
429
|
+
|
|
430
|
+
See: https://github.com/anthropics/claude-code/issues/3426
|
|
431
|
+
"""
|
|
432
|
+
if not mcp_servers:
|
|
433
|
+
return {}
|
|
434
|
+
|
|
435
|
+
# Count servers by type
|
|
436
|
+
http_servers = [name for name, cfg in mcp_servers.items() if cfg.get("type") == "http"]
|
|
437
|
+
sse_servers = [name for name, cfg in mcp_servers.items() if cfg.get("type") == "sse"]
|
|
438
|
+
stdio_servers = [name for name, cfg in mcp_servers.items() if "command" in cfg]
|
|
439
|
+
|
|
440
|
+
logger.info(
|
|
441
|
+
"starting_mcp_resource_discovery",
|
|
442
|
+
server_count=len(mcp_servers),
|
|
443
|
+
server_names=list(mcp_servers.keys()),
|
|
444
|
+
http_count=len(http_servers),
|
|
445
|
+
sse_count=len(sse_servers),
|
|
446
|
+
stdio_count=len(stdio_servers),
|
|
447
|
+
note="HTTP: skipped (SDK native) | SSE: workaround | stdio: verification"
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# Discover resources from all servers in parallel
|
|
451
|
+
tasks = [
|
|
452
|
+
discover_mcp_resources(server_name, config)
|
|
453
|
+
for server_name, config in mcp_servers.items()
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
457
|
+
|
|
458
|
+
# Build results dictionary
|
|
459
|
+
discovered = {}
|
|
460
|
+
for result in results:
|
|
461
|
+
if isinstance(result, Exception):
|
|
462
|
+
logger.error(
|
|
463
|
+
"mcp_discovery_task_failed",
|
|
464
|
+
error=str(result),
|
|
465
|
+
error_type=type(result).__name__,
|
|
466
|
+
)
|
|
467
|
+
continue
|
|
468
|
+
|
|
469
|
+
server_name = result["server_name"]
|
|
470
|
+
discovered[server_name] = result
|
|
471
|
+
|
|
472
|
+
# Log summary with proper categorization
|
|
473
|
+
skipped_servers = [name for name, r in discovered.items() if r.get("skipped")]
|
|
474
|
+
discovered_servers = [name for name, r in discovered.items() if not r.get("skipped")]
|
|
475
|
+
|
|
476
|
+
total_tools = sum(len(r["tools"]) for r in discovered.values() if not r.get("skipped"))
|
|
477
|
+
total_resources = sum(len(r["resources"]) for r in discovered.values() if not r.get("skipped"))
|
|
478
|
+
total_prompts = sum(len(r["prompts"]) for r in discovered.values() if not r.get("skipped"))
|
|
479
|
+
|
|
480
|
+
logger.info(
|
|
481
|
+
"mcp_resource_discovery_complete",
|
|
482
|
+
servers_total=len(discovered),
|
|
483
|
+
servers_skipped=len(skipped_servers),
|
|
484
|
+
servers_discovered=len(discovered_servers),
|
|
485
|
+
skipped_server_names=skipped_servers,
|
|
486
|
+
discovered_server_names=discovered_servers,
|
|
487
|
+
total_tools=total_tools,
|
|
488
|
+
total_resources=total_resources,
|
|
489
|
+
total_prompts=total_prompts,
|
|
490
|
+
note="HTTP servers skipped (SDK native) | SSE/stdio pre-discovered"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
return discovered
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def build_mcp_context_metadata(discovered_resources: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
497
|
+
"""
|
|
498
|
+
Build structured metadata about MCP capabilities to inject into execution context.
|
|
499
|
+
|
|
500
|
+
⚠️ WORKAROUND for Claude SDK Bug #3426:
|
|
501
|
+
This function builds metadata from pre-discovered MCP resources so we can
|
|
502
|
+
explicitly tell Claude about available MCP tools, resources, and prompts.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
discovered_resources: Results from discover_all_mcp_resources()
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Structured metadata dict with all MCP capabilities
|
|
509
|
+
"""
|
|
510
|
+
metadata = {
|
|
511
|
+
"mcp_servers": {},
|
|
512
|
+
"all_tools": [],
|
|
513
|
+
"all_resources": [],
|
|
514
|
+
"all_prompts": [],
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
for server_name, data in discovered_resources.items():
|
|
518
|
+
if not data["connected"]:
|
|
519
|
+
continue
|
|
520
|
+
|
|
521
|
+
metadata["mcp_servers"][server_name] = {
|
|
522
|
+
"tools_count": len(data["tools"]),
|
|
523
|
+
"resources_count": len(data["resources"]),
|
|
524
|
+
"prompts_count": len(data["prompts"]),
|
|
525
|
+
"tools": data["tools"],
|
|
526
|
+
"resources": data["resources"],
|
|
527
|
+
"prompts": data["prompts"],
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
# Add prefixed tool names for reference
|
|
531
|
+
for tool in data["tools"]:
|
|
532
|
+
metadata["all_tools"].append({
|
|
533
|
+
"server": server_name,
|
|
534
|
+
"name": tool["name"],
|
|
535
|
+
"full_name": f"mcp__{server_name}__{tool['name']}",
|
|
536
|
+
"description": tool["description"],
|
|
537
|
+
"inputSchema": tool["inputSchema"],
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
# Add resource URIs
|
|
541
|
+
for resource in data["resources"]:
|
|
542
|
+
metadata["all_resources"].append({
|
|
543
|
+
"server": server_name,
|
|
544
|
+
"uri": resource["uri"],
|
|
545
|
+
"name": resource["name"],
|
|
546
|
+
"description": resource["description"],
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
# Add prompts
|
|
550
|
+
for prompt in data["prompts"]:
|
|
551
|
+
metadata["all_prompts"].append({
|
|
552
|
+
"server": server_name,
|
|
553
|
+
"name": prompt["name"],
|
|
554
|
+
"description": prompt["description"],
|
|
555
|
+
"arguments": prompt["arguments"],
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
return metadata
|