flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (359) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +678 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +79 -0
  5. flock/cli.py +75 -0
  6. flock/components.py +173 -0
  7. flock/dashboard/__init__.py +28 -0
  8. flock/dashboard/collector.py +283 -0
  9. flock/dashboard/events.py +182 -0
  10. flock/dashboard/launcher.py +230 -0
  11. flock/dashboard/service.py +537 -0
  12. flock/dashboard/websocket.py +235 -0
  13. flock/engines/__init__.py +6 -0
  14. flock/engines/dspy_engine.py +856 -0
  15. flock/examples.py +128 -0
  16. flock/{core/util → helper}/cli_helper.py +4 -3
  17. flock/{core/logging → logging}/__init__.py +2 -3
  18. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  19. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  20. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
  21. flock/{core/logging → logging}/logging.py +77 -61
  22. flock/{core/logging → logging}/telemetry.py +20 -26
  23. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  24. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
  25. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  26. flock/{core/logging → logging}/trace_and_logged.py +20 -24
  27. flock/mcp/__init__.py +91 -0
  28. flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
  29. flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
  30. flock/mcp/manager.py +255 -0
  31. flock/mcp/servers/sse/__init__.py +1 -1
  32. flock/mcp/servers/sse/flock_sse_server.py +11 -53
  33. flock/mcp/servers/stdio/__init__.py +1 -1
  34. flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
  35. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
  36. flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
  37. flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
  38. flock/mcp/types/__init__.py +42 -0
  39. flock/{core/mcp → mcp}/types/callbacks.py +9 -15
  40. flock/{core/mcp → mcp}/types/factories.py +7 -6
  41. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  42. flock/{core/mcp → mcp}/types/types.py +70 -74
  43. flock/{core/mcp → mcp}/util/helpers.py +1 -1
  44. flock/orchestrator.py +645 -0
  45. flock/registry.py +148 -0
  46. flock/runtime.py +262 -0
  47. flock/service.py +140 -0
  48. flock/store.py +69 -0
  49. flock/subscription.py +111 -0
  50. flock/themes/andromeda.toml +1 -1
  51. flock/themes/apple-system-colors.toml +1 -1
  52. flock/themes/arcoiris.toml +1 -1
  53. flock/themes/atomonelight.toml +1 -1
  54. flock/themes/ayu copy.toml +1 -1
  55. flock/themes/ayu-light.toml +1 -1
  56. flock/themes/belafonte-day.toml +1 -1
  57. flock/themes/belafonte-night.toml +1 -1
  58. flock/themes/blulocodark.toml +1 -1
  59. flock/themes/breeze.toml +1 -1
  60. flock/themes/broadcast.toml +1 -1
  61. flock/themes/brogrammer.toml +1 -1
  62. flock/themes/builtin-dark.toml +1 -1
  63. flock/themes/builtin-pastel-dark.toml +1 -1
  64. flock/themes/catppuccin-latte.toml +1 -1
  65. flock/themes/catppuccin-macchiato.toml +1 -1
  66. flock/themes/catppuccin-mocha.toml +1 -1
  67. flock/themes/cga.toml +1 -1
  68. flock/themes/chalk.toml +1 -1
  69. flock/themes/ciapre.toml +1 -1
  70. flock/themes/coffee-theme.toml +1 -1
  71. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  72. flock/themes/dark+.toml +1 -1
  73. flock/themes/darkermatrix.toml +1 -1
  74. flock/themes/darkside.toml +1 -1
  75. flock/themes/desert.toml +1 -1
  76. flock/themes/django.toml +1 -1
  77. flock/themes/djangosmooth.toml +1 -1
  78. flock/themes/doomone.toml +1 -1
  79. flock/themes/dotgov.toml +1 -1
  80. flock/themes/dracula+.toml +1 -1
  81. flock/themes/duckbones.toml +1 -1
  82. flock/themes/encom.toml +1 -1
  83. flock/themes/espresso.toml +1 -1
  84. flock/themes/everblush.toml +1 -1
  85. flock/themes/fairyfloss.toml +1 -1
  86. flock/themes/fideloper.toml +1 -1
  87. flock/themes/fishtank.toml +1 -1
  88. flock/themes/flexoki-light.toml +1 -1
  89. flock/themes/floraverse.toml +1 -1
  90. flock/themes/framer.toml +1 -1
  91. flock/themes/galizur.toml +1 -1
  92. flock/themes/github.toml +1 -1
  93. flock/themes/grass.toml +1 -1
  94. flock/themes/grey-green.toml +1 -1
  95. flock/themes/gruvboxlight.toml +1 -1
  96. flock/themes/guezwhoz.toml +1 -1
  97. flock/themes/harper.toml +1 -1
  98. flock/themes/hax0r-blue.toml +1 -1
  99. flock/themes/hopscotch.256.toml +1 -1
  100. flock/themes/ic-green-ppl.toml +1 -1
  101. flock/themes/iceberg-dark.toml +1 -1
  102. flock/themes/japanesque.toml +1 -1
  103. flock/themes/jubi.toml +1 -1
  104. flock/themes/kibble.toml +1 -1
  105. flock/themes/kolorit.toml +1 -1
  106. flock/themes/kurokula.toml +1 -1
  107. flock/themes/materialdesigncolors.toml +1 -1
  108. flock/themes/matrix.toml +1 -1
  109. flock/themes/mellifluous.toml +1 -1
  110. flock/themes/midnight-in-mojave.toml +1 -1
  111. flock/themes/monokai-remastered.toml +1 -1
  112. flock/themes/monokai-soda.toml +1 -1
  113. flock/themes/neon.toml +1 -1
  114. flock/themes/neopolitan.toml +1 -1
  115. flock/themes/nord-light.toml +1 -1
  116. flock/themes/ocean.toml +1 -1
  117. flock/themes/onehalfdark.toml +1 -1
  118. flock/themes/onehalflight.toml +1 -1
  119. flock/themes/palenighthc.toml +1 -1
  120. flock/themes/paulmillr.toml +1 -1
  121. flock/themes/pencildark.toml +1 -1
  122. flock/themes/pnevma.toml +1 -1
  123. flock/themes/purple-rain.toml +1 -1
  124. flock/themes/purplepeter.toml +1 -1
  125. flock/themes/raycast-dark.toml +1 -1
  126. flock/themes/red-sands.toml +1 -1
  127. flock/themes/relaxed.toml +1 -1
  128. flock/themes/retro.toml +1 -1
  129. flock/themes/rose-pine.toml +1 -1
  130. flock/themes/royal.toml +1 -1
  131. flock/themes/ryuuko.toml +1 -1
  132. flock/themes/sakura.toml +1 -1
  133. flock/themes/scarlet-protocol.toml +1 -1
  134. flock/themes/seoulbones-dark.toml +1 -1
  135. flock/themes/shades-of-purple.toml +1 -1
  136. flock/themes/smyck.toml +1 -1
  137. flock/themes/softserver.toml +1 -1
  138. flock/themes/solarized-darcula.toml +1 -1
  139. flock/themes/square.toml +1 -1
  140. flock/themes/sugarplum.toml +1 -1
  141. flock/themes/thayer-bright.toml +1 -1
  142. flock/themes/tokyonight.toml +1 -1
  143. flock/themes/tomorrow.toml +1 -1
  144. flock/themes/ubuntu.toml +1 -1
  145. flock/themes/ultradark.toml +1 -1
  146. flock/themes/ultraviolent.toml +1 -1
  147. flock/themes/unikitty.toml +1 -1
  148. flock/themes/urple.toml +1 -1
  149. flock/themes/vesper.toml +1 -1
  150. flock/themes/vimbones.toml +1 -1
  151. flock/themes/wildcherry.toml +1 -1
  152. flock/themes/wilmersdorf.toml +1 -1
  153. flock/themes/wryan.toml +1 -1
  154. flock/themes/xcodedarkhc.toml +1 -1
  155. flock/themes/xcodelight.toml +1 -1
  156. flock/themes/zenbones-light.toml +1 -1
  157. flock/themes/zenwritten-dark.toml +1 -1
  158. flock/utilities.py +301 -0
  159. flock/{components/utility → utility}/output_utility_component.py +68 -53
  160. flock/visibility.py +107 -0
  161. flock_core-0.5.56b0.dist-info/METADATA +747 -0
  162. flock_core-0.5.56b0.dist-info/RECORD +398 -0
  163. flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
  164. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/licenses/LICENSE +1 -1
  165. flock/adapter/__init__.py +0 -14
  166. flock/adapter/azure_adapter.py +0 -68
  167. flock/adapter/chroma_adapter.py +0 -73
  168. flock/adapter/faiss_adapter.py +0 -97
  169. flock/adapter/pinecone_adapter.py +0 -51
  170. flock/adapter/vector_base.py +0 -47
  171. flock/cli/assets/release_notes.md +0 -140
  172. flock/cli/config.py +0 -8
  173. flock/cli/constants.py +0 -36
  174. flock/cli/create_agent.py +0 -1
  175. flock/cli/create_flock.py +0 -280
  176. flock/cli/execute_flock.py +0 -620
  177. flock/cli/load_agent.py +0 -1
  178. flock/cli/load_examples.py +0 -1
  179. flock/cli/load_flock.py +0 -192
  180. flock/cli/load_release_notes.py +0 -20
  181. flock/cli/loaded_flock_cli.py +0 -254
  182. flock/cli/manage_agents.py +0 -459
  183. flock/cli/registry_management.py +0 -889
  184. flock/cli/runner.py +0 -41
  185. flock/cli/settings.py +0 -857
  186. flock/cli/utils.py +0 -135
  187. flock/cli/view_results.py +0 -29
  188. flock/cli/yaml_editor.py +0 -396
  189. flock/components/__init__.py +0 -30
  190. flock/components/evaluation/__init__.py +0 -9
  191. flock/components/evaluation/declarative_evaluation_component.py +0 -606
  192. flock/components/routing/__init__.py +0 -15
  193. flock/components/routing/conditional_routing_component.py +0 -494
  194. flock/components/routing/default_routing_component.py +0 -103
  195. flock/components/routing/llm_routing_component.py +0 -206
  196. flock/components/utility/__init__.py +0 -22
  197. flock/components/utility/example_utility_component.py +0 -250
  198. flock/components/utility/feedback_utility_component.py +0 -206
  199. flock/components/utility/memory_utility_component.py +0 -550
  200. flock/components/utility/metrics_utility_component.py +0 -700
  201. flock/config.py +0 -61
  202. flock/core/__init__.py +0 -110
  203. flock/core/agent/__init__.py +0 -16
  204. flock/core/agent/default_agent.py +0 -216
  205. flock/core/agent/flock_agent_components.py +0 -104
  206. flock/core/agent/flock_agent_execution.py +0 -101
  207. flock/core/agent/flock_agent_integration.py +0 -260
  208. flock/core/agent/flock_agent_lifecycle.py +0 -186
  209. flock/core/agent/flock_agent_serialization.py +0 -381
  210. flock/core/api/__init__.py +0 -10
  211. flock/core/api/custom_endpoint.py +0 -45
  212. flock/core/api/endpoints.py +0 -254
  213. flock/core/api/main.py +0 -162
  214. flock/core/api/models.py +0 -97
  215. flock/core/api/run_store.py +0 -224
  216. flock/core/api/runner.py +0 -44
  217. flock/core/api/service.py +0 -214
  218. flock/core/component/__init__.py +0 -15
  219. flock/core/component/agent_component_base.py +0 -309
  220. flock/core/component/evaluation_component.py +0 -62
  221. flock/core/component/routing_component.py +0 -74
  222. flock/core/component/utility_component.py +0 -69
  223. flock/core/config/flock_agent_config.py +0 -58
  224. flock/core/config/scheduled_agent_config.py +0 -40
  225. flock/core/context/context.py +0 -213
  226. flock/core/context/context_manager.py +0 -37
  227. flock/core/context/context_vars.py +0 -10
  228. flock/core/evaluation/utils.py +0 -396
  229. flock/core/execution/batch_executor.py +0 -369
  230. flock/core/execution/evaluation_executor.py +0 -438
  231. flock/core/execution/local_executor.py +0 -31
  232. flock/core/execution/opik_executor.py +0 -103
  233. flock/core/execution/temporal_executor.py +0 -164
  234. flock/core/flock.py +0 -634
  235. flock/core/flock_agent.py +0 -336
  236. flock/core/flock_factory.py +0 -613
  237. flock/core/flock_scheduler.py +0 -166
  238. flock/core/flock_server_manager.py +0 -136
  239. flock/core/interpreter/python_interpreter.py +0 -689
  240. flock/core/mcp/__init__.py +0 -1
  241. flock/core/mcp/flock_mcp_server.py +0 -680
  242. flock/core/mcp/mcp_client_manager.py +0 -201
  243. flock/core/mcp/types/__init__.py +0 -1
  244. flock/core/mixin/dspy_integration.py +0 -403
  245. flock/core/mixin/prompt_parser.py +0 -125
  246. flock/core/orchestration/__init__.py +0 -15
  247. flock/core/orchestration/flock_batch_processor.py +0 -94
  248. flock/core/orchestration/flock_evaluator.py +0 -113
  249. flock/core/orchestration/flock_execution.py +0 -295
  250. flock/core/orchestration/flock_initialization.py +0 -149
  251. flock/core/orchestration/flock_server_manager.py +0 -67
  252. flock/core/orchestration/flock_web_server.py +0 -117
  253. flock/core/registry/__init__.py +0 -45
  254. flock/core/registry/agent_registry.py +0 -69
  255. flock/core/registry/callable_registry.py +0 -139
  256. flock/core/registry/component_discovery.py +0 -142
  257. flock/core/registry/component_registry.py +0 -64
  258. flock/core/registry/config_mapping.py +0 -64
  259. flock/core/registry/decorators.py +0 -137
  260. flock/core/registry/registry_hub.py +0 -205
  261. flock/core/registry/server_registry.py +0 -57
  262. flock/core/registry/type_registry.py +0 -86
  263. flock/core/serialization/__init__.py +0 -13
  264. flock/core/serialization/callable_registry.py +0 -52
  265. flock/core/serialization/flock_serializer.py +0 -832
  266. flock/core/serialization/json_encoder.py +0 -41
  267. flock/core/serialization/secure_serializer.py +0 -175
  268. flock/core/serialization/serializable.py +0 -342
  269. flock/core/serialization/serialization_utils.py +0 -412
  270. flock/core/util/file_path_utils.py +0 -223
  271. flock/core/util/hydrator.py +0 -309
  272. flock/core/util/input_resolver.py +0 -164
  273. flock/core/util/loader.py +0 -59
  274. flock/core/util/splitter.py +0 -219
  275. flock/di.py +0 -27
  276. flock/platform/docker_tools.py +0 -49
  277. flock/platform/jaeger_install.py +0 -86
  278. flock/webapp/__init__.py +0 -1
  279. flock/webapp/app/__init__.py +0 -0
  280. flock/webapp/app/api/__init__.py +0 -0
  281. flock/webapp/app/api/agent_management.py +0 -241
  282. flock/webapp/app/api/execution.py +0 -709
  283. flock/webapp/app/api/flock_management.py +0 -129
  284. flock/webapp/app/api/registry_viewer.py +0 -30
  285. flock/webapp/app/chat.py +0 -665
  286. flock/webapp/app/config.py +0 -104
  287. flock/webapp/app/dependencies.py +0 -117
  288. flock/webapp/app/main.py +0 -1070
  289. flock/webapp/app/middleware.py +0 -113
  290. flock/webapp/app/models_ui.py +0 -7
  291. flock/webapp/app/services/__init__.py +0 -0
  292. flock/webapp/app/services/feedback_file_service.py +0 -363
  293. flock/webapp/app/services/flock_service.py +0 -337
  294. flock/webapp/app/services/sharing_models.py +0 -81
  295. flock/webapp/app/services/sharing_store.py +0 -762
  296. flock/webapp/app/templates/theme_mapper.html +0 -326
  297. flock/webapp/app/theme_mapper.py +0 -812
  298. flock/webapp/app/utils.py +0 -85
  299. flock/webapp/run.py +0 -215
  300. flock/webapp/static/css/chat.css +0 -301
  301. flock/webapp/static/css/components.css +0 -167
  302. flock/webapp/static/css/header.css +0 -39
  303. flock/webapp/static/css/layout.css +0 -46
  304. flock/webapp/static/css/sidebar.css +0 -127
  305. flock/webapp/static/css/two-pane.css +0 -48
  306. flock/webapp/templates/base.html +0 -200
  307. flock/webapp/templates/chat.html +0 -152
  308. flock/webapp/templates/chat_settings.html +0 -19
  309. flock/webapp/templates/flock_editor.html +0 -16
  310. flock/webapp/templates/index.html +0 -12
  311. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  312. flock/webapp/templates/partials/_agent_list.html +0 -18
  313. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  314. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  315. flock/webapp/templates/partials/_chat_container.html +0 -15
  316. flock/webapp/templates/partials/_chat_messages.html +0 -57
  317. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  318. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  319. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  320. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  321. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  322. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  323. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  324. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  325. flock/webapp/templates/partials/_execution_form.html +0 -118
  326. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  327. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  328. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  329. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  330. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  331. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  332. flock/webapp/templates/partials/_registry_table.html +0 -25
  333. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  334. flock/webapp/templates/partials/_results_display.html +0 -78
  335. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  336. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  337. flock/webapp/templates/partials/_settings_view.html +0 -36
  338. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  339. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  340. flock/webapp/templates/partials/_sidebar.html +0 -74
  341. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  342. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  343. flock/webapp/templates/partials/_theme_preview.html +0 -36
  344. flock/webapp/templates/registry_viewer.html +0 -84
  345. flock/webapp/templates/shared_run_page.html +0 -140
  346. flock/workflow/__init__.py +0 -0
  347. flock/workflow/activities.py +0 -196
  348. flock/workflow/agent_activities.py +0 -24
  349. flock/workflow/agent_execution_activity.py +0 -202
  350. flock/workflow/flock_workflow.py +0 -214
  351. flock/workflow/temporal_config.py +0 -96
  352. flock/workflow/temporal_setup.py +0 -68
  353. flock_core-0.5.0b28.dist-info/METADATA +0 -274
  354. flock_core-0.5.0b28.dist-info/RECORD +0 -561
  355. flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
  356. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  357. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  358. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  359. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
