flock-core 0.4.3__py3-none-any.whl → 0.4.5__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 (38) hide show
  1. flock/core/__init__.py +11 -0
  2. flock/core/flock.py +144 -42
  3. flock/core/flock_agent.py +117 -4
  4. flock/core/flock_evaluator.py +1 -1
  5. flock/core/flock_factory.py +290 -2
  6. flock/core/flock_module.py +101 -0
  7. flock/core/flock_registry.py +39 -2
  8. flock/core/flock_server_manager.py +136 -0
  9. flock/core/logging/telemetry.py +1 -1
  10. flock/core/mcp/__init__.py +1 -0
  11. flock/core/mcp/flock_mcp_server.py +614 -0
  12. flock/core/mcp/flock_mcp_tool_base.py +201 -0
  13. flock/core/mcp/mcp_client.py +658 -0
  14. flock/core/mcp/mcp_client_manager.py +201 -0
  15. flock/core/mcp/mcp_config.py +237 -0
  16. flock/core/mcp/types/__init__.py +1 -0
  17. flock/core/mcp/types/callbacks.py +86 -0
  18. flock/core/mcp/types/factories.py +111 -0
  19. flock/core/mcp/types/handlers.py +240 -0
  20. flock/core/mcp/types/types.py +157 -0
  21. flock/core/mcp/util/__init__.py +0 -0
  22. flock/core/mcp/util/helpers.py +23 -0
  23. flock/core/mixin/dspy_integration.py +45 -12
  24. flock/core/serialization/flock_serializer.py +52 -1
  25. flock/core/util/spliter.py +4 -0
  26. flock/evaluators/declarative/declarative_evaluator.py +4 -3
  27. flock/mcp/servers/sse/__init__.py +1 -0
  28. flock/mcp/servers/sse/flock_sse_server.py +139 -0
  29. flock/mcp/servers/stdio/__init__.py +1 -0
  30. flock/mcp/servers/stdio/flock_stdio_server.py +138 -0
  31. flock/mcp/servers/websockets/__init__.py +1 -0
  32. flock/mcp/servers/websockets/flock_websocket_server.py +119 -0
  33. flock/modules/performance/metrics_module.py +159 -1
  34. {flock_core-0.4.3.dist-info → flock_core-0.4.5.dist-info}/METADATA +4 -2
  35. {flock_core-0.4.3.dist-info → flock_core-0.4.5.dist-info}/RECORD +38 -18
  36. {flock_core-0.4.3.dist-info → flock_core-0.4.5.dist-info}/WHEEL +0 -0
  37. {flock_core-0.4.3.dist-info → flock_core-0.4.5.dist-info}/entry_points.txt +0 -0
  38. {flock_core-0.4.3.dist-info → flock_core-0.4.5.dist-info}/licenses/LICENSE +0 -0
flock/core/__init__.py CHANGED
@@ -14,6 +14,12 @@ from flock.core.flock_registry import (
14
14
  flock_type,
15
15
  get_registry,
16
16
  )
17
+ from flock.core.mcp.flock_mcp_server import (
18
+ FlockMCPServerBase,
19
+ )
20
+ from flock.core.mcp.flock_mcp_tool_base import FlockMCPToolBase
21
+ from flock.core.mcp.mcp_client import FlockMCPClientBase
22
+ from flock.core.mcp.mcp_client_manager import FlockMCPClientManagerBase
17
23
 
