fast-agent-mcp 0.0.7__py3-none-any.whl → 0.0.8__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 fast-agent-mcp might be problematic. Click here for more details.

Files changed (31) hide show
  1. {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/METADATA +22 -57
  2. {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/RECORD +29 -23
  3. mcp_agent/agents/agent.py +8 -4
  4. mcp_agent/app.py +5 -1
  5. mcp_agent/cli/commands/bootstrap.py +180 -121
  6. mcp_agent/cli/commands/setup.py +20 -16
  7. mcp_agent/core/__init__.py +0 -0
  8. mcp_agent/core/exceptions.py +47 -0
  9. mcp_agent/core/fastagent.py +176 -85
  10. mcp_agent/core/server_validation.py +44 -0
  11. mcp_agent/event_progress.py +4 -1
  12. mcp_agent/logging/rich_progress.py +11 -0
  13. mcp_agent/mcp/mcp_connection_manager.py +11 -2
  14. mcp_agent/resources/examples/data-analysis/analysis.py +35 -0
  15. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +20 -0
  16. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  17. mcp_agent/resources/examples/workflows/chaining.py +31 -0
  18. mcp_agent/resources/examples/{decorator/optimizer.py → workflows/evaluator.py} +7 -10
  19. mcp_agent/resources/examples/workflows/human_input.py +26 -0
  20. mcp_agent/resources/examples/{decorator → workflows}/orchestrator.py +20 -11
  21. mcp_agent/resources/examples/{decorator → workflows}/parallel.py +14 -18
  22. mcp_agent/resources/examples/{decorator → workflows}/router.py +9 -10
  23. mcp_agent/workflows/llm/augmented_llm_anthropic.py +48 -12
  24. mcp_agent/workflows/llm/augmented_llm_openai.py +38 -9
  25. mcp_agent/resources/examples/decorator/main.py +0 -26
  26. mcp_agent/resources/examples/decorator/tiny.py +0 -22
  27. {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/WHEEL +0 -0
  28. {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/entry_points.txt +0 -0
  29. {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/licenses/LICENSE +0 -0
  30. /mcp_agent/resources/examples/mcp_researcher/{main-evalopt.py → researcher-eval.py} +0 -0
  31. /mcp_agent/resources/examples/mcp_researcher/{main.py → researcher.py} +0 -0
@@ -9,6 +9,13 @@ import yaml
9
9
  import argparse
10
10
  from contextlib import asynccontextmanager
11
11
 
12
+ from mcp_agent.core.exceptions import (
13
+ AgentConfigError,
14
+ ServerConfigError,
15
+ ProviderKeyError,
16
+ ServerInitializationError,
17
+ )
18
+
12
19
  from mcp_agent.app import MCPApp
13
20
  from mcp_agent.agents.agent import Agent, AgentConfig
14
21
  from mcp_agent.context_dependent import ContextDependent
@@ -28,7 +35,6 @@ from mcp_agent.workflows.llm.augmented_llm import RequestParams
28
35
 
29
36
  import readline # noqa: F401
30
37
 
31
-
32
38
  # Type aliases for better readability
33
39
  WorkflowType: TypeAlias = Union[
34
40
  Orchestrator, ParallelLLM, EvaluatorOptimizerLLM, LLMRouter
@@ -304,6 +310,85 @@ class FastAgent(ContextDependent):
304
310
  with open(self.config_path) as f:
305
311
  self.config = yaml.safe_load(f)
306
312
 
313
+ def _validate_server_references(self) -> None:
314
+ """
315
+ Validate that all server references in agent configurations exist in config.
316
+ Raises ServerConfigError if any referenced servers are not defined.
317
+ """
318
+ if not self.context.config.mcp or not self.context.config.mcp.servers:
319
+ available_servers = set()
320
+ else:
321
+ available_servers = set(self.context.config.mcp.servers.keys())
322
+
323
+ # Check each agent's server references
324
+ for name, agent_data in self.agents.items():
325
+ config = agent_data["config"]
326
+ if config.servers:
327
+ missing = [s for s in config.servers if s not in available_servers]
328
+ if missing:
329
+ raise ServerConfigError(
330
+ f"Missing server configuration for agent '{name}'",
331
+ f"The following servers are referenced but not defined in config: {', '.join(missing)}",
332
+ )
333
+
334
+ def _validate_workflow_references(self) -> None:
335
+ """
336
+ Validate that all workflow references point to valid agents/workflows.
337
+ Raises ValueError if any referenced components are not defined.
338
+ """
339
+ available_components = set(self.agents.keys())
340
+
341
+ for name, agent_data in self.agents.items():
342
+ agent_type = agent_data["type"]
343
+
344
+ if agent_type == AgentType.PARALLEL.value:
345
+ # Check fan_in exists
346
+ fan_in = agent_data["fan_in"]
347
+ if fan_in not in available_components:
348
+ raise AgentConfigError(
349
+ f"Parallel workflow '{name}' references non-existent fan_in component: {fan_in}"
350
+ )
351
+
352
+ # Check fan_out agents exist
353
+ fan_out = agent_data["fan_out"]
354
+ missing = [a for a in fan_out if a not in available_components]
355
+ if missing:
356
+ raise AgentConfigError(
357
+ f"Parallel workflow '{name}' references non-existent fan_out components: {', '.join(missing)}"
358
+ )
359
+
360
+ elif agent_type == AgentType.ORCHESTRATOR.value:
361
+ # Check all child agents exist
362
+ child_agents = agent_data["child_agents"]
363
+ missing = [a for a in child_agents if a not in available_components]
364
+ if missing:
365
+ raise AgentConfigError(
366
+ f"Orchestrator '{name}' references non-existent agents: {', '.join(missing)}"
367
+ )
368
+
369
+ elif agent_type == AgentType.ROUTER.value:
370
+ # Check all referenced agents exist
371
+ router_agents = agent_data["agents"]
372
+ missing = [a for a in router_agents if a not in available_components]
373
+ if missing:
374
+ raise AgentConfigError(
375
+ f"Router '{name}' references non-existent agents: {', '.join(missing)}"
376
+ )
377
+
378
+ elif agent_type == AgentType.EVALUATOR_OPTIMIZER.value:
379
+ # Check both evaluator and optimizer exist
380
+ evaluator = agent_data["evaluator"]
381
+ optimizer = agent_data["optimizer"]
382
+ missing = []
383
+ if evaluator not in available_components:
384
+ missing.append(f"evaluator: {evaluator}")
385
+ if optimizer not in available_components:
386
+ missing.append(f"optimizer: {optimizer}")
387
+ if missing:
388
+ raise AgentConfigError(
389
+ f"Evaluator-Optimizer '{name}' references non-existent components: {', '.join(missing)}"
390
+ )
391
+
307
392
  def _get_model_factory(
308
393
  self,
309
394
  model: Optional[str] = None,
@@ -350,6 +435,7 @@ class FastAgent(ContextDependent):
350
435
  model: Optional[str] = None,
351
436
  use_history: bool = True,
352
437
  request_params: Optional[Dict] = None,
438
+ human_input: bool = False,
353
439
  ) -> Callable:
354
440
  """
355
441
  Decorator to create and register an agent with configuration.
@@ -362,9 +448,9 @@ class FastAgent(ContextDependent):
362
448
  use_history: Whether to maintain conversation history
363
449
  request_params: Additional request parameters for the LLM
364
450
  """
365
- # print(f"\nDecorating agent {name} with model={model}")
366
451
 
367
452
  def decorator(func: Callable) -> Callable:
453
+ # Create base request params
368
454
  base_params = RequestParams(
369
455
  use_history=use_history,
370
456
  model=model, # Include model in initial params
@@ -380,6 +466,7 @@ class FastAgent(ContextDependent):
380
466
  model=model, # Highest precedence
381
467
  use_history=use_history,
382
468
  default_request_params=base_params,
469
+ human_input=human_input,
383
470
  )
384
471
 
385
472
  # Store the agent configuration
@@ -389,10 +476,7 @@ class FastAgent(ContextDependent):
389
476
  "func": func,
390
477
  }
391
478
 
392
- async def wrapper(*args, **kwargs):
393
- return await func(*args, **kwargs)
394
-
395
- return wrapper
479
+ return func # Don't wrap the function, just return it
396
480
 
397
481
  return decorator
398
482
 
@@ -404,6 +488,7 @@ class FastAgent(ContextDependent):
404
488
  model: str | None = None,
405
489
  use_history: bool = True,
406
490
  request_params: Optional[Dict] = None,
491
+ human_input: bool = False,
407
492
  ) -> Callable:
408
493
  """
409
494
  Decorator to create and register an orchestrator.
@@ -431,6 +516,7 @@ class FastAgent(ContextDependent):
431
516
  model=model, # Highest precedence
432
517
  use_history=use_history,
433
518
  default_request_params=base_params,
519
+ human_input=human_input,
434
520
  )
435
521
 
436
522
  # Store the orchestrator configuration
@@ -441,10 +527,7 @@ class FastAgent(ContextDependent):
441
527
  "func": func,
442
528
  }
443
529
 
444
- async def wrapper(*args, **kwargs):
445
- return await func(*args, **kwargs)
446
-
447
- return wrapper
530
+ return func
448
531
 
449
532
  return decorator
450
533
 
@@ -495,10 +578,7 @@ class FastAgent(ContextDependent):
495
578
  "func": func,
496
579
  }
497
580
 
498
- async def wrapper(*args, **kwargs):
499
- return await func(*args, **kwargs)
500
-
501
- return wrapper
581
+ return func
502
582
 
503
583
  return decorator
504
584
 
@@ -557,10 +637,11 @@ class FastAgent(ContextDependent):
557
637
  self,
558
638
  name: str,
559
639
  agents: List[str],
560
- servers: List[str] = [],
640
+ # servers: List[str] = [],
561
641
  model: Optional[str] = None,
562
642
  use_history: bool = True,
563
643
  request_params: Optional[Dict] = None,
644
+ human_input: bool = False,
564
645
  ) -> Callable:
565
646
  """
566
647
  Decorator to create and register a router.
@@ -584,10 +665,11 @@ class FastAgent(ContextDependent):
584
665
  config = AgentConfig(
585
666
  name=name,
586
667
  instruction="", # Router uses its own routing instruction
587
- servers=servers,
668
+ servers=[], # , servers are not supported now
588
669
  model=model,
589
670
  use_history=use_history,
590
671
  default_request_params=base_params,
672
+ human_input=human_input,
591
673
  )
592
674
 
593
675
  # Store the router configuration
@@ -937,77 +1019,86 @@ class FastAgent(ContextDependent):
937
1019
  async def run(self):
938
1020
  """
939
1021
  Context manager for running the application.
940
- Handles setup and teardown of the app and agents.
941
-
942
- Yields:
943
- AgentAppWrapper instance with all initialized agents
944
- """
945
- async with self.app.run() as agent_app:
946
- # Create all types of agents
947
- active_agents = await self._create_basic_agents(agent_app)
948
- orchestrators = self._create_orchestrators(agent_app, active_agents)
949
- parallel_agents = self._create_parallel_agents(agent_app, active_agents)
950
- evaluator_optimizers = await self._create_evaluator_optimizers(
951
- agent_app, active_agents
952
- )
953
- routers = self._create_routers(agent_app, active_agents)
1022
+ Performs validation and provides user-friendly error messages.
1023
+ """
1024
+ active_agents = {}
1025
+ had_error = False
1026
+ try:
1027
+ async with self.app.run() as agent_app:
1028
+ # Pre-flight validation
1029
+ self._validate_server_references()
1030
+ self._validate_workflow_references()
1031
+
1032
+ # Create all types of agents
1033
+ active_agents = await self._create_basic_agents(agent_app)
1034
+ orchestrators = self._create_orchestrators(agent_app, active_agents)
1035
+ parallel_agents = self._create_parallel_agents(agent_app, active_agents)
1036
+ evaluator_optimizers = await self._create_evaluator_optimizers(
1037
+ agent_app, active_agents
1038
+ )
1039
+ routers = self._create_routers(agent_app, active_agents)
954
1040
 
955
- # Merge all agents into active_agents
956
- active_agents.update(orchestrators)
957
- active_agents.update(parallel_agents)
958
- active_agents.update(evaluator_optimizers)
959
- active_agents.update(routers)
1041
+ # Merge all agents into active_agents
1042
+ active_agents.update(orchestrators)
1043
+ active_agents.update(parallel_agents)
1044
+ active_agents.update(evaluator_optimizers)
1045
+ active_agents.update(routers)
960
1046
 
961
- # Create wrapper with all agents
962
- wrapper = AgentApp(agent_app, active_agents)
963
- try:
1047
+ # Create wrapper with all agents
1048
+ wrapper = AgentApp(agent_app, active_agents)
964
1049
  yield wrapper
965
- finally:
966
- # Clean up basic agents - need to get the actual agent from the proxy
1050
+
1051
+ except ServerConfigError as e:
1052
+ had_error = True
1053
+ print("\n[bold red]Server Configuration Error:")
1054
+ print(e.message)
1055
+ if e.details:
1056
+ print("\nDetails:")
1057
+ print(e.details)
1058
+ print(
1059
+ "\nPlease check your 'fastagent.config.yaml' configuration file and add the missing server definitions."
1060
+ )
1061
+ raise SystemExit(1)
1062
+
1063
+ except ProviderKeyError as e:
1064
+ had_error = True
1065
+ print("\n[bold red]Provider Configuration Error:")
1066
+ print(e.message)
1067
+ if e.details:
1068
+ print("\nDetails:")
1069
+ print(e.details)
1070
+ print(
1071
+ "\nPlease check your 'fastagent.secrets.yaml' configuration file and ensure all required API keys are set."
1072
+ )
1073
+ raise SystemExit(1)
1074
+
1075
+ except AgentConfigError as e:
1076
+ had_error = True
1077
+ print("\n[bold red]Workflow or Agent Configuration Error:")
1078
+ print(e.message)
1079
+ if e.details:
1080
+ print("\nDetails:")
1081
+ print(e.details)
1082
+ print(
1083
+ "\nPlease check your agent definition and ensure names and references are correct."
1084
+ )
1085
+ raise SystemExit(1)
1086
+
1087
+ except ServerInitializationError as e:
1088
+ had_error = True
1089
+ print("\n[bold red]Server Startup Error:")
1090
+ print(e.message)
1091
+ if e.details:
1092
+ print("\nDetails:")
1093
+ print(e.details)
1094
+ print("\nThere was an error starting up the MCP Server.")
1095
+ raise SystemExit(1)
1096
+ finally:
1097
+ # Clean up any active agents without re-raising errors
1098
+ if active_agents and not had_error:
967
1099
  for name, proxy in active_agents.items():
968
1100
  if isinstance(proxy, LLMAgentProxy):
969
- await proxy._agent.__aexit__(None, None, None)
970
-
971
- # async def send(self, agent_name: str, message: str) -> Any:
972
- # """
973
- # Send a message to a specific agent and get the response.
974
-
975
- # Args:
976
- # agent_name: Name of the target agent
977
- # message: Message to send
978
-
979
- # Returns:
980
- # Agent's response
981
-
982
- # Raises:
983
- # ValueError: If agent not found
984
- # RuntimeError: If agent has no LLM attached
985
- # """
986
- # if agent_name not in self.agents:
987
- # raise ValueError(f"Agent {agent_name} not found")
988
-
989
- # agent = self.agents[agent_name]
990
-
991
- # # Special handling for routers
992
- # if isinstance(agent._llm, LLMRouter):
993
- # # Route the message and get results
994
- # results = await agent._llm.route(message)
995
- # if not results:
996
- # return "No appropriate route found for the request."
997
-
998
- # # Get the top result
999
- # top_result = results[0]
1000
- # if isinstance(top_result.result, Agent):
1001
- # # Agent route - delegate to the agent
1002
- # return await top_result.result._llm.generate_str(message)
1003
- # elif isinstance(top_result.result, str):
1004
- # # Server route - use the router directly
1005
- # return "Tool call requested by router - not yet supported"
1006
- # else:
1007
- # return f"Routed to: {top_result.result} ({top_result.confidence}): {top_result.reasoning}"
1008
-
1009
- # # Normal agent handling
1010
- # if not hasattr(agent, "_llm") or agent._llm is None:
1011
- # raise RuntimeError(f"Agent {agent_name} has no LLM attached")
1012
-
1013
- # return await agent._llm.generate_str(message)
1101
+ try:
1102
+ await proxy._agent.__aexit__(None, None, None)
1103
+ except Exception:
1104
+ pass # Ignore cleanup errors
@@ -0,0 +1,44 @@
1
+ """FastAgent validation methods."""
2
+
3
+ from mcp_agent.core.exceptions import ServerConfigError
4
+
5
+
6
+ def _validate_server_references(self) -> None:
7
+ """
8
+ Validate that all server references in agent configurations exist in config.
9
+ Raises ServerConfigError if any referenced servers are not defined.
10
+ """
11
+ # First check if any agents need servers
12
+ agents_needing_servers = {
13
+ name: agent_data["config"].servers
14
+ for name, agent_data in self.agents.items()
15
+ if agent_data["config"].servers
16
+ }
17
+
18
+ if not agents_needing_servers:
19
+ return # No validation needed
20
+
21
+ # If we need servers, verify MCP config exists
22
+ if not hasattr(self.context.config, "mcp"):
23
+ raise ServerConfigError(
24
+ "MCP configuration missing",
25
+ "Agents require server access but no MCP configuration found.\n"
26
+ "Add an 'mcp' section to your configuration file.",
27
+ )
28
+
29
+ if not self.context.config.mcp.servers:
30
+ raise ServerConfigError(
31
+ "No MCP servers configured",
32
+ "Agents require server access but no servers are defined.\n"
33
+ "Add server definitions under mcp.servers in your configuration file.",
34
+ )
35
+
36
+ # Now check each agent's servers exist
37
+ available_servers = set(self.context.config.mcp.servers.keys())
38
+ for name, servers in agents_needing_servers.items():
39
+ missing = [s for s in servers if s not in available_servers]
40
+ if missing:
41
+ raise ServerConfigError(
42
+ f"Missing server configuration for agent '{name}'",
43
+ f"The following servers are referenced but not defined in config: {', '.join(missing)}",
44
+ )
@@ -19,6 +19,7 @@ class ProgressAction(str, Enum):
19
19
  SHUTDOWN = "Shutdown"
20
20
  AGGREGATOR_INITIALIZED = "Running"
21
21
  ROUTING = "Routing"
22
+ FATAL_ERROR = "Error"
22
23
 
23
24
 
24
25
  @dataclass
@@ -61,7 +62,9 @@ def convert_log_event(event: Event) -> Optional[ProgressEvent]:
61
62
  agent_name = event_data.get("agent_name")
62
63
  target = agent_name
63
64
  details = ""
64
- if "mcp_aggregator" in namespace:
65
+ if progress_action == ProgressAction.FATAL_ERROR:
66
+ details = event_data.get("error_message", "An error occurred")
67
+ elif "mcp_aggregator" in namespace:
65
68
  server_name = event_data.get("server_name", "")
66
69
  tool_name = event_data.get("tool_name")
67
70
  if tool_name:
@@ -76,6 +76,7 @@ class RichProgressDisplay:
76
76
  ProgressAction.FINISHED: "black on green",
77
77
  ProgressAction.SHUTDOWN: "black on red",
78
78
  ProgressAction.AGGREGATOR_INITIALIZED: "bold green",
79
+ ProgressAction.FATAL_ERROR: "black on red",
79
80
  }.get(action, "white")
80
81
 
81
82
  def update(self, event: ProgressEvent) -> None:
@@ -116,5 +117,15 @@ class RichProgressDisplay:
116
117
  for task in self._progress.tasks:
117
118
  if task.id != task_id:
118
119
  task.visible = False
120
+ elif event.action == ProgressAction.FATAL_ERROR:
121
+ self._progress.update(
122
+ task_id,
123
+ completed=100,
124
+ total=100,
125
+ details=f" / {event.details}",
126
+ )
127
+ for task in self._progress.tasks:
128
+ if task.id != task_id:
129
+ task.visible = False
119
130
  else:
120
131
  self._progress.reset(task_id)
@@ -24,6 +24,8 @@ from mcp.client.sse import sse_client
24
24
  from mcp.types import JSONRPCMessage
25
25
 
26
26
  from mcp_agent.config import MCPServerSettings
27
+ from mcp_agent.core.exceptions import ServerInitializationError
28
+ from mcp_agent.event_progress import ProgressAction
27
29
  from mcp_agent.logging.logger import get_logger
28
30
  from mcp_agent.mcp.stdio import stdio_client_with_rich_stderr
29
31
  from mcp_agent.context_dependent import ContextDependent
@@ -144,7 +146,9 @@ async def _server_lifecycle_task(server_conn: ServerConnection) -> None:
144
146
  transport_context = server_conn._transport_context_factory()
145
147
 
146
148
  async with transport_context as (read_stream, write_stream):
149
+ # try:
147
150
  server_conn.create_session(read_stream, write_stream)
151
+ # except FileNotFoundError as e:
148
152
 
149
153
  async with server_conn.session:
150
154
  await server_conn.initialize_session()
@@ -153,7 +157,12 @@ async def _server_lifecycle_task(server_conn: ServerConnection) -> None:
153
157
 
154
158
  except Exception as exc:
155
159
  logger.error(
156
- f"{server_name}: Lifecycle task encountered an error: {exc}", exc_info=True
160
+ f"{server_name}: Lifecycle task encountered an error: {exc}",
161
+ exc_info=True,
162
+ data={
163
+ "progress_action": ProgressAction.FATAL_ERROR,
164
+ "server_name": server_name,
165
+ },
157
166
  )
158
167
  # If there's an error, we should also set the event so that
159
168
  # 'get_server' won't hang
@@ -295,7 +304,7 @@ class MCPConnectionManager(ContextDependent):
295
304
 
296
305
  # If the session is still None, it means the lifecycle task crashed
297
306
  if not server_conn or not server_conn.session:
298
- raise RuntimeError(
307
+ raise ServerInitializationError(
299
308
  f"{server_name}: Failed to initialize server; check logs for errors."
300
309
  )
301
310
  return server_conn
@@ -0,0 +1,35 @@
1
+ import asyncio
2
+
3
+ from mcp_agent.core.fastagent import FastAgent
4
+
5
+ # Create the application
6
+ fast = FastAgent("Data Analysis (Roots)")
7
+
8
+
9
+ @fast.agent(
10
+ name="Data_Analysis",
11
+ instruction="""
12
+ You have access to a Python 3.12 interpreter and you can use this to analyse and process data.
13
+ Common analysis packages such as Pandas, Seaborn and Matplotlib are already installed.
14
+ You can add further packages if needed.
15
+ Data files are accessible from the /mnt/data/ directory (this is the current working directory).
16
+ Visualisations should be saved as .png files in the current working directory.
17
+ """,
18
+ servers=["interpreter"],
19
+ )
20
+ async def main():
21
+ # Use the app's context manager
22
+ async with fast.run() as agent:
23
+ await agent(
24
+ "There is a csv file in the current directory. "
25
+ "Analyse the file, produce a detailed description of the data, and any patterns it contains.",
26
+ )
27
+ await agent(
28
+ "Consider the data, and how to usefully group it for presentation to a Human. Find insights, using the Python Interpreter as needed.\n"
29
+ "Use MatPlotLib to produce insightful visualisations. Save them as '.png' files in the current directory. Be sure to run the code and save the files.\n"
30
+ "Produce a summary with major insights to the data",
31
+ )
32
+
33
+
34
+ if __name__ == "__main__":
35
+ asyncio.run(main())
@@ -0,0 +1,20 @@
1
+ default_model: sonnet
2
+
3
+ mcp:
4
+ servers:
5
+ interpreter:
6
+ command: "docker"
7
+ args:
8
+ [
9
+ "run",
10
+ "-i",
11
+ "--rm",
12
+ "--pull=always",
13
+ "-v",
14
+ "./mount-point:/mnt/data/",
15
+ "ghcr.io/evalstate/mcp-py-repl:latest",
16
+ ]
17
+ roots:
18
+ - uri: "file://./mount-point/"
19
+ name: "test_data"
20
+ server_uri_alias: "file:///mnt/data/"