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.
Files changed (479) hide show
  1. control_plane_api/LICENSE +676 -0
  2. control_plane_api/README.md +350 -0
  3. control_plane_api/__init__.py +4 -0
  4. control_plane_api/__version__.py +8 -0
  5. control_plane_api/alembic/README +1 -0
  6. control_plane_api/alembic/env.py +121 -0
  7. control_plane_api/alembic/script.py.mako +28 -0
  8. control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
  9. control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
  10. control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
  11. control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
  12. control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
  13. control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
  14. control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
  15. control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
  16. control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
  17. control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
  18. control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
  19. control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
  20. control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
  21. control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
  22. control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
  23. control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
  24. control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
  25. control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
  26. control_plane_api/alembic.ini +148 -0
  27. control_plane_api/api/index.py +12 -0
  28. control_plane_api/app/__init__.py +11 -0
  29. control_plane_api/app/activities/__init__.py +20 -0
  30. control_plane_api/app/activities/agent_activities.py +384 -0
  31. control_plane_api/app/activities/plan_generation_activities.py +499 -0
  32. control_plane_api/app/activities/team_activities.py +424 -0
  33. control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
  34. control_plane_api/app/config/__init__.py +35 -0
  35. control_plane_api/app/config/api_config.py +469 -0
  36. control_plane_api/app/config/config_loader.py +224 -0
  37. control_plane_api/app/config/model_pricing.py +323 -0
  38. control_plane_api/app/config/storage_config.py +159 -0
  39. control_plane_api/app/config.py +115 -0
  40. control_plane_api/app/controllers/__init__.py +0 -0
  41. control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
  42. control_plane_api/app/database.py +135 -0
  43. control_plane_api/app/exceptions.py +408 -0
  44. control_plane_api/app/lib/__init__.py +11 -0
  45. control_plane_api/app/lib/environment.py +65 -0
  46. control_plane_api/app/lib/event_bus/__init__.py +17 -0
  47. control_plane_api/app/lib/event_bus/base.py +136 -0
  48. control_plane_api/app/lib/event_bus/manager.py +335 -0
  49. control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
  50. control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
  51. control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
  52. control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
  53. control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
  54. control_plane_api/app/lib/job_executor.py +330 -0
  55. control_plane_api/app/lib/kubiya_client.py +293 -0
  56. control_plane_api/app/lib/litellm_pricing.py +166 -0
  57. control_plane_api/app/lib/mcp_validation.py +163 -0
  58. control_plane_api/app/lib/nats/__init__.py +13 -0
  59. control_plane_api/app/lib/nats/credentials_manager.py +288 -0
  60. control_plane_api/app/lib/nats/listener.py +374 -0
  61. control_plane_api/app/lib/planning_prompt_builder.py +153 -0
  62. control_plane_api/app/lib/planning_tools/__init__.py +41 -0
  63. control_plane_api/app/lib/planning_tools/agents.py +409 -0
  64. control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
  65. control_plane_api/app/lib/planning_tools/base.py +119 -0
  66. control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
  67. control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
  68. control_plane_api/app/lib/planning_tools/environments.py +218 -0
  69. control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
  70. control_plane_api/app/lib/planning_tools/models.py +93 -0
  71. control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
  72. control_plane_api/app/lib/planning_tools/resources.py +242 -0
  73. control_plane_api/app/lib/planning_tools/teams.py +334 -0
  74. control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
  75. control_plane_api/app/lib/redis_client.py +803 -0
  76. control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
  77. control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
  78. control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
  79. control_plane_api/app/lib/storage/__init__.py +20 -0
  80. control_plane_api/app/lib/storage/base_provider.py +274 -0
  81. control_plane_api/app/lib/storage/provider_factory.py +157 -0
  82. control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
  83. control_plane_api/app/lib/supabase.py +71 -0
  84. control_plane_api/app/lib/supabase_utils.py +138 -0
  85. control_plane_api/app/lib/task_planning/__init__.py +138 -0
  86. control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
  87. control_plane_api/app/lib/task_planning/agents.py +389 -0
  88. control_plane_api/app/lib/task_planning/cache.py +218 -0
  89. control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
  90. control_plane_api/app/lib/task_planning/helpers.py +293 -0
  91. control_plane_api/app/lib/task_planning/hooks.py +474 -0
  92. control_plane_api/app/lib/task_planning/models.py +503 -0
  93. control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
  94. control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
  95. control_plane_api/app/lib/task_planning/runner.py +656 -0
  96. control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
  97. control_plane_api/app/lib/task_planning/workflow.py +424 -0
  98. control_plane_api/app/lib/templating/__init__.py +88 -0
  99. control_plane_api/app/lib/templating/compiler.py +278 -0
  100. control_plane_api/app/lib/templating/engine.py +178 -0
  101. control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
  102. control_plane_api/app/lib/templating/parsers/base.py +96 -0
  103. control_plane_api/app/lib/templating/parsers/env.py +85 -0
  104. control_plane_api/app/lib/templating/parsers/graph.py +112 -0
  105. control_plane_api/app/lib/templating/parsers/secret.py +87 -0
  106. control_plane_api/app/lib/templating/parsers/simple.py +81 -0
  107. control_plane_api/app/lib/templating/resolver.py +366 -0
  108. control_plane_api/app/lib/templating/types.py +214 -0
  109. control_plane_api/app/lib/templating/validator.py +201 -0
  110. control_plane_api/app/lib/temporal_client.py +232 -0
  111. control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
  112. control_plane_api/app/lib/temporal_credentials_service.py +203 -0
  113. control_plane_api/app/lib/validation/__init__.py +24 -0
  114. control_plane_api/app/lib/validation/runtime_validation.py +388 -0
  115. control_plane_api/app/main.py +531 -0
  116. control_plane_api/app/middleware/__init__.py +10 -0
  117. control_plane_api/app/middleware/auth.py +645 -0
  118. control_plane_api/app/middleware/exception_handler.py +267 -0
  119. control_plane_api/app/middleware/prometheus_middleware.py +173 -0
  120. control_plane_api/app/middleware/rate_limiting.py +384 -0
  121. control_plane_api/app/middleware/request_id.py +202 -0
  122. control_plane_api/app/models/__init__.py +40 -0
  123. control_plane_api/app/models/agent.py +90 -0
  124. control_plane_api/app/models/analytics.py +206 -0
  125. control_plane_api/app/models/associations.py +107 -0
  126. control_plane_api/app/models/auth_user.py +73 -0
  127. control_plane_api/app/models/context.py +161 -0
  128. control_plane_api/app/models/custom_integration.py +99 -0
  129. control_plane_api/app/models/environment.py +64 -0
  130. control_plane_api/app/models/execution.py +125 -0
  131. control_plane_api/app/models/execution_transition.py +50 -0
  132. control_plane_api/app/models/job.py +159 -0
  133. control_plane_api/app/models/llm_model.py +78 -0
  134. control_plane_api/app/models/orchestration.py +66 -0
  135. control_plane_api/app/models/plan_execution.py +102 -0
  136. control_plane_api/app/models/presence.py +49 -0
  137. control_plane_api/app/models/project.py +61 -0
  138. control_plane_api/app/models/project_management.py +85 -0
  139. control_plane_api/app/models/session.py +29 -0
  140. control_plane_api/app/models/skill.py +155 -0
  141. control_plane_api/app/models/system_tables.py +43 -0
  142. control_plane_api/app/models/task_planning.py +372 -0
  143. control_plane_api/app/models/team.py +86 -0
  144. control_plane_api/app/models/trace.py +257 -0
  145. control_plane_api/app/models/user_profile.py +54 -0
  146. control_plane_api/app/models/worker.py +221 -0
  147. control_plane_api/app/models/workflow.py +161 -0
  148. control_plane_api/app/models/workspace.py +50 -0
  149. control_plane_api/app/observability/__init__.py +177 -0
  150. control_plane_api/app/observability/context_logging.py +475 -0
  151. control_plane_api/app/observability/decorators.py +337 -0
  152. control_plane_api/app/observability/local_span_processor.py +702 -0
  153. control_plane_api/app/observability/metrics.py +303 -0
  154. control_plane_api/app/observability/middleware.py +246 -0
  155. control_plane_api/app/observability/optional.py +115 -0
  156. control_plane_api/app/observability/tracing.py +382 -0
  157. control_plane_api/app/policies/README.md +149 -0
  158. control_plane_api/app/policies/approved_users.rego +62 -0
  159. control_plane_api/app/policies/business_hours.rego +51 -0
  160. control_plane_api/app/policies/rate_limiting.rego +100 -0
  161. control_plane_api/app/policies/tool_enforcement/README.md +336 -0
  162. control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
  163. control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
  164. control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
  165. control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
  166. control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
  167. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  168. control_plane_api/app/routers/__init__.py +4 -0
  169. control_plane_api/app/routers/agents.py +382 -0
  170. control_plane_api/app/routers/agents_v2.py +1598 -0
  171. control_plane_api/app/routers/analytics.py +1310 -0
  172. control_plane_api/app/routers/auth.py +59 -0
  173. control_plane_api/app/routers/client_config.py +57 -0
  174. control_plane_api/app/routers/context_graph.py +561 -0
  175. control_plane_api/app/routers/context_manager.py +577 -0
  176. control_plane_api/app/routers/custom_integrations.py +490 -0
  177. control_plane_api/app/routers/enforcer.py +132 -0
  178. control_plane_api/app/routers/environment_context.py +252 -0
  179. control_plane_api/app/routers/environments.py +761 -0
  180. control_plane_api/app/routers/execution_environment.py +847 -0
  181. control_plane_api/app/routers/executions/__init__.py +28 -0
  182. control_plane_api/app/routers/executions/router.py +286 -0
  183. control_plane_api/app/routers/executions/services/__init__.py +22 -0
  184. control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
  185. control_plane_api/app/routers/executions/services/status_service.py +420 -0
  186. control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
  187. control_plane_api/app/routers/executions/services/worker_health.py +514 -0
  188. control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
  189. control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
  190. control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
  191. control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
  192. control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
  193. control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
  194. control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
  195. control_plane_api/app/routers/executions.py +4888 -0
  196. control_plane_api/app/routers/health.py +165 -0
  197. control_plane_api/app/routers/health_v2.py +394 -0
  198. control_plane_api/app/routers/integration_templates.py +496 -0
  199. control_plane_api/app/routers/integrations.py +287 -0
  200. control_plane_api/app/routers/jobs.py +1809 -0
  201. control_plane_api/app/routers/metrics.py +517 -0
  202. control_plane_api/app/routers/models.py +82 -0
  203. control_plane_api/app/routers/models_v2.py +628 -0
  204. control_plane_api/app/routers/plan_executions.py +1481 -0
  205. control_plane_api/app/routers/plan_generation_async.py +304 -0
  206. control_plane_api/app/routers/policies.py +669 -0
  207. control_plane_api/app/routers/presence.py +234 -0
  208. control_plane_api/app/routers/projects.py +987 -0
  209. control_plane_api/app/routers/runners.py +379 -0
  210. control_plane_api/app/routers/runtimes.py +172 -0
  211. control_plane_api/app/routers/secrets.py +171 -0
  212. control_plane_api/app/routers/skills.py +1010 -0
  213. control_plane_api/app/routers/skills_definitions.py +140 -0
  214. control_plane_api/app/routers/storage.py +456 -0
  215. control_plane_api/app/routers/task_planning.py +611 -0
  216. control_plane_api/app/routers/task_queues.py +650 -0
  217. control_plane_api/app/routers/team_context.py +274 -0
  218. control_plane_api/app/routers/teams.py +1747 -0
  219. control_plane_api/app/routers/templates.py +248 -0
  220. control_plane_api/app/routers/traces.py +571 -0
  221. control_plane_api/app/routers/websocket_client.py +479 -0
  222. control_plane_api/app/routers/websocket_executions_status.py +437 -0
  223. control_plane_api/app/routers/websocket_gateway.py +323 -0
  224. control_plane_api/app/routers/websocket_traces.py +576 -0
  225. control_plane_api/app/routers/worker_queues.py +2555 -0
  226. control_plane_api/app/routers/worker_websocket.py +419 -0
  227. control_plane_api/app/routers/workers.py +1004 -0
  228. control_plane_api/app/routers/workflows.py +204 -0
  229. control_plane_api/app/runtimes/__init__.py +6 -0
  230. control_plane_api/app/runtimes/validation.py +344 -0
  231. control_plane_api/app/schemas/__init__.py +1 -0
  232. control_plane_api/app/schemas/job_schemas.py +302 -0
  233. control_plane_api/app/schemas/mcp_schemas.py +311 -0
  234. control_plane_api/app/schemas/template_schemas.py +133 -0
  235. control_plane_api/app/schemas/trace_schemas.py +168 -0
  236. control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
  237. control_plane_api/app/services/__init__.py +1 -0
  238. control_plane_api/app/services/agno_planning_strategy.py +233 -0
  239. control_plane_api/app/services/agno_service.py +838 -0
  240. control_plane_api/app/services/claude_code_planning_service.py +203 -0
  241. control_plane_api/app/services/context_graph_client.py +224 -0
  242. control_plane_api/app/services/custom_integration_service.py +415 -0
  243. control_plane_api/app/services/integration_resolution_service.py +345 -0
  244. control_plane_api/app/services/litellm_service.py +394 -0
  245. control_plane_api/app/services/plan_generator.py +79 -0
  246. control_plane_api/app/services/planning_strategy.py +66 -0
  247. control_plane_api/app/services/planning_strategy_factory.py +118 -0
  248. control_plane_api/app/services/policy_service.py +615 -0
  249. control_plane_api/app/services/state_transition_service.py +755 -0
  250. control_plane_api/app/services/storage_service.py +593 -0
  251. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  252. control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
  253. control_plane_api/app/services/trace_retention.py +354 -0
  254. control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
  255. control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
  256. control_plane_api/app/services/workflow_operations_service.py +611 -0
  257. control_plane_api/app/skills/__init__.py +100 -0
  258. control_plane_api/app/skills/base.py +239 -0
  259. control_plane_api/app/skills/builtin/__init__.py +37 -0
  260. control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
  261. control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
  262. control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
  263. control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
  264. control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
  265. control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
  266. control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
  267. control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
  268. control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
  269. control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
  270. control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
  271. control_plane_api/app/skills/builtin/docker/skill.py +104 -0
  272. control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
  273. control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
  274. control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
  275. control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
  276. control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
  277. control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
  278. control_plane_api/app/skills/builtin/python/__init__.py +4 -0
  279. control_plane_api/app/skills/builtin/python/skill.py +92 -0
  280. control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
  281. control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
  282. control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
  283. control_plane_api/app/skills/builtin/shell/skill.py +161 -0
  284. control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
  285. control_plane_api/app/skills/builtin/slack/skill.py +302 -0
  286. control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
  287. control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
  288. control_plane_api/app/skills/business_intelligence.py +189 -0
  289. control_plane_api/app/skills/config.py +63 -0
  290. control_plane_api/app/skills/loaders/__init__.py +14 -0
  291. control_plane_api/app/skills/loaders/base.py +73 -0
  292. control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
  293. control_plane_api/app/skills/registry.py +125 -0
  294. control_plane_api/app/utils/helpers.py +12 -0
  295. control_plane_api/app/utils/workflow_executor.py +354 -0
  296. control_plane_api/app/workflows/__init__.py +11 -0
  297. control_plane_api/app/workflows/agent_execution.py +520 -0
  298. control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
  299. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  300. control_plane_api/app/workflows/plan_generation.py +254 -0
  301. control_plane_api/app/workflows/team_execution.py +442 -0
  302. control_plane_api/scripts/seed_models.py +240 -0
  303. control_plane_api/scripts/validate_existing_tool_names.py +492 -0
  304. control_plane_api/shared/__init__.py +8 -0
  305. control_plane_api/shared/version.py +17 -0
  306. control_plane_api/test_deduplication.py +274 -0
  307. control_plane_api/test_executor_deduplication_e2e.py +309 -0
  308. control_plane_api/test_job_execution_e2e.py +283 -0
  309. control_plane_api/test_real_integration.py +193 -0
  310. control_plane_api/version.py +38 -0
  311. control_plane_api/worker/__init__.py +0 -0
  312. control_plane_api/worker/activities/__init__.py +0 -0
  313. control_plane_api/worker/activities/agent_activities.py +1585 -0
  314. control_plane_api/worker/activities/approval_activities.py +234 -0
  315. control_plane_api/worker/activities/job_activities.py +199 -0
  316. control_plane_api/worker/activities/runtime_activities.py +1167 -0
  317. control_plane_api/worker/activities/skill_activities.py +282 -0
  318. control_plane_api/worker/activities/team_activities.py +479 -0
  319. control_plane_api/worker/agent_runtime_server.py +370 -0
  320. control_plane_api/worker/binary_manager.py +333 -0
  321. control_plane_api/worker/config/__init__.py +31 -0
  322. control_plane_api/worker/config/worker_config.py +273 -0
  323. control_plane_api/worker/control_plane_client.py +1491 -0
  324. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  325. control_plane_api/worker/health_monitor.py +159 -0
  326. control_plane_api/worker/metrics.py +237 -0
  327. control_plane_api/worker/models/__init__.py +1 -0
  328. control_plane_api/worker/models/error_events.py +105 -0
  329. control_plane_api/worker/models/inputs.py +89 -0
  330. control_plane_api/worker/runtimes/__init__.py +35 -0
  331. control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
  332. control_plane_api/worker/runtimes/agno/__init__.py +34 -0
  333. control_plane_api/worker/runtimes/agno/config.py +248 -0
  334. control_plane_api/worker/runtimes/agno/hooks.py +385 -0
  335. control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
  336. control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
  337. control_plane_api/worker/runtimes/agno/utils.py +163 -0
  338. control_plane_api/worker/runtimes/base.py +979 -0
  339. control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
  340. control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
  341. control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
  342. control_plane_api/worker/runtimes/claude_code/config.py +829 -0
  343. control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
  344. control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
  345. control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
  346. control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
  347. control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
  348. control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
  349. control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
  350. control_plane_api/worker/runtimes/factory.py +173 -0
  351. control_plane_api/worker/runtimes/model_utils.py +107 -0
  352. control_plane_api/worker/runtimes/validation.py +93 -0
  353. control_plane_api/worker/services/__init__.py +1 -0
  354. control_plane_api/worker/services/agent_communication_tools.py +908 -0
  355. control_plane_api/worker/services/agent_executor.py +485 -0
  356. control_plane_api/worker/services/agent_executor_v2.py +793 -0
  357. control_plane_api/worker/services/analytics_collector.py +457 -0
  358. control_plane_api/worker/services/analytics_service.py +464 -0
  359. control_plane_api/worker/services/approval_tools.py +310 -0
  360. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  361. control_plane_api/worker/services/cancellation_manager.py +177 -0
  362. control_plane_api/worker/services/code_ingestion_tools.py +465 -0
  363. control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
  364. control_plane_api/worker/services/data_visualization.py +834 -0
  365. control_plane_api/worker/services/event_publisher.py +531 -0
  366. control_plane_api/worker/services/jira_tools.py +257 -0
  367. control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
  368. control_plane_api/worker/services/runtime_analytics.py +328 -0
  369. control_plane_api/worker/services/session_service.py +365 -0
  370. control_plane_api/worker/services/skill_context_enhancement.py +181 -0
  371. control_plane_api/worker/services/skill_factory.py +471 -0
  372. control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
  373. control_plane_api/worker/services/team_executor.py +715 -0
  374. control_plane_api/worker/services/team_executor_v2.py +1866 -0
  375. control_plane_api/worker/services/tool_enforcement.py +254 -0
  376. control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
  377. control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
  378. control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
  379. control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
  380. control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
  381. control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
  382. control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
  383. control_plane_api/worker/services/workflow_executor/models.py +142 -0
  384. control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
  385. control_plane_api/worker/skills/__init__.py +12 -0
  386. control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
  387. control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
  388. control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
  389. control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
  390. control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
  391. control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
  392. control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
  393. control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
  394. control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
  395. control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
  396. control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
  397. control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
  398. control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
  399. control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
  400. control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
  401. control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
  402. control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
  403. control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
  404. control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
  405. control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
  406. control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
  407. control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
  408. control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
  409. control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
  410. control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
  411. control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
  412. control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
  413. control_plane_api/worker/skills/loaders/__init__.py +5 -0
  414. control_plane_api/worker/skills/loaders/base.py +23 -0
  415. control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
  416. control_plane_api/worker/skills/registry.py +208 -0
  417. control_plane_api/worker/tests/__init__.py +1 -0
  418. control_plane_api/worker/tests/conftest.py +12 -0
  419. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  420. control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
  421. control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
  422. control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
  423. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  424. control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
  425. control_plane_api/worker/tests/integration/__init__.py +0 -0
  426. control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
  427. control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
  428. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  429. control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
  430. control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
  431. control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
  432. control_plane_api/worker/tests/unit/__init__.py +0 -0
  433. control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
  434. control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
  435. control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
  436. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  437. control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
  438. control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
  439. control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
  440. control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
  441. control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
  442. control_plane_api/worker/utils/__init__.py +1 -0
  443. control_plane_api/worker/utils/chunk_batcher.py +330 -0
  444. control_plane_api/worker/utils/environment.py +65 -0
  445. control_plane_api/worker/utils/error_publisher.py +260 -0
  446. control_plane_api/worker/utils/event_batcher.py +256 -0
  447. control_plane_api/worker/utils/logging_config.py +335 -0
  448. control_plane_api/worker/utils/logging_helper.py +326 -0
  449. control_plane_api/worker/utils/parameter_validator.py +120 -0
  450. control_plane_api/worker/utils/retry_utils.py +60 -0
  451. control_plane_api/worker/utils/streaming_utils.py +665 -0
  452. control_plane_api/worker/utils/tool_validation.py +332 -0
  453. control_plane_api/worker/utils/workspace_manager.py +163 -0
  454. control_plane_api/worker/websocket_client.py +393 -0
  455. control_plane_api/worker/worker.py +1297 -0
  456. control_plane_api/worker/workflows/__init__.py +0 -0
  457. control_plane_api/worker/workflows/agent_execution.py +909 -0
  458. control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
  459. control_plane_api/worker/workflows/team_execution.py +611 -0
  460. kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
  461. kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
  462. kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
  463. kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
  464. kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
  465. kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
  466. scripts/__init__.py +1 -0
  467. scripts/migrations.py +39 -0
  468. scripts/seed_worker_queues.py +128 -0
  469. scripts/setup_agent_runtime.py +142 -0
  470. worker_internal/__init__.py +1 -0
  471. worker_internal/planner/__init__.py +1 -0
  472. worker_internal/planner/activities.py +1499 -0
  473. worker_internal/planner/agent_tools.py +197 -0
  474. worker_internal/planner/event_models.py +148 -0
  475. worker_internal/planner/event_publisher.py +67 -0
  476. worker_internal/planner/models.py +199 -0
  477. worker_internal/planner/retry_logic.py +134 -0
  478. worker_internal/planner/worker.py +300 -0
  479. 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