18
24
  __all__ = [
19
25
  "Flock",
@@ -22,6 +28,11 @@ __all__ = [
22
28
  "FlockEvaluator",
23
29
  "FlockEvaluatorConfig",
24
30
  "FlockFactory",
31
+ "FlockMCPClientBase",
32
+ "FlockMCPClientManagerBase",
33
+ "FlockMCPServerBase",
34
+ "FlockMCPServerConfig",
35
+ "FlockMCPToolBase",
25
36
  "FlockModule",
26
37
  "FlockModuleConfig",
27
38
  "FlockRegistry",
flock/core/flock.py CHANGED
@@ -22,6 +22,9 @@ _R = TypeVar("_R")
22
22
  from box import Box
23
23
  from temporalio import workflow
24
24
 
25
+ from flock.core.flock_server_manager import FlockServerManager
26
+ from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
27
+
25
28
  with workflow.unsafe.imports_passed_through():
26
29
  from datasets import Dataset # type: ignore
27
30
 
@@ -135,7 +138,14 @@ class Flock(BaseModel, Serializable):
135
138
  # Marked with underscore to indicate it's managed internally and accessed via property
136
139
  _agents: dict[str, FlockAgent]
137
140
  _start_agent_name: str | None = None # For potential pre-configuration
138
- _start_input: dict = {} # Instance attribute overwritten in __init__; kept for typing clarity
141
+ _start_input: dict = {} # For potential pre-configuration
142
+
143
+ # Internal server storage - not part of the Pydantic model for direct serialization
144
+ _servers: dict[str, FlockMCPServerBase]
145
+
146
+ # Async context-manager for startup and teardown of servers
147
+ # Not part of the pydantic model
148
+ _mgr: FlockServerManager
139
149
 
140
150
  # Pydantic v2 model config
141
151
  model_config = {
@@ -175,6 +185,7 @@ class Flock(BaseModel, Serializable):
175
185
  enable_temporal: bool = False,
176
186
  enable_logging: bool | list[str] = False,
177
187
  agents: list[FlockAgent] | None = None,
188
+ servers: list[FlockMCPServerBase] | None = None,
178
189
  temporal_config: TemporalWorkflowConfig | None = None,
179
190
  temporal_start_in_process_worker: bool = True,
180
191
  **kwargs,
@@ -198,12 +209,31 @@ class Flock(BaseModel, Serializable):
198
209
 
199
210
  # Initialize runtime attributes AFTER super().__init__()
200
211
  self._agents = {}
212
+ self._servers = {}
201
213
  self._start_agent_name = None
202
214
  self._start_input = {}
215
+ self._mgr = FlockServerManager()
203
216
 
204
217
  # Set up logging based on the enable_logging flag
205
218
  self._configure_logging(enable_logging) # Pass original value to _configure_logging
206
219
 
220
+ # Register passed servers
221
+ # (need to be registered first so that agents can retrieve them from the registry)
222
+ # This will also add them to the managed list of self._mgr
223
+ if servers:
224
+ from flock.core.mcp.flock_mcp_server import (
225
+ FlockMCPServerBase as ConcreteFlockMCPServer,
226
+ )
227
+
228
+ for server in servers:
229
+ if isinstance(server, ConcreteFlockMCPServer):
230
+ self.add_server(server)
231
+ else:
232
+ logger.warning(
233
+ f"Item provided in 'servers' list is not a FlockMCPServer: {type(server)}"
234
+ )
235
+
236
+
207
237
  # Register passed agents
208
238
  if agents:
209
239
  from flock.core.flock_agent import (
@@ -330,12 +360,48 @@ class Flock(BaseModel, Serializable):
330
360
  set_baggage("session_id", session_id)
331
361
  logger.debug(f"Generated new session_id: {session_id}")
332
362
 
333
- def add_agent(self, agent: FlockAgent) -> FlockAgent:
334
- """Adds an agent instance to this Flock configuration and registry."""
335
- from flock.core.flock_agent import (
336
- FlockAgent as ConcreteFlockAgent, # Local import
363
+ def add_server(self, server: FlockMCPServerBase) -> FlockMCPServerBase:
364
+ """Adds a server instance to this Flock configuration and registry as well as set it up to be managed by self._mgr."""
365
+ from flock.core.mcp.flock_mcp_server import (
366
+ FlockMCPServerBase as ConcreteFlockMCPServer,
337
367
  )
338
368
 
369
+ if not isinstance(server, ConcreteFlockMCPServer):
370
+ raise TypeError("Provided object is not a FlockMCPServer instance.")
371
+ if not server.config.name:
372
+ raise ValueError("Server must have a name.")
373
+
374
+ if server.config.name in self.servers:
375
+ raise ValueError(
376
+ f"Server with this name already exists. Name: '{server.config.name}'"
377
+ )
378
+
379
+ self._servers[server.config.name] = server
380
+ FlockRegistry.register_server(server) # Register globally.
381
+
382
+ # Make sure that the server is also added to
383
+ # the server_list managed by FlockServerManager
384
+ if not self._mgr:
385
+ self._mgr = FlockServerManager()
386
+
387
+ # Prepare server to be managed by the FlockServerManager
388
+ logger.info(f"Adding server '{server.config.name}' to managed list.")
389
+ self._mgr.add_server_sync(server=server)
390
+ logger.info(f"Server '{server.config.name}' is now on managed list.")
391
+
392
+ logger.info(
393
+ f"Server '{server.config.name}' added to Flock '{self.name}'"
394
+ )
395
+ return server
396
+
397
+ def add_agent(self, agent: FlockAgent) -> FlockAgent:
398
+ """Adds an agent instance to this Flock configuration and registry.
399
+
400
+ This also registers all servers attached to the agent, if they have not been registered
401
+ beforehand.
402
+ """
403
+ from flock.core.flock_agent import FlockAgent as ConcreteFlockAgent
404
+
339
405
  if not isinstance(agent, ConcreteFlockAgent):
340
406
  raise TypeError("Provided object is not a FlockAgent instance.")
341
407
  if not agent.name:
@@ -372,6 +438,11 @@ class Flock(BaseModel, Serializable):
372
438
  """Returns the dictionary of agents managed by this Flock instance."""
373
439
  return self._agents
374
440
 
441
+ @property
442
+ def servers(self) -> dict[str, FlockMCPServerBase]:
443
+ """Returns the dictionary of servers managed by this Flock instance."""
444
+ return self._servers
445
+
375
446
  def run(
376
447
  self,
377
448
  start_agent: FlockAgent | str | None = None,
@@ -380,6 +451,7 @@ class Flock(BaseModel, Serializable):
380
451
  run_id: str = "",
381
452
  box_result: bool = True,
382
453
  agents: list[FlockAgent] | None = None,
454
+ servers: list[FlockMCPServerBase] | None = None,
383
455
  memo: dict[str, Any] | None = None
384
456
  ) -> Box | dict:
385
457
  return self._run_sync(
@@ -390,6 +462,7 @@ class Flock(BaseModel, Serializable):
390
462
  run_id=run_id,
391
463
  box_result=box_result,
392
464
  agents=agents,
465
+ servers=servers,
393
466
  memo=memo,
394
467
  )
395
468
  )
@@ -403,15 +476,28 @@ class Flock(BaseModel, Serializable):
403
476
  run_id: str = "",
404
477
  box_result: bool = True,
405
478
  agents: list[FlockAgent] | None = None,
479
+ servers: list[FlockMCPServerBase] | None = None,
406
480
  memo: dict[str, Any] | None = None,
407
481
  ) -> Box | dict:
408
482
  """Entry point for running an agent system asynchronously."""
409
- from flock.core.flock_agent import (
410
- FlockAgent as ConcreteFlockAgent, # Local import
483
+ # Import here to allow forward reference resolution
484
+ from flock.core.flock_agent import FlockAgent as ConcreteFlockAgent
485
+ from flock.core.mcp.flock_mcp_server import (
486
+ FlockMCPServerBase as ConcreteFlockServer,
411
487
  )
412
488
 
413
489
  with tracer.start_as_current_span("flock.run_async") as span:
414
- # Add passed agents first
490
+ # Add passed servers so that agents have access to them.
491
+ if servers:
492
+ for server_obj in servers:
493
+ if isinstance(server_obj, ConcreteFlockServer):
494
+ self.add_server(server=server_obj)
495
+ else:
496
+ logger.warning(
497
+ f"Item in 'servers' list is not a FlockMCPServer: {type(server_obj)}"
498
+ )
499
+
500
+ # Add passed agents
415
501
  if agents:
416
502
  for agent_obj in agents:
417
503
  if isinstance(agent_obj, ConcreteFlockAgent):
@@ -434,7 +520,7 @@ class Flock(BaseModel, Serializable):
434
520
 
435
521
  # Default to first agent if only one exists and none specified
436
522
  if not start_agent_name and len(self._agents) == 1:
437
- start_agent_name = list(self._agents.keys())[0]
523
+ start_agent_name = next(iter(self._agents.keys()))
438
524
  elif not start_agent_name:
439
525
  raise ValueError(
440
526
  "No start_agent specified and multiple/no agents exist in the Flock instance."
@@ -497,44 +583,60 @@ class Flock(BaseModel, Serializable):
497
583
  self.temporal_config.model_dump(mode="json"),
498
584
  )
499
585
 
500
- logger.info(
501
- "Starting agent execution",
502
- agent=start_agent_name,
503
- enable_temporal=self.enable_temporal,
504
- )
505
-
506
- # Execute workflow
507
- if not self.enable_temporal:
508
- result = await run_local_workflow(
509
- run_context, box_result=False # Boxing handled below
510
- )
511
- else:
512
- result = await run_temporal_workflow(
513
- self, # Pass the Flock instance
514
- run_context,
515
- box_result=False, # Boxing handled below
516
- memo=memo,
586
+ # At this point, initial setup is done
587
+ # and flock is ready to execute it's agent_workflow.
588
+ # Befor that happens, the ServerManager needs to
589
+ # get the Servers up and running (Populate pools, build connections, start scripts, etc.)
590
+ async with self._mgr:
591
+ # Enter the manager's async context,
592
+ # running it's __aenter__ method and starting all registered servers
593
+ # after this block ends, self._mgr's __aexit__ will be called
594
+ # all servers will be torn down.
595
+ logger.info(
596
+ f"Entering managed server context. Servers starting up."
517
597
  )
518
598
 
519
- span.set_attribute("result.type", str(type(result)))
520
- result_str = str(result)
521
- span.set_attribute(
522
- "result.preview",
523
- result_str[:1000]
524
- + ("..." if len(result_str) > 1000 else ""),
525
- )
599
+ logger.info(
600
+ "Starting agent execution",
601
+ agent=start_agent_name,
602
+ enable_temporal=self.enable_temporal,
603
+ )
526
604
 
527
- if box_result:
528
- try:
529
- logger.debug("Boxing final result.")
530
- return Box(result)
531
- except ImportError: # Should not happen as Box is a direct dependency
532
- logger.warning(
533
- "Box library not installed (should be direct dependency), returning raw dict."
605
+ # Execute workflow
606
+ if not self.enable_temporal:
607
+ result = await run_local_workflow(
608
+ run_context, box_result=False # Boxing handled below
609
+ )
610
+ else:
611
+ result = await run_temporal_workflow(
612
+ self, # Pass the Flock instance
613
+ run_context,
614
+ box_result=False, # Boxing handled below
615
+ memo=memo,
534
616
  )
617
+
618
+ span.set_attribute("result.type", str(type(result)))
619
+ result_str = str(result)
620
+ span.set_attribute(
621
+ "result.preview",
622
+ result_str[:1000]
623
+ + ("..." if len(result_str) > 1000 else ""),
624
+ )
625
+
626
+ if box_result:
627
+ try:
628
+ logger.debug("Boxing final result.")
629
+ return Box(result)
630
+ except ImportError:
631
+ logger.warning(
632
+ "Box library not installed, returning raw dict."
633
+ )
634
+ return result
635
+ else:
535
636
  return result
536
- else:
537
- return result
637
+
638
+ # The context of self._mgr ends here, meaning, that servers will
639
+ # be cleaned up and shut down.
538
640
 
539
641
  except Exception as e:
540
642
  logger.error(
flock/core/flock_agent.py CHANGED
@@ -4,11 +4,13 @@
4
4
  import asyncio
5
5
  import json
6
6
  import os
7
+ import uuid
7
8
  from abc import ABC
8
9
  from collections.abc import Callable
9
10
  from datetime import datetime
10
11
  from typing import TYPE_CHECKING, Any, TypeVar
11
12
 
13
+ from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
12
14
  from flock.core.serialization.json_encoder import FlockJSONEncoder
13
15
  from flock.workflow.temporal_config import TemporalActivityConfig
14
16
 
@@ -59,7 +61,13 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
59
61
  Inherits from Pydantic BaseModel, ABC, DSPyIntegrationMixin, and Serializable.
60
62
  """
61
63
 
64
+ agent_id: str = Field(
65
+ default_factory=lambda: str(uuid.uuid4()),
66
+ description="Internal, Unique UUID4 for this agent instance. No need to set it manually. Used for MCP features.",
67
+ )
68
+
62
69
  name: str = Field(..., description="Unique identifier for the agent.")
70
+
63
71
  model: str | None = Field(
64
72
  None,
65
73
  description="The model identifier to use (e.g., 'openai/gpt-4o'). If None, uses Flock's default.",
@@ -88,6 +96,11 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
88
96
  description="List of callable tools the agent can use. These must be registered.",
89
97
  )
90
98
  )
99
+ servers: list[str | FlockMCPServerBase] | None = Field(
100
+ default=None,
101
+ description="List of MCP Servers the agent can use to enhance its capabilities. These must be registered.",
102
+ )
103
+
91
104
  write_to_file: bool = Field(
92
105
  default=False,
93
106
  description="Write the agent's output to a file.",
@@ -132,9 +145,11 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
132
145
  input: SignatureType = None,
133
146
  output: SignatureType = None,
134
147
  tools: list[Callable[..., Any]] | None = None,
148
+ servers: list[str | FlockMCPServerBase] | None = None,
135
149
  evaluator: "FlockEvaluator | None" = None,
136
150
  handoff_router: "FlockRouter | None" = None,
137
- modules: dict[str, "FlockModule"] | None = None, # Use dict for modules
151
+ # Use dict for modules
152
+ modules: dict[str, "FlockModule"] | None = None,
138
153
  write_to_file: bool = False,
139
154
  wait_for_input: bool = False,
140
155
  temporal_activity_config: TemporalActivityConfig | None = None,
@@ -147,6 +162,7 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
147
162
  input=input, # Store the raw input spec
148
163
  output=output, # Store the raw output spec
149
164
  tools=tools,
165
+ servers=servers,
150
166
  write_to_file=write_to_file,
151
167
  wait_for_input=wait_for_input,
152
168
  evaluator=evaluator,
@@ -335,6 +351,37 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
335
351
  # For now, assume evaluator handles tool resolution if necessary
336
352
  registered_tools = self.tools
337
353
 
354
+ # Retrieve available mcp_tools if the evaluator needs them
355
+ mcp_tools = []
356
+ if self.servers:
357
+ from flock.core.flock_registry import get_registry
358
+
359
+ FlockRegistry = get_registry() # Get the registry
360
+ for server in self.servers:
361
+ registered_server: FlockMCPServerBase | None = None
362
+ server_tools = []
363
+ if isinstance(server, FlockMCPServerBase):
364
+ # check if registered
365
+ server_name = server.config.name
366
+ registered_server = FlockRegistry.get_server(
367
+ server_name
368
+ )
369
+ else:
370
+ # servers must be registered.
371
+ registered_server = FlockRegistry.get_server(
372
+ name=server
373
+ )
374
+ if registered_server:
375
+ server_tools = await registered_server.get_tools(
376
+ agent_id=self.agent_id,
377
+ run_id=self.context.run_id,
378
+ )
379
+ else:
380
+ logger.warning(
381
+ f"No Server with name '{server}' registered! Skipping."
382
+ )
383
+ mcp_tools = mcp_tools + server_tools
384
+
338
385
  # --------------------------------------------------
339
386
  # Optional DI middleware pipeline
340
387
  # --------------------------------------------------
@@ -391,7 +438,10 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
391
438
  else:
392
439
  # No DI container – standard execution
393
440
  result = await self.evaluator.evaluate(
394
- self, current_inputs, registered_tools
441
+ self,
442
+ current_inputs,
443
+ registered_tools,
444
+ mcp_tools=mcp_tools,
395
445
  )
396
446
  except Exception as eval_error:
397
447
  logger.error(
@@ -662,7 +712,14 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
662
712
 
663
713
  FlockRegistry = get_registry()
664
714
 
665
- exclude = ["context", "evaluator", "modules", "handoff_router", "tools"]
715
+ exclude = [
716
+ "context",
717
+ "evaluator",
718
+ "modules",
719
+ "handoff_router",
720
+ "tools",
721
+ "servers",
722
+ ]
666
723
 
667
724
  is_descrition_callable = False
668
725
  is_input_callable = False
@@ -755,6 +812,25 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
755
812
  f"Added {len(serialized_modules)} modules to agent '{self.name}'"
756
813
  )
757
814
 
815
+ # --- Serialize Servers ---
816
+ if self.servers:
817
+ logger.debug(
818
+ f"Serializing {len(self.servers)} servers for agent '{self.name}'"
819
+ )
820
+ serialized_servers = []
821
+ for server in self.servers:
822
+ if isinstance(server, FlockMCPServerBase):
823
+ serialized_servers.append(server.config.name)
824
+ else:
825
+ # Write it down as a list of server names.
826
+ serialized_servers.append(server)
827
+
828
+ if serialized_servers:
829
+ data["mcp_servers"] = serialized_servers
830
+ logger.debug(
831
+ f"Added {len(serialized_servers)} servers to agent '{self.name}'"
832
+ )
833
+
758
834
  # --- Serialize Tools (Callables) ---
759
835
  if self.tools:
760
836
  logger.debug(
@@ -848,6 +924,7 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
848
924
  component_configs = {}
849
925
  callable_configs = {}
850
926
  tool_config = []
927
+ servers_config = []
851
928
  agent_data = {}
852
929
 
853
930
  component_keys = [
@@ -863,6 +940,8 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
863
940
  ]
864
941
  tool_key = "tools"
865
942
 
943
+ servers_key = "mcp_servers"
944
+
866
945
  for key, value in data.items():
867
946
  if key in component_keys and value is not None:
868
947
  component_configs[key] = value
@@ -870,8 +949,11 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
870
949
  callable_configs[key] = value
871
950
  elif key == tool_key and value is not None:
872
951
  tool_config = value # Expecting a list of names
952
+ elif key == servers_key and value is not None:
953
+ servers_config = value # Expecting a list of names
873
954
  elif key not in component_keys + callable_keys + [
874
- tool_key
955
+ tool_key,
956
+ servers_key,
875
957
  ]: # Avoid double adding
876
958
  agent_data[key] = value
877
959
  # else: ignore keys that are None or already handled
@@ -987,6 +1069,37 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
987
1069
  exc_info=True,
988
1070
  )
989
1071
 
1072
+ # --- Deserialize Servers ---
1073
+ agent.servers = [] # Initialize Servers list.
1074
+ if servers_config:
1075
+ logger.debug(
1076
+ f"Deserializing {len(servers_config)} servers for '{agent.name}'"
1077
+ )
1078
+ # Agents keep track of server by getting a list of server names.
1079
+ # The server instances will be retrieved during runtime from the registry. (default behavior)
1080
+
1081
+ for server_name in servers_config:
1082
+ if isinstance(server_name, str):
1083
+ # Case 1 (default behavior): A server name is passe.
1084
+ agent.servers.append(server_name)
1085
+ elif isinstance(server_name, FlockMCPServerBase):
1086
+ # Case 2 (highly unlikely): If someone somehow manages to pass
1087
+ # an instance of a server during the deserialization step (however that might be achieved)
1088
+ # check the registry, if the server is already registered, if not, register it
1089
+ # and store the name in the servers list
1090
+ FlockRegistry = get_registry()
1091
+ server_exists = (
1092
+ FlockRegistry.get_server(server_name.config.name)
1093
+ is not None
1094
+ )
1095
+ if server_exists:
1096
+ agent.servers.append(server_name.config.name)
1097
+ else:
1098
+ FlockRegistry.register_server(
1099
+ server=server_name
1100
+ ) # register it.
1101
+ agent.servers.append(server_name.config.name)
1102
+
990
1103
  # --- Deserialize Callables ---
991
1104
  logger.debug(f"Deserializing callable fields for '{agent.name}'")
992
1105
  # available_callables = registry.get_all_callables() # Incorrect
@@ -47,7 +47,7 @@ class FlockEvaluator(ABC, BaseModel):
47
47
 
48
48
  @abstractmethod
49
49
  async def evaluate(
50
- self, agent: Any, inputs: dict[str, Any], tools: list[Any]
50
+ self, agent: Any, inputs: dict[str, Any], tools: list[Any], mcp_tools: list[Any] | None = None
51
51
  ) -> dict[str, Any]:
52
52
  """Evaluate inputs to produce outputs."""
53
53
  pass