@@ -1,12 +1,12 @@
1
1
  """Base Config for MCP Clients."""
2
2
 
3
3
  import importlib
4
- from typing import Any, Literal, TypeVar
4
+ from typing import TYPE_CHECKING, Any, Literal, TypeVar
5
5
 
6
- import httpx
7
6
  from pydantic import BaseModel, ConfigDict, Field, create_model
7
+ from typing_extensions import Self
8
8
 
9
- from flock.core.mcp.types.types import (
9
+ from flock.mcp.types import (
10
10
  FlockListRootsMCPCallback,
11
11
  FlockLoggingMCPCallback,
12
12
  FlockMessageHandlerMCPCallback,
@@ -18,11 +18,11 @@ from flock.core.mcp.types.types import (
18
18
  StreamableHttpServerParameters,
19
19
  WebsocketServerParameters,
20
20
  )
21
- from flock.core.serialization.serializable import Serializable
22
- from flock.core.serialization.serialization_utils import (
23
- deserialize_item,
24
- serialize_item,
25
- )
21
+
22
+
23
+ if TYPE_CHECKING:
24
+ import httpx
25
+
26
26
 
27
27
  LoggingLevel = Literal[
28
28
  "debug",
@@ -43,7 +43,7 @@ D = TypeVar("D", bound="FlockMCPCachingConfiguration")
43
43
  E = TypeVar("E", bound="FlockMCPFeatureConfiguration")
44
44
 
45
45
 
46
- class FlockMCPCachingConfiguration(BaseModel, Serializable):
46
+ class FlockMCPCachingConfiguration(BaseModel):
47
47
  """Configuration for Caching in Clients."""
48
48
 
49
49
  tool_cache_max_size: float = Field(
@@ -98,19 +98,17 @@ class FlockMCPCachingConfiguration(BaseModel, Serializable):
98
98
  )
99
99
 
100
100
  @classmethod
101
- def from_dict(cls: type[D], data: dict[str, Any]) -> D:
101
+ def from_dict(cls, data: dict[str, Any]) -> Self:
102
102
  """Deserialize from a dict."""
103
- return cls(**{k: v for k, v in data.items()})
103
+ return cls(**dict(data.items()))
104
104
 
105
105
  @classmethod
106
- def with_fields(cls: type[D], **field_definitions) -> type[D]:
106
+ def with_fields(cls, **field_definitions) -> type[Self]:
107
107
  """Create a new config class with additional fields."""
108
- return create_model(
109
- f"Dynamic{cls.__name__}", __base__=cls, **field_definitions
110
- )
108
+ return create_model(f"Dynamic{cls.__name__}", __base__=cls, **field_definitions)
111
109
 
112
110
 
113
- class FlockMCPCallbackConfiguration(BaseModel, Serializable):
111
+ class FlockMCPCallbackConfiguration(BaseModel):
114
112
  """Base Configuration Class for Callbacks for Clients."""
115
113
 
116
114
  sampling_callback: FlockSamplingMCPCallback | None = Field(
@@ -136,59 +134,21 @@ class FlockMCPCallbackConfiguration(BaseModel, Serializable):
136
134
 
137
135
  def to_dict(self, path_type: str = "relative"):
138
136
  """Serialize the object."""
139
- # we need to register callables.
140
- data: dict[str, Any] = {}
141
- if self.sampling_callback:
142
- sampling_callback_data = serialize_item(self.sampling_callback)
143
- data["sampling_callback"] = sampling_callback_data
144
-
145
- if self.list_roots_callback:
146
- list_roots_callback_data = serialize_item(self.list_roots_callback)
147
- data["list_roots_callback"] = list_roots_callback_data
148
-
149
- if self.logging_callback:
150
- logging_callback_data = serialize_item(self.logging_callback)
151
- data["logging_callback"] = logging_callback_data
152
-
153
- if self.message_handler:
154
- message_handler_data = serialize_item(self.message_handler)
155
- data["message_handler"] = message_handler_data
156
-
157
- return data
137
+ # Callbacks are set programmatically and cannot be serialized
138
+ return {}
158
139
 
159
140
  @classmethod
160
- def from_dict(cls: type[A], data: dict[str, Any]) -> A:
141
+ def from_dict(cls, data: dict[str, Any]) -> Self:
161
142
  """Deserialize from a dict."""
162
- instance = cls()
163
- if data:
164
- if "sampling_callback" in data:
165
- instance.sampling_callback = deserialize_item(
166
- data["sampling_callback"]
167
- )
168
- if "list_roots_callback" in data:
169
- instance.list_roots_callback = deserialize_item(
170
- data["list_roots_callback"]
171
- )
172
- if "logging_callback" in data:
173
- instance.logging_callback = deserialize_item(
174
- data["logging_callback"]
175
- )
176
- if "message_handler" in data:
177
- instance.message_handler = deserialize_item(
178
- data["message_handler"]
179
- )
180
-
181
- return instance
143
+ return cls()
182
144
 
183
145
  @classmethod
184
- def with_fields(cls: type[A], **field_definitions) -> type[A]:
146
+ def with_fields(cls, **field_definitions) -> type[Self]:
185
147
  """Create a new config class with additional fields."""
186
- return create_model(
187
- f"Dynamic{cls.__name__}", __base__=cls, **field_definitions
188
- )
148
+ return create_model(f"Dynamic{cls.__name__}", __base__=cls, **field_definitions)
189
149
 
190
150
 
191
- class FlockMCPConnectionConfiguration(BaseModel, Serializable):
151
+ class FlockMCPConnectionConfiguration(BaseModel):
192
152
  """Base Configuration Class for Connection Parameters for a client."""
193
153
 
194
154
  max_retries: int = Field(
@@ -200,17 +160,15 @@ class FlockMCPConnectionConfiguration(BaseModel, Serializable):
200
160
  ..., description="Connection parameters for the server."
201
161
  )
202
162
 
203
- transport_type: Literal[
204
- "stdio", "websockets", "sse", "streamable_http", "custom"
205
- ] = Field(..., description="Type of transport to use.")
163
+ transport_type: Literal["stdio", "websockets", "sse", "streamable_http", "custom"] = Field(
164
+ ..., description="Type of transport to use."
165
+ )
206
166
 
207
167
  mount_points: list[MCPRoot] | None = Field(
208
168
  default=None, description="Initial Mountpoints to operate under."
209
169
  )
210
170
 
211
- read_timeout_seconds: float | int = Field(
212
- default=60 * 5, description="Read Timeout."
213
- )
171
+ read_timeout_seconds: float | int = Field(default=60 * 5, description="Read Timeout.")
214
172
 
215
173
  server_logging_level: LoggingLevel = Field(
216
174
  default="error",
@@ -230,14 +188,12 @@ class FlockMCPConnectionConfiguration(BaseModel, Serializable):
230
188
  mode="json",
231
189
  )
232
190
 
233
- data["connection_parameters"] = self.connection_parameters.to_dict(
234
- path_type=path_type
235
- )
191
+ data["connection_parameters"] = self.connection_parameters.to_dict(path_type=path_type)
236
192
 
237
193
  return data
238
194
 
239
195
  @classmethod
240
- def from_dict(cls: type[B], data: dict[str, Any]) -> B:
196
+ def from_dict(cls, data: dict[str, Any]) -> Self:
241
197
  """Deserialize from dict."""
242
198
  connection_params = data.get("connection_parameters")
243
199
  connection_params_obj = None
@@ -248,13 +204,13 @@ class FlockMCPConnectionConfiguration(BaseModel, Serializable):
248
204
  if auth_spec:
249
205
  # find the concrete implementation and
250
206
  # instantiate it.
251
- # find the concrete implementation for auth and instatiate it.
207
+ # find the concrete implementation for auth and instatiate it.
252
208
  impl = auth_spec.get("implementation", None)
253
209
  params = auth_spec.get("params", None)
254
210
  if impl and params:
255
- mod = importlib.import_module(impl["module_path"])
256
- real_cls = getattr(mod, impl["class_name"])
257
- auth_obj = real_cls(**{k: v for k, v in params.items()})
211
+ mod = importlib.import_module(impl["module_path"])
212
+ real_cls = getattr(mod, impl["class_name"])
213
+ auth_obj = real_cls(**dict(params.items()))
258
214
 
259
215
  if auth_obj:
260
216
  connection_params["auth"] = auth_obj
@@ -264,41 +220,36 @@ class FlockMCPConnectionConfiguration(BaseModel, Serializable):
264
220
  match kind:
265
221
  case "stdio":
266
222
  connection_params_obj = StdioServerParameters(
267
- **{k: v for k, v in connection_params.items()}
223
+ **dict(connection_params.items())
268
224
  )
269
225
  case "websockets":
270
226
  connection_params_obj = WebsocketServerParameters(
271
- **{k: v for k, v in connection_params.items()}
227
+ **dict(connection_params.items())
272
228
  )
273
229
  case "streamable_http":
274
230
  connection_params_obj = StreamableHttpServerParameters(
275
- **{k: v for k, v in connection_params.items()}
231
+ **dict(connection_params.items())
276
232
  )
277
233
  case "sse":
278
234
  connection_params_obj = SseServerParameters(
279
- **{k: v for k, v in connection_params.items()}
235
+ **dict(connection_params.items())
280
236
  )
281
237
  case _:
282
238
  # handle custom server params
283
- connection_params_obj = ServerParameters(
284
- **{k: v for k, v in connection_params.items()}
285
- )
239
+ connection_params_obj = ServerParameters(**dict(connection_params.items()))
286
240
 
287
241
  if connection_params_obj:
288
242
  data["connection_parameters"] = connection_params_obj
289
- return cls(**{k: v for k, v in data.items()})
290
- else:
291
- raise ValueError("No connection parameters provided.")
243
+ return cls(**dict(data.items()))
244
+ raise ValueError("No connection parameters provided.")
292
245
 
293
246
  @classmethod
294
- def with_fields(cls: type[B], **field_definitions) -> type[B]:
247
+ def with_fields(cls, **field_definitions) -> type[Self]:
295
248
  """Create a new config class with additional fields."""
296
- return create_model(
297
- f"Dynamic{cls.__name__}", __base__=cls, **field_definitions
298
- )
249
+ return create_model(f"Dynamic{cls.__name__}", __base__=cls, **field_definitions)
299
250
 
300
251
 
301
- class FlockMCPFeatureConfiguration(BaseModel, Serializable):
252
+ class FlockMCPFeatureConfiguration(BaseModel):
302
253
  """Base Configuration Class for switching MCP Features on and off."""
303
254
 
304
255
  roots_enabled: bool = Field(
@@ -319,10 +270,10 @@ class FlockMCPFeatureConfiguration(BaseModel, Serializable):
319
270
  tool_whitelist: list[str] | None = Field(
320
271
  default=None,
321
272
  description="Whitelist of tool names that are enabled for this MCP server. "
322
- "If provided, only tools with names in this list will be available "
323
- "from this server. Used in conjunction with allow_all_tools setting. "
324
- "Note: Agent-level tool filtering is generally preferred over "
325
- "server-level filtering for better granular control."
273
+ "If provided, only tools with names in this list will be available "
274
+ "from this server. Used in conjunction with allow_all_tools setting. "
275
+ "Note: Agent-level tool filtering is generally preferred over "
276
+ "server-level filtering for better granular control.",
326
277
  )
327
278
 
328
279
  prompts_enabled: bool = Field(
@@ -345,34 +296,30 @@ class FlockMCPFeatureConfiguration(BaseModel, Serializable):
345
296
  @classmethod
346
297
  def from_dict(cls, data: dict[str, Any]):
347
298
  """Deserialize from a dict."""
348
- return cls(**{k: v for k, v in data.items()})
299
+ return cls(**dict(data.items()))
349
300
 
350
301
  @classmethod
351
- def with_fields(cls: type[E], **field_definitions) -> type[E]:
302
+ def with_fields(cls, **field_definitions) -> type[Self]:
352
303
  """Create a new config class with additional fields."""
353
- return create_model(
354
- f"Dynamic{cls.__name__}", __base__=cls, **field_definitions
355
- )
304
+ return create_model(f"Dynamic{cls.__name__}", __base__=cls, **field_definitions)
356
305
 
357
306
 
358
- class FlockMCPConfiguration(BaseModel, Serializable):
307
+ class FlockMCPConfiguration(BaseModel):
359
308
  """Base Configuration Class for MCP Clients.
360
309
 
361
310
  Each Client should implement their own config
362
311
  model by inheriting from this class.
363
312
  """
364
313
 
365
- name: str = Field(
366
- ..., description="Name of the server the client connects to."
367
- )
314
+ name: str = Field(..., description="Name of the server the client connects to.")
368
315
 
369
316
  allow_all_tools: bool = Field(
370
317
  default=True,
371
318
  description="Whether to allow usage of all tools from this MCP server. "
372
- "When True (default), all tools are available unless restricted "
373
- "by tool_whitelist. When False, tool access is controlled entirely "
374
- "by tool_whitelist (if provided). Setting to False with no whitelist "
375
- "will block all tools from this server."
319
+ "When True (default), all tools are available unless restricted "
320
+ "by tool_whitelist. When False, tool access is controlled entirely "
321
+ "by tool_whitelist (if provided). Setting to False with no whitelist "
322
+ "will block all tools from this server.",
376
323
  )
377
324
 
378
325
  connection_config: FlockMCPConnectionConfiguration = Field(
@@ -425,16 +372,14 @@ class FlockMCPConfiguration(BaseModel, Serializable):
425
372
  return data
426
373
 
427
374
  @classmethod
428
- def from_dict(cls: type[C], data: dict[str, Any]) -> C:
375
+ def from_dict(cls, data: dict[str, Any]) -> Self:
429
376
  """Deserialize the class."""
430
377
  connection_config = data.pop("connection_config", None)
431
378
  caching_config = data.pop("caching_config", None)
432
379
  feature_config = data.pop("feature_config", None)
433
380
  callback_config = data.pop("callback_config", None)
434
381
 
435
- instance_data: dict[str, Any] = {
436
- "name": data["name"]
437
- }
382
+ instance_data: dict[str, Any] = {"name": data["name"]}
438
383
 
439
384
  if connection_config:
440
385
  # Forcing a square into a round hole
@@ -446,7 +391,9 @@ class FlockMCPConfiguration(BaseModel, Serializable):
446
391
  config_cls = FlockMCPConnectionConfiguration
447
392
  instance_data["connection_config"] = config_cls.from_dict(connection_config)
448
393
  else:
449
- raise ValueError(f"connection_config MUST be specified for '{data.get('name', 'unknown_server')}")
394
+ raise ValueError(
395
+ f"connection_config MUST be specified for '{data.get('name', 'unknown_server')}"
396
+ )
450
397
 
451
398
  if caching_config:
452
399
  try:
@@ -481,11 +428,9 @@ class FlockMCPConfiguration(BaseModel, Serializable):
481
428
  else:
482
429
  instance_data["callback_config"] = FlockMCPCallbackConfiguration()
483
430
 
484
- return cls(**{k: v for k, v in instance_data.items()})
431
+ return cls(**dict(instance_data.items()))
485
432
 
486
433
  @classmethod
487
- def with_fields(cls: type[C], **field_definitions) -> type[C]:
434
+ def with_fields(cls, **field_definitions) -> type[Self]:
488
435
  """Create a new config class with additional fields."""
489
- return create_model(
490
- f"Dynamic{cls.__name__}", __base__=cls, **field_definitions
491
- )
436
+ return create_model(f"Dynamic{cls.__name__}", __base__=cls, **field_definitions)
flock/mcp/manager.py ADDED
@@ -0,0 +1,255 @@
1
+ """MCP Client Manager for Connection Pooling and Lifecycle Management.
2
+
3
+ This module provides the FlockMCPClientManager class which manages MCP client
4
+ connections with per-(agent_id, run_id) isolation and lazy connection establishment.
5
+
6
+ Architecture Decisions:
7
+ - AD004: Per-(agent_id, run_id) Connection Isolation
8
+ Each unique (agent_id, run_id) pair gets its own set of isolated MCP connections
9
+ - AD005: Lazy Connection Establishment
10
+ Connections are only established when first requested
11
+ - AD007: Graceful Degradation on MCP Failures
12
+ Failures to connect to individual servers don't prevent agent execution
13
+ """
14
+
15
+ import asyncio
16
+ from typing import Any
17
+
18
+ from flock.logging.logging import get_logger
19
+ from flock.mcp.client import FlockMCPClient
20
+ from flock.mcp.config import FlockMCPConfiguration
21
+
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ class FlockMCPClientManager:
27
+ """Manages MCP client connections with per-(agent_id, run_id) isolation.
28
+
29
+ This manager implements lazy connection pooling where clients are only
30
+ initialized when first requested. Each unique (agent_id, run_id) pair
31
+ gets its own set of isolated MCP connections.
32
+
33
+ Architecture Decision: AD004 - Per-(agent_id, run_id) Connection Isolation
34
+
35
+ Example:
36
+ ```python
37
+ # Initialize manager with configurations
38
+ configs = {
39
+ "filesystem": FlockMCPConfiguration(
40
+ name="filesystem",
41
+ connection_config=connection_config,
42
+ feature_config=feature_config
43
+ )
44
+ }
45
+ manager = FlockMCPClientManager(configs)
46
+
47
+ # Get client for specific agent and run
48
+ client = await manager.get_client("filesystem", "agent_1", "run_123")
49
+
50
+ # Get all tools for agent
51
+ tools = await manager.get_tools_for_agent(
52
+ "agent_1", "run_123", {"filesystem"}
53
+ )
54
+
55
+ # Cleanup after run completes
56
+ await manager.cleanup_run("agent_1", "run_123")
57
+ ```
58
+ """
59
+
60
+ def __init__(self, configs: dict[str, FlockMCPConfiguration]):
61
+ """Initialize the manager with MCP server configurations.
62
+
63
+ Args:
64
+ configs: Dictionary mapping server names to their configurations
65
+ """
66
+ self._configs = configs
67
+ # Pool structure: (agent_id, run_id) → server_name → FlockMCPClient
68
+ self._pool: dict[tuple[str, str], dict[str, FlockMCPClient]] = {}
69
+ self._lock = asyncio.Lock()
70
+
71
+ async def get_client(self, server_name: str, agent_id: str, run_id: str) -> FlockMCPClient:
72
+ """Get or create an MCP client for the given context.
73
+
74
+ Architecture Decision: AD005 - Lazy Connection Establishment
75
+ Connections are only established when first requested.
76
+
77
+ Args:
78
+ server_name: Name of the MCP server
79
+ agent_id: Agent requesting the client
80
+ run_id: Current run identifier
81
+
82
+ Returns:
83
+ FlockMCPClient instance ready for use
84
+
85
+ Raises:
86
+ ValueError: If server_name not registered
87
+ """
88
+ if server_name not in self._configs:
89
+ raise ValueError(
90
+ f"MCP server '{server_name}' not registered. "
91
+ f"Available servers: {list(self._configs.keys())}"
92
+ )
93
+
94
+ key = (agent_id, run_id)
95
+
96
+ async with self._lock:
97
+ # Check if we have a client pool for this (agent, run)
98
+ if key not in self._pool:
99
+ self._pool[key] = {}
100
+
101
+ # Check if we have a client for this server
102
+ if server_name not in self._pool[key]:
103
+ logger.info(
104
+ f"Creating new MCP client for server '{server_name}' "
105
+ f"(agent={agent_id}, run={run_id})"
106
+ )
107
+ config = self._configs[server_name]
108
+
109
+ # Instantiate the correct concrete client class based on transport type
110
+ # Lazy import to avoid requiring all dependencies
111
+ transport_type = config.connection_config.transport_type
112
+ if transport_type == "stdio":
113
+ from flock.mcp.servers.stdio.flock_stdio_server import (
114
+ FlockStdioClient,
115
+ )
116
+
117
+ client = FlockStdioClient(config=config)
118
+ elif transport_type == "sse":
119
+ from flock.mcp.servers.sse.flock_sse_server import (
120
+ FlockSSEClient,
121
+ )
122
+
123
+ client = FlockSSEClient(config=config)
124
+ elif transport_type == "websocket":
125
+ from flock.mcp.servers.websockets.flock_websocket_server import (
126
+ FlockWSClient,
127
+ )
128
+
129
+ client = FlockWSClient(config=config)
130
+ elif transport_type == "streamable_http":
131
+ from flock.mcp.servers.streamable_http.flock_streamable_http_server import (
132
+ FlockStreamableHttpClient,
133
+ )
134
+
135
+ client = FlockStreamableHttpClient(config=config)
136
+ else:
137
+ raise ValueError(
138
+ f"Unsupported transport type: {transport_type}. "
139
+ f"Supported types: stdio, sse, websocket, streamable_http"
140
+ )
141
+
142
+ await client._connect()
143
+ self._pool[key][server_name] = client
144
+
145
+ return self._pool[key][server_name]
146
+
147
+ async def get_tools_for_agent(
148
+ self, agent_id: str, run_id: str, server_names: set[str]
149
+ ) -> dict[str, Any]:
150
+ """Get all tools from specified servers for an agent.
151
+
152
+ Architecture Decision: AD003 - Tool Namespacing
153
+ All tools are returned with format: {server}__{tool}
154
+
155
+ Args:
156
+ agent_id: Agent requesting tools
157
+ run_id: Current run identifier
158
+ server_names: Set of MCP server names to fetch tools from
159
+
160
+ Returns:
161
+ Dictionary mapping namespaced tool names to tool definitions
162
+
163
+ Note:
164
+ Architecture Decision: AD007 - Graceful Degradation
165
+ If a server fails to load, we log the error and continue with
166
+ other servers rather than failing the entire operation.
167
+ """
168
+ tools = {}
169
+
170
+ for server_name in server_names:
171
+ try:
172
+ client = await self.get_client(server_name, agent_id, run_id)
173
+ server_tools = await client.get_tools(agent_id, run_id)
174
+
175
+ # Apply namespacing: AD003
176
+ for tool in server_tools:
177
+ namespaced_name = f"{server_name}__{tool.name}"
178
+ tools[namespaced_name] = {
179
+ "server_name": server_name,
180
+ "original_name": tool.name,
181
+ "tool": tool,
182
+ "client": client,
183
+ }
184
+
185
+ logger.debug(
186
+ f"Loaded {len(server_tools)} tools from server '{server_name}' "
187
+ f"for agent {agent_id}"
188
+ )
189
+
190
+ except Exception as e:
191
+ # Architecture Decision: AD007 - Graceful Degradation
192
+ logger.exception(
193
+ f"Failed to load tools from MCP server '{server_name}': {e}. "
194
+ f"Agent {agent_id} will continue without these tools."
195
+ )
196
+ # Continue loading other servers
197
+ continue
198
+
199
+ return tools
200
+
201
+ async def cleanup_run(self, agent_id: str, run_id: str) -> None:
202
+ """Clean up all MCP connections for a completed run.
203
+
204
+ Args:
205
+ agent_id: Agent identifier
206
+ run_id: Run identifier to clean up
207
+ """
208
+ key = (agent_id, run_id)
209
+
210
+ async with self._lock:
211
+ if key in self._pool:
212
+ logger.info(f"Cleaning up MCP connections for run {run_id}")
213
+ clients = self._pool[key]
214
+
215
+ # Disconnect all clients for this run
216
+ for server_name, client in clients.items():
217
+ try:
218
+ await client.disconnect()
219
+ logger.debug(f"Disconnected from MCP server '{server_name}'")
220
+ except Exception as e:
221
+ logger.warning(f"Error disconnecting from '{server_name}': {e}")
222
+
223
+ # Remove from pool
224
+ del self._pool[key]
225
+
226
+ async def cleanup_all(self) -> None:
227
+ """Clean up all MCP connections (orchestrator shutdown)."""
228
+ async with self._lock:
229
+ logger.info("Shutting down all MCP connections")
230
+
231
+ for _key, clients in self._pool.items():
232
+ for server_name, client in clients.items():
233
+ try:
234
+ await client.disconnect()
235
+ except Exception as e:
236
+ logger.warning(f"Error disconnecting from '{server_name}': {e}")
237
+
238
+ self._pool.clear()
239
+ logger.info("All MCP connections closed")
240
+
241
+ def list_servers(self) -> dict[str, dict[str, Any]]:
242
+ """List all registered MCP servers with their configurations.
243
+
244
+ Returns:
245
+ Dictionary mapping server names to configuration metadata
246
+ """
247
+ return {
248
+ name: {
249
+ "transport_type": config.connection_config.transport_type,
250
+ "tools_enabled": config.feature_config.tools_enabled,
251
+ "prompts_enabled": config.feature_config.prompts_enabled,
252
+ "sampling_enabled": config.feature_config.sampling_enabled,
253
+ }
254
+ for name, config in self._configs.items()
255
+ }
@@ -1 +1 @@
1
- """Default SSE Server Implementation for Flock."""
1
+ """Default SSE Server Implementation for flock."""