fast-agent-mcp 0.2.22__py3-none-any.whl → 0.2.23__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.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.2.22
3
+ Version: 0.2.23
4
4
  Summary: Define, Prompt and Test MCP enabled Agents and Workflows
5
- Author-email: Shaun Smith <fastagent@llmindset.co.uk>, Sarmad Qadri <sarmad@lastmileai.dev>
5
+ Author-email: Shaun Smith <fastagent@llmindset.co.uk>
6
6
  License: Apache License
7
7
  Version 2.0, January 2004
8
8
  http://www.apache.org/licenses/
@@ -1,11 +1,11 @@
1
1
  mcp_agent/__init__.py,sha256=18T0AG0W9sJhTY38O9GFFOzliDhxx9p87CvRyti9zbw,1620
2
2
  mcp_agent/app.py,sha256=WRsiUdwy_9IAnaGRDwuLm7pzgQpt2wgsg10vBOpfcwM,5539
3
- mcp_agent/config.py,sha256=ZC4SiIVbxVn7-hUfv3RFj6fNrXxvci6gmUNCGM7vzs8,12624
3
+ mcp_agent/config.py,sha256=L_wUWTdqFXaRTBA5tL_j2l_9dufWE_MHHPut5e89lBk,12773
4
4
  mcp_agent/console.py,sha256=Gjf2QLFumwG1Lav__c07X_kZxxEUSkzV-1_-YbAwcwo,813
5
5
  mcp_agent/context.py,sha256=Kb3s_0MolHx7AeTs1NVcY3ly-xFBd35o8LT7Srpx9is,7334
6
6
  mcp_agent/context_dependent.py,sha256=QXfhw3RaQCKfscEEBRGuZ3sdMWqkgShz2jJ1ivGGX1I,1455
7
7
  mcp_agent/event_progress.py,sha256=b1VKlQQF2AgPMb6XHjlJAVoPdx8GuxRTUk2g-4lBNm0,2749
8
- mcp_agent/mcp_server_registry.py,sha256=jUmCdfcpTitXm1-3TxpWsdRWY_8phdKNYgXwB16ZSVU,10100
8
+ mcp_agent/mcp_server_registry.py,sha256=QTzu0elBWzqXks6u5nI5n8uN5CX8CpyV6ybxnyt5LZM,11531
9
9
  mcp_agent/progress_display.py,sha256=GeJU9VUt6qKsFVymG688hCMVCsAygG9ifiiEb5IcbN4,361
10
10
  mcp_agent/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  mcp_agent/agents/agent.py,sha256=GgaUHoilgqzh9PQYr5k2WiPj4pagwicf9-ZLFsHkNNo,3848
@@ -13,7 +13,7 @@ mcp_agent/agents/base_agent.py,sha256=fjDr01-hZ9sB3ghI4DlXYVePP0s5f9pmtLH-N3X8bR
13
13
  mcp_agent/agents/workflow/__init__.py,sha256=HloteEW6kalvgR0XewpiFAqaQlMPlPJYg5p3K33IUzI,25
14
14
  mcp_agent/agents/workflow/chain_agent.py,sha256=eIlImirrSXkqBJmPuAJgOKis81Cl6lZEGM0-6IyaUV8,6105
15
15
  mcp_agent/agents/workflow/evaluator_optimizer.py,sha256=ysUMGM2NzeCIutgr_vXH6kUPpZMw0cX4J_Wl1r8eT84,13296
16
- mcp_agent/agents/workflow/orchestrator_agent.py,sha256=byZe4bx7D_7BSZZ3hN8BNUWVFPYeqeUwDUCLTRC8mlI,21583
16
+ mcp_agent/agents/workflow/orchestrator_agent.py,sha256=lArV7wHwPYepSuxe0ybTGJRJv85iebRI4ZOY_m8kMZQ,21593
17
17
  mcp_agent/agents/workflow/orchestrator_models.py,sha256=5P_aXADVT4Et8qT4e1cb9RelmHX5dCRrzu8j8T41Kdg,7230
18
18
  mcp_agent/agents/workflow/orchestrator_prompts.py,sha256=EXKEI174sshkZyPPEnWbwwNafzSPuA39MXL7iqG9cWc,9106
19
19
  mcp_agent/agents/workflow/parallel_agent.py,sha256=JaQFp35nmAdoBRLAwx8BfnK7kirVq9PMw24LQ3ZEzoc,7705
@@ -23,9 +23,10 @@ mcp_agent/cli/__main__.py,sha256=AVZ7tQFhU_sDOGuUGJq8ujgKtcxsYJBJwHbVaaiRDlI,166
23
23
  mcp_agent/cli/main.py,sha256=XjrgXMBaPKkVqAFo8T9LJz6Tp1-ivrKDOuNYWke99YA,3090
24
24
  mcp_agent/cli/terminal.py,sha256=GRwD-RGW7saIz2IOWZn5vD6JjiArscELBThm1GTFkuI,1065
25
25
  mcp_agent/cli/commands/check_config.py,sha256=9Ryxo_fLInm3YKdYv46yLrAJgnQtMisGreu6Kkriw2g,16677
26
- mcp_agent/cli/commands/go.py,sha256=2UY8TSDwhh_-p-WYXrZz3pEv3-2eTdBl5Lxy3JyJV0E,6057
26
+ mcp_agent/cli/commands/go.py,sha256=LIsOJQuTdfCUcNm7JT-NQDU8cI-GCnYwYjN2VOWxvqs,8658
27
27
  mcp_agent/cli/commands/quickstart.py,sha256=SM3CHMzDgvTxIpKjFuX9BrS_N1vRoXNBDaO90aWx1Rk,14586
28
28
  mcp_agent/cli/commands/setup.py,sha256=eOEd4TL-b0DaDeSJMGOfNOsTEItoZ67W88eTP4aP-bo,6482
29
+ mcp_agent/cli/commands/url_parser.py,sha256=7QL9bp9tO7w0cPnwhbpt8GwjbOJ1Rrry1o71uVJhSss,5655
29
30
  mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
31
  mcp_agent/core/agent_app.py,sha256=5nQJNo8DocIRWiX4pVKAHUZF8s6HWpc-hJnfzl_1v1c,9697
31
32
  mcp_agent/core/agent_types.py,sha256=bQVQMTwKH7qHIJsNglj4C_d6PNFBBzC_0RIkcENSII4,1459
@@ -34,11 +35,11 @@ mcp_agent/core/direct_factory.py,sha256=d96OM1yS3eIocIiaA9FQt6C2zr6VDUyCJBTZCp_D
34
35
  mcp_agent/core/enhanced_prompt.py,sha256=bzvcengS7XzHWB7NWhyxHM3hhO2HI4zP5DbGXAOw0Jw,19155
35
36
  mcp_agent/core/error_handling.py,sha256=xoyS2kLe0eG0bj2eSJCJ2odIhGUve2SbDR7jP-A-uRw,624
36
37
  mcp_agent/core/exceptions.py,sha256=ENAD_qGG67foxy6vDkIvc-lgopIUQy6O7zvNPpPXaQg,2289
37
- mcp_agent/core/fastagent.py,sha256=WEEGz2WBAddDGNeWJwqwFIPLiQnLjaNxZLoMR0peyyU,22884
38
+ mcp_agent/core/fastagent.py,sha256=uS_NSXeniUYFu6xce8OHGJ9PbEYNU-gm1XVpa1r0rZc,22893
38
39
  mcp_agent/core/interactive_prompt.py,sha256=w3VyRzW4hzn0xhWZRwo_qRRAD5WVSrJYe8QDe1XZ55Y,24252
39
40
  mcp_agent/core/mcp_content.py,sha256=2D7KHY9mG_vxoDwFLKvsPQV9VRIzHItM7V-jcEnACh8,8878
40
41
  mcp_agent/core/prompt.py,sha256=qnintOUGEoDPYLI9bu9G2OlgVMCe5ZPUZilgMzydXhc,7919
41
- mcp_agent/core/request_params.py,sha256=vRfAz9T6Ir-0oeJ4qEdO62LDOzoLwBuuXcBcdh6WPZ8,1576
42
+ mcp_agent/core/request_params.py,sha256=qmFWZXeYEJyYw2IwonyrTnZWxQG7qX6bKpOPcqETa60,1603
42
43
  mcp_agent/core/validation.py,sha256=RIBKFlh0GJg4rTcFQXoXp8A0sK1HpsCigKcYSK3gFaY,12090
43
44
  mcp_agent/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  mcp_agent/executor/executor.py,sha256=E44p6d-o3OMRoP_dNs_cDnyti91LQ3P9eNU88mSi1kc,9462
@@ -87,7 +88,7 @@ mcp_agent/mcp/interfaces.py,sha256=PAou8znAl2HgtvfCpLQOZFbKra9F72OcVRfBJbboNX8,6
87
88
  mcp_agent/mcp/logger_textio.py,sha256=vljC1BtNTCxBAda9ExqNB-FwVNUZIuJT3h1nWmCjMws,3172
88
89
  mcp_agent/mcp/mcp_agent_client_session.py,sha256=Ng7epBXq8BEA_3m1GX5LqwafgNUAMSzBugwN6N0VUWQ,4364
89
90
  mcp_agent/mcp/mcp_aggregator.py,sha256=lVSt0yp0CnaYjcHCWmluwBeFgl8JXHYEZk0MzXgrQzA,40110
90
- mcp_agent/mcp/mcp_connection_manager.py,sha256=6jtjclh4YNJZsNwYnSWmQ6cPzapAwsRUxir1c_gVNfM,16051
91
+ mcp_agent/mcp/mcp_connection_manager.py,sha256=jlqaAdS4zc1UfVBHQU0TkTbVr0-rOkbN9bkrLPrZVLk,17159
91
92
  mcp_agent/mcp/mime_utils.py,sha256=difepNR_gpb4MpMLkBRAoyhDk-AjXUHTiqKvT_VwS1o,1805
92
93
  mcp_agent/mcp/prompt_message_multipart.py,sha256=BDwRdNwyWHb2q2bccDb2iR2VlORqVvkvoG3xYzcMpCE,4403
93
94
  mcp_agent/mcp/prompt_render.py,sha256=k3v4BZDThGE2gGiOYVQtA6x8WTEdOuXIEnRafANhN1U,2996
@@ -101,10 +102,10 @@ mcp_agent/mcp/prompts/__main__.py,sha256=gr1Tdz9fcK0EXjEuZg_BOnKUmvhYq5AH2lFZicV
101
102
  mcp_agent/mcp/prompts/prompt_constants.py,sha256=Q9W0t3rOXl2LHIG9wcghApUV2QZ1iICuo7SwVwHUf3c,566
102
103
  mcp_agent/mcp/prompts/prompt_helpers.py,sha256=Joqo2t09pTKDP-Wge3G-ozPEHikzjaqwV6GVk8hNR50,7534
103
104
  mcp_agent/mcp/prompts/prompt_load.py,sha256=Zo0FogqWFEG5FtF1d9ZH-RWsCSSMsi5FIEQHpJD8N7M,5404
104
- mcp_agent/mcp/prompts/prompt_server.py,sha256=DbuDcYCMbsbqwBeebpNGInAQ4-DP1Jjp49y8uZ-0XlY,18872
105
+ mcp_agent/mcp/prompts/prompt_server.py,sha256=VAKS4rHTE5Mp7e0NV6qADslR_5vSLab8RUhNxCkAdJE,19234
105
106
  mcp_agent/mcp/prompts/prompt_template.py,sha256=EejiqGkau8OizORNyKTUwUjrPof5V-hH1H_MBQoQfXw,15732
106
107
  mcp_agent/mcp_server/__init__.py,sha256=zBU51ITHIEPScd9nRafnhEddsWqXRPAAvHhkrbRI2_4,155
107
- mcp_agent/mcp_server/agent_server.py,sha256=s-nI0uTNWx4nYDDM_5GmuY5x6ZeFkymfNoCSuwuBRd8,19891
108
+ mcp_agent/mcp_server/agent_server.py,sha256=df3UbPLg52e_SS98F3lc4T8BqqzvQRBl6kplODsaq-M,20096
108
109
  mcp_agent/resources/examples/data-analysis/analysis-campaign.py,sha256=16gxrQ5kM8fb8tPwSCMXaitonk3PSEhz28njWwPxXrw,7269
109
110
  mcp_agent/resources/examples/data-analysis/analysis.py,sha256=M9z8Q4YC5OGuqSa5uefYmmfmctqMn-WqCSfg5LI407o,2609
110
111
  mcp_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=ini94PHyJCfgpjcjHKMMbGuHs6LIj46F1NwY0ll5HVk,1609
@@ -123,7 +124,7 @@ mcp_agent/resources/examples/internal/sizer.py,sha256=xP1TBJkp4xIdtJnyk2MP4BufTh
123
124
  mcp_agent/resources/examples/internal/social.py,sha256=pTKcpHAcvA-vQYgjVfDuU1FivCR004Nq4N2GXd5OMs0,1716
124
125
  mcp_agent/resources/examples/mcp/state-transfer/agent_one.py,sha256=HR-Igr8k68HU0tqIpaXujtJxnKSUwwtZqTdZk8QHNgo,455
125
126
  mcp_agent/resources/examples/mcp/state-transfer/agent_two.py,sha256=TY9SPzJZFv3TL6VEP3IpdJvTjYup5txF_DjpvEzlmbw,476
126
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml,sha256=-IIocaehANnWiwFHSNNzDRdV80ApicNmxAY4flKETpk,797
127
+ mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml,sha256=e3Esqw850p9GcapVVhPhAAHWwtt2gH2ivsVIULD9n6Q,798
127
128
  mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example,sha256=0n3F2S_Z2CeLHZueZqCGy37mxiMDHLplvxUHYiCpD2A,421
128
129
  mcp_agent/resources/examples/prompting/__init__.py,sha256=2GSrs9MSDIKo-uDrUI0O311F0UH0RW02ZNdvItJzjfI,50
129
130
  mcp_agent/resources/examples/prompting/agent.py,sha256=HxzUsidfxoc7Th0Ws55ppQCHNLkdZvcbiAcc2fMd4KI,490
@@ -144,9 +145,9 @@ mcp_agent/resources/examples/workflows/orchestrator.py,sha256=rOGilFTliWWnZ3Jx5w
144
145
  mcp_agent/resources/examples/workflows/parallel.py,sha256=DQ5vY5-h8Qa5QHcYjsWXhZ_FYrYoloVWOdgeXV9p2gI,1890
145
146
  mcp_agent/resources/examples/workflows/router.py,sha256=E4x_-c3l4YW9w1i4ARcDtkdeqIdbWEGfsMzwLYpdbVc,1677
146
147
  mcp_agent/resources/examples/workflows/short_story.txt,sha256=X3y_1AyhLFN2AKzCKvucJtDgAFIJfnlbsbGZO5bBWu0,1187
147
- mcp_agent/ui/console_display.py,sha256=TVGDtJ37hc6UG0ei9g7ZPZZfFNeS1MYozt-Mx8HsPCk,9752
148
- fast_agent_mcp-0.2.22.dist-info/METADATA,sha256=HbiOG6NhC3IEfEyBsUDQnihh2LbU4CEZEe19X4Y7VTQ,30194
149
- fast_agent_mcp-0.2.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
150
- fast_agent_mcp-0.2.22.dist-info/entry_points.txt,sha256=bRniFM5zk3Kix5z7scX0gf9VnmGQ2Cz_Q1Gh7Ir4W00,186
151
- fast_agent_mcp-0.2.22.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
152
- fast_agent_mcp-0.2.22.dist-info/RECORD,,
148
+ mcp_agent/ui/console_display.py,sha256=EUeMJ7yqtxJ0-hAjXNZFJFTlmfyKlENdfzTlw0e5ETg,9949
149
+ fast_agent_mcp-0.2.23.dist-info/METADATA,sha256=Vtk96ocWT3Xk_Y8f3ZLUILhh2aybr8OebCPn5jZeZOY,30156
150
+ fast_agent_mcp-0.2.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
+ fast_agent_mcp-0.2.23.dist-info/entry_points.txt,sha256=bRniFM5zk3Kix5z7scX0gf9VnmGQ2Cz_Q1Gh7Ir4W00,186
152
+ fast_agent_mcp-0.2.23.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
153
+ fast_agent_mcp-0.2.23.dist-info/RECORD,,
@@ -229,7 +229,7 @@ class OrchestratorAgent(BaseAgent):
229
229
  self.logger.warning(
230
230
  f"Reached maximum step limit ({max_steps}) without completing objective"
231
231
  )
232
- plan_result.max_steps_reached = True
232
+ plan_result.max_iterations_reached = True
233
233
  break
234
234
 
235
235
  # Execute the step and collect results
@@ -239,7 +239,7 @@ class OrchestratorAgent(BaseAgent):
239
239
  total_steps_executed += 1
240
240
 
241
241
  # Check if we need to break due to hitting max steps
242
- if getattr(plan_result, "max_steps_reached", False):
242
+ if getattr(plan_result, "max_iterations_reached", False):
243
243
  break
244
244
 
245
245
  # If the plan is marked complete, finalize the result
@@ -2,16 +2,18 @@
2
2
 
3
3
  import asyncio
4
4
  import sys
5
- from typing import List, Optional
5
+ from typing import Dict, List, Optional
6
6
 
7
7
  import typer
8
8
 
9
+ from mcp_agent.cli.commands.url_parser import generate_server_configs, parse_server_urls
9
10
  from mcp_agent.core.fastagent import FastAgent
10
11
 
11
12
  app = typer.Typer(
12
13
  help="Run an interactive agent directly from the command line without creating an agent.py file"
13
14
  )
14
15
 
16
+
15
17
  async def _run_agent(
16
18
  name: str = "FastAgent CLI",
17
19
  instruction: str = "You are a helpful AI Agent.",
@@ -19,33 +21,61 @@ async def _run_agent(
19
21
  server_list: Optional[List[str]] = None,
20
22
  model: Optional[str] = None,
21
23
  message: Optional[str] = None,
22
- prompt_file: Optional[str] = None
24
+ prompt_file: Optional[str] = None,
25
+ url_servers: Optional[Dict[str, Dict[str, str]]] = None,
23
26
  ) -> None:
24
27
  """Async implementation to run an interactive agent."""
25
28
  from pathlib import Path
26
29
 
30
+ from mcp_agent.config import MCPServerSettings, MCPSettings
27
31
  from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
28
32
 
29
- # Create the FastAgent instance with CLI arg parsing enabled
30
- # It will automatically parse args like --model, --quiet, etc.
33
+ # Create the FastAgent instance
31
34
  fast_kwargs = {
32
35
  "name": name,
33
36
  "config_path": config_path,
34
37
  "ignore_unknown_args": True,
35
38
  "parse_cli_args": False, # Don't parse CLI args, we're handling it ourselves
36
39
  }
37
-
40
+
38
41
  fast = FastAgent(**fast_kwargs)
39
42
 
43
+ # Add URL-based servers to the context configuration
44
+ if url_servers:
45
+ # Initialize the app to ensure context is ready
46
+ await fast.app.initialize()
47
+
48
+ # Initialize mcp settings if needed
49
+ if not hasattr(fast.app.context.config, "mcp"):
50
+ fast.app.context.config.mcp = MCPSettings()
51
+
52
+ # Initialize servers dictionary if needed
53
+ if (
54
+ not hasattr(fast.app.context.config.mcp, "servers")
55
+ or fast.app.context.config.mcp.servers is None
56
+ ):
57
+ fast.app.context.config.mcp.servers = {}
58
+
59
+ # Add each URL server to the config
60
+ for server_name, server_config in url_servers.items():
61
+ server_settings = {"transport": server_config["transport"], "url": server_config["url"]}
62
+
63
+ # Add headers if present in the server config
64
+ if "headers" in server_config:
65
+ server_settings["headers"] = server_config["headers"]
66
+
67
+ fast.app.context.config.mcp.servers[server_name] = MCPServerSettings(**server_settings)
68
+
40
69
  # Define the agent with specified parameters
41
70
  agent_kwargs = {"instruction": instruction}
42
71
  if server_list:
43
72
  agent_kwargs["servers"] = server_list
44
73
  if model:
45
74
  agent_kwargs["model"] = model
46
-
75
+
47
76
  # Handle prompt file and message options
48
77
  if message or prompt_file:
78
+
49
79
  @fast.agent(**agent_kwargs)
50
80
  async def cli_agent():
51
81
  async with fast.run() as agent:
@@ -55,7 +85,7 @@ async def _run_agent(
55
85
  print(response)
56
86
  elif prompt_file:
57
87
  prompt = load_prompt_multipart(Path(prompt_file))
58
- response = await agent.generate(prompt)
88
+ response = await agent.default.generate(prompt)
59
89
  # Print the response text and exit
60
90
  print(response.last_text())
61
91
  else:
@@ -68,18 +98,37 @@ async def _run_agent(
68
98
  # Run the agent
69
99
  await cli_agent()
70
100
 
101
+
71
102
  def run_async_agent(
72
- name: str,
73
- instruction: str,
74
- config_path: Optional[str] = None,
103
+ name: str,
104
+ instruction: str,
105
+ config_path: Optional[str] = None,
75
106
  servers: Optional[str] = None,
107
+ urls: Optional[str] = None,
108
+ auth: Optional[str] = None,
76
109
  model: Optional[str] = None,
77
110
  message: Optional[str] = None,
78
- prompt_file: Optional[str] = None
111
+ prompt_file: Optional[str] = None,
79
112
  ):
80
113
  """Run the async agent function with proper loop handling."""
81
- server_list = servers.split(',') if servers else None
82
-
114
+ server_list = servers.split(",") if servers else None
115
+
116
+ # Parse URLs and generate server configurations if provided
117
+ url_servers = None
118
+ if urls:
119
+ try:
120
+ parsed_urls = parse_server_urls(urls, auth)
121
+ url_servers = generate_server_configs(parsed_urls)
122
+ # If we have servers from URLs, add their names to the server_list
123
+ if url_servers and not server_list:
124
+ server_list = list(url_servers.keys())
125
+ elif url_servers and server_list:
126
+ # Merge both lists
127
+ server_list.extend(list(url_servers.keys()))
128
+ except ValueError as e:
129
+ print(f"Error parsing URLs: {e}")
130
+ return
131
+
83
132
  # Check if we're already in an event loop
84
133
  try:
85
134
  loop = asyncio.get_event_loop()
@@ -92,24 +141,27 @@ def run_async_agent(
92
141
  # No event loop exists, so we'll create one
93
142
  loop = asyncio.new_event_loop()
94
143
  asyncio.set_event_loop(loop)
95
-
144
+
96
145
  try:
97
- loop.run_until_complete(_run_agent(
98
- name=name,
99
- instruction=instruction,
100
- config_path=config_path,
101
- server_list=server_list,
102
- model=model,
103
- message=message,
104
- prompt_file=prompt_file
105
- ))
146
+ loop.run_until_complete(
147
+ _run_agent(
148
+ name=name,
149
+ instruction=instruction,
150
+ config_path=config_path,
151
+ server_list=server_list,
152
+ model=model,
153
+ message=message,
154
+ prompt_file=prompt_file,
155
+ url_servers=url_servers,
156
+ )
157
+ )
106
158
  finally:
107
159
  try:
108
160
  # Clean up the loop
109
161
  tasks = asyncio.all_tasks(loop)
110
162
  for task in tasks:
111
163
  task.cancel()
112
-
164
+
113
165
  # Run the event loop until all tasks are done
114
166
  if sys.version_info >= (3, 7):
115
167
  loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
@@ -118,6 +170,7 @@ def run_async_agent(
118
170
  except Exception:
119
171
  pass
120
172
 
173
+
121
174
  @app.callback(invoke_without_command=True)
122
175
  def go(
123
176
  ctx: typer.Context,
@@ -131,6 +184,12 @@ def go(
131
184
  servers: Optional[str] = typer.Option(
132
185
  None, "--servers", help="Comma-separated list of server names to enable from config"
133
186
  ),
187
+ urls: Optional[str] = typer.Option(
188
+ None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
189
+ ),
190
+ auth: Optional[str] = typer.Option(
191
+ None, "--auth", help="Bearer token for authorization with URL-based servers"
192
+ ),
134
193
  model: Optional[str] = typer.Option(
135
194
  None, "--model", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
136
195
  ),
@@ -148,6 +207,8 @@ def go(
148
207
  fast-agent go --model=haiku --instruction="You are a coding assistant" --servers=fetch,filesystem
149
208
  fast-agent go --message="What is the weather today?" --model=haiku
150
209
  fast-agent go --prompt-file=my-prompt.txt --model=haiku
210
+ fast-agent go --url=http://localhost:8001/mcp,http://api.example.com/sse
211
+ fast-agent go --url=https://api.example.com/mcp --auth=YOUR_API_TOKEN
151
212
 
152
213
  This will start an interactive session with the agent, using the specified model
153
214
  and instruction. It will use the default configuration from fastagent.config.yaml
@@ -157,15 +218,19 @@ def go(
157
218
  --model Override the default model (e.g., --model=haiku)
158
219
  --quiet Disable progress display and logging
159
220
  --servers Comma-separated list of server names to enable from config
221
+ --url Comma-separated list of HTTP/SSE URLs to connect to
222
+ --auth Bearer token for authorization with URL-based servers
160
223
  --message, -m Send a single message and exit
161
224
  --prompt-file, -p Use a prompt file instead of interactive mode
162
225
  """
163
226
  run_async_agent(
164
- name=name,
165
- instruction=instruction,
166
- config_path=config_path,
227
+ name=name,
228
+ instruction=instruction,
229
+ config_path=config_path,
167
230
  servers=servers,
231
+ urls=urls,
232
+ auth=auth,
168
233
  model=model,
169
234
  message=message,
170
- prompt_file=prompt_file
171
- )
235
+ prompt_file=prompt_file,
236
+ )
@@ -0,0 +1,185 @@
1
+ """
2
+ URL parsing utility for the fast-agent CLI.
3
+ Provides functions to parse URLs and determine MCP server configurations.
4
+ """
5
+
6
+ import hashlib
7
+ import re
8
+ from typing import Dict, List, Literal, Tuple
9
+ from urllib.parse import urlparse
10
+
11
+
12
+ def parse_server_url(
13
+ url: str,
14
+ ) -> Tuple[str, Literal["http", "sse"], str]:
15
+ """
16
+ Parse a server URL and determine the transport type and server name.
17
+
18
+ Args:
19
+ url: The URL to parse
20
+
21
+ Returns:
22
+ Tuple containing:
23
+ - server_name: A generated name for the server
24
+ - transport_type: Either "http" or "sse" based on URL
25
+ - url: The parsed and validated URL
26
+
27
+ Raises:
28
+ ValueError: If the URL is invalid or unsupported
29
+ """
30
+ # Basic URL validation
31
+ if not url:
32
+ raise ValueError("URL cannot be empty")
33
+
34
+ # Parse the URL
35
+ parsed_url = urlparse(url)
36
+
37
+ # Ensure scheme is present and is either http or https
38
+ if not parsed_url.scheme or parsed_url.scheme not in ("http", "https"):
39
+ raise ValueError(f"URL must have http or https scheme: {url}")
40
+
41
+ # Ensure netloc (hostname) is present
42
+ if not parsed_url.netloc:
43
+ raise ValueError(f"URL must include a hostname: {url}")
44
+
45
+ # Determine transport type based on URL path
46
+ transport_type: Literal["http", "sse"] = "http"
47
+ if parsed_url.path.endswith("/sse"):
48
+ transport_type = "sse"
49
+ elif not parsed_url.path.endswith("/mcp"):
50
+ # If path doesn't end with /mcp or /sse, append /mcp
51
+ url = url if url.endswith("/") else f"{url}/"
52
+ url = f"{url}mcp"
53
+
54
+ # Generate a server name based on hostname and port
55
+ server_name = generate_server_name(url)
56
+
57
+ return server_name, transport_type, url
58
+
59
+
60
+ def generate_server_name(url: str) -> str:
61
+ """
62
+ Generate a unique and readable server name from a URL.
63
+
64
+ Args:
65
+ url: The URL to generate a name for
66
+
67
+ Returns:
68
+ A server name string
69
+ """
70
+ parsed_url = urlparse(url)
71
+
72
+ # Extract hostname and port
73
+ hostname = parsed_url.netloc.split(":")[0]
74
+
75
+ # Clean the hostname for use in a server name
76
+ # Replace non-alphanumeric characters with underscores
77
+ clean_hostname = re.sub(r"[^a-zA-Z0-9]", "_", hostname)
78
+
79
+ if len(clean_hostname) > 15:
80
+ clean_hostname = clean_hostname[:9] + clean_hostname[-5:]
81
+
82
+ # If it's localhost or an IP, add a more unique identifier
83
+ if clean_hostname in ("localhost", "127_0_0_1") or re.match(r"^(\d+_){3}\d+$", clean_hostname):
84
+ # Use the path as part of the name for uniqueness
85
+ path = parsed_url.path.strip("/")
86
+ path = re.sub(r"[^a-zA-Z0-9]", "_", path)
87
+
88
+ # Include port if specified
89
+ port = ""
90
+ if ":" in parsed_url.netloc:
91
+ port = f"_{parsed_url.netloc.split(':')[1]}"
92
+
93
+ if path:
94
+ return f"{clean_hostname}{port}_{path[:20]}" # Limit path length
95
+ else:
96
+ # Use a hash if no path for uniqueness
97
+ url_hash = hashlib.md5(url.encode()).hexdigest()[:8]
98
+ return f"{clean_hostname}{port}_{url_hash}"
99
+
100
+ return clean_hostname
101
+
102
+
103
+ def parse_server_urls(
104
+ urls_param: str, auth_token: str = None
105
+ ) -> List[Tuple[str, Literal["http", "sse"], str, Dict[str, str] | None]]:
106
+ """
107
+ Parse a comma-separated list of URLs into server configurations.
108
+
109
+ Args:
110
+ urls_param: Comma-separated list of URLs
111
+ auth_token: Optional bearer token for authorization
112
+
113
+ Returns:
114
+ List of tuples containing (server_name, transport_type, url, headers)
115
+
116
+ Raises:
117
+ ValueError: If any URL is invalid
118
+ """
119
+ if not urls_param:
120
+ return []
121
+
122
+ # Split by comma and strip whitespace
123
+ url_list = [url.strip() for url in urls_param.split(",")]
124
+
125
+ # Prepare headers if auth token is provided
126
+ headers = None
127
+ if auth_token:
128
+ headers = {"Authorization": f"Bearer {auth_token}"}
129
+
130
+ # Parse each URL
131
+ result = []
132
+ for url in url_list:
133
+ server_name, transport_type, parsed_url = parse_server_url(url)
134
+ result.append((server_name, transport_type, parsed_url, headers))
135
+
136
+ return result
137
+
138
+
139
+ def generate_server_configs(
140
+ parsed_urls: List[Tuple[str, Literal["http", "sse"], str, Dict[str, str] | None]],
141
+ ) -> Dict[str, Dict[str, str | Dict[str, str]]]:
142
+ """
143
+ Generate server configurations from parsed URLs.
144
+
145
+ Args:
146
+ parsed_urls: List of tuples containing (server_name, transport_type, url, headers)
147
+
148
+ Returns:
149
+ Dictionary of server configurations
150
+ """
151
+ server_configs = {}
152
+ # Keep track of server name occurrences to handle collisions
153
+ name_counts = {}
154
+
155
+ for server_name, transport_type, url, headers in parsed_urls:
156
+ # Handle name collisions by adding a suffix
157
+ final_name = server_name
158
+ if server_name in server_configs:
159
+ # Initialize counter if we haven't seen this name yet
160
+ if server_name not in name_counts:
161
+ name_counts[server_name] = 1
162
+
163
+ # Generate a new name with suffix
164
+ suffix = name_counts[server_name]
165
+ final_name = f"{server_name}_{suffix}"
166
+ name_counts[server_name] += 1
167
+
168
+ # Ensure the new name is also unique
169
+ while final_name in server_configs:
170
+ suffix = name_counts[server_name]
171
+ final_name = f"{server_name}_{suffix}"
172
+ name_counts[server_name] += 1
173
+
174
+ config = {
175
+ "transport": transport_type,
176
+ "url": url,
177
+ }
178
+
179
+ # Add headers if provided
180
+ if headers:
181
+ config["headers"] = headers
182
+
183
+ server_configs[final_name] = config
184
+
185
+ return server_configs
mcp_agent/config.py CHANGED
@@ -60,7 +60,7 @@ class MCPServerSettings(BaseModel):
60
60
  description: str | None = None
61
61
  """The description of the server."""
62
62
 
63
- transport: Literal["stdio", "sse"] = "stdio"
63
+ transport: Literal["stdio", "sse", "http"] = "stdio"
64
64
  """The transport mechanism."""
65
65
 
66
66
  command: str | None = None
@@ -249,6 +249,8 @@ class LoggerSettings(BaseModel):
249
249
  """Show MCP Sever tool calls on the console"""
250
250
  truncate_tools: bool = True
251
251
  """Truncate display of long tool calls"""
252
+ enable_markup: bool = True
253
+ """Enable markup in console output. Disable for outputs that may conflict with rich console formatting"""
252
254
 
253
255
 
254
256
  class Settings(BaseSettings):
@@ -131,8 +131,8 @@ class FastAgent:
131
131
  )
132
132
  parser.add_argument(
133
133
  "--transport",
134
- choices=["sse", "stdio"],
135
- default="sse",
134
+ choices=["sse", "http", "stdio"],
135
+ default="http",
136
136
  help="Transport protocol to use when running as a server (sse or stdio)",
137
137
  )
138
138
  parser.add_argument(
@@ -25,24 +25,23 @@ class RequestParams(CreateMessageRequestParams):
25
25
 
26
26
  model: str | None = None
27
27
  """
28
- The model to use for the LLM generation.
28
+ The model to use for the LLM generation. This can only be set during Agent creation.
29
29
  If specified, this overrides the 'modelPreferences' selection criteria.
30
30
  """
31
31
 
32
32
  use_history: bool = True
33
33
  """
34
- Include the message history in the generate request.
34
+ Agent/LLM maintains conversation history. Does not include applied Prompts
35
35
  """
36
36
 
37
- max_iterations: int = 10
37
+ max_iterations: int = 20
38
38
  """
39
- The maximum number of iterations to run the LLM for.
39
+ The maximum number of tool calls allowed in a conversation turn
40
40
  """
41
41
 
42
42
  parallel_tool_calls: bool = True
43
43
  """
44
- Whether to allow multiple tool calls per iteration.
45
- Also known as multi-step tool use.
44
+ Whether to allow simultaneous tool calls
46
45
  """
47
46
  response_format: Any | None = None
48
47
  """
@@ -23,6 +23,7 @@ from mcp.client.stdio import (
23
23
  get_default_environment,
24
24
  stdio_client,
25
25
  )
26
+ from mcp.client.streamable_http import GetSessionIdCallback, streamablehttp_client
26
27
  from mcp.types import JSONRPCMessage, ServerCapabilities
27
28
 
28
29
  from mcp_agent.config import MCPServerSettings
@@ -40,6 +41,27 @@ if TYPE_CHECKING:
40
41
  logger = get_logger(__name__)
41
42
 
42
43
 
44
+ class StreamingContextAdapter:
45
+ """Adapter to provide a 3-value context from a 2-value context manager"""
46
+
47
+ def __init__(self, context_manager):
48
+ self.context_manager = context_manager
49
+ self.cm_instance = None
50
+
51
+ async def __aenter__(self):
52
+ self.cm_instance = await self.context_manager.__aenter__()
53
+ read_stream, write_stream = self.cm_instance
54
+ return read_stream, write_stream, None
55
+
56
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
57
+ return await self.context_manager.__aexit__(exc_type, exc_val, exc_tb)
58
+
59
+
60
+ def _add_none_to_context(context_manager):
61
+ """Helper to add a None value to context managers that return 2 values instead of 3"""
62
+ return StreamingContextAdapter(context_manager)
63
+
64
+
43
65
  class ServerConnection:
44
66
  """
45
67
  Represents a long-lived MCP server connection, including:
@@ -57,6 +79,7 @@ class ServerConnection:
57
79
  tuple[
58
80
  MemoryObjectReceiveStream[JSONRPCMessage | Exception],
59
81
  MemoryObjectSendStream[JSONRPCMessage],
82
+ GetSessionIdCallback | None,
60
83
  ],
61
84
  None,
62
85
  ],
@@ -162,7 +185,7 @@ async def _server_lifecycle_task(server_conn: ServerConnection) -> None:
162
185
  try:
163
186
  transport_context = server_conn._transport_context_factory()
164
187
 
165
- async with transport_context as (read_stream, write_stream):
188
+ async with transport_context as (read_stream, write_stream, _):
166
189
  server_conn.create_session(read_stream, write_stream)
167
190
 
168
191
  async with server_conn.session:
@@ -303,14 +326,17 @@ class MCPConnectionManager(ContextDependent):
303
326
  error_handler = get_stderr_handler(server_name)
304
327
  # Explicitly ensure we're using our custom logger for stderr
305
328
  logger.debug(f"{server_name}: Creating stdio client with custom error handler")
306
- return stdio_client(server_params, errlog=error_handler)
329
+ return _add_none_to_context(stdio_client(server_params, errlog=error_handler))
307
330
  elif config.transport == "sse":
308
- return sse_client(
309
- config.url,
310
- config.headers,
311
- sse_read_timeout=config.read_transport_sse_timeout_seconds,
331
+ return _add_none_to_context(
332
+ sse_client(
333
+ config.url,
334
+ config.headers,
335
+ sse_read_timeout=config.read_transport_sse_timeout_seconds,
336
+ )
312
337
  )
313
-
338
+ elif config.transport == "http":
339
+ return streamablehttp_client(config.url, config.headers)
314
340
  else:
315
341
  raise ValueError(f"Unsupported transport: {config.transport}")
316
342
 
@@ -335,7 +335,7 @@ def parse_args():
335
335
  parser.add_argument(
336
336
  "--transport",
337
337
  type=str,
338
- choices=["stdio", "sse"],
338
+ choices=["stdio", "sse", "http"],
339
339
  default="stdio",
340
340
  help="Transport to use (default: stdio)",
341
341
  )
@@ -502,14 +502,22 @@ async def async_main() -> int:
502
502
  return await test_prompt(args.test, config)
503
503
 
504
504
  # Start the server with the specified transport
505
- if config.transport == "stdio":
506
- await mcp.run_stdio_async()
507
- else: # sse
505
+ if config.transport == "sse": # sse
508
506
  # Set the host and port in settings before running the server
509
507
  mcp.settings.host = config.host
510
508
  mcp.settings.port = config.port
511
509
  logger.info(f"Starting SSE server on {config.host}:{config.port}")
512
510
  await mcp.run_sse_async()
511
+ elif config.transport == "http":
512
+ mcp.settings.host = config.host
513
+ mcp.settings.port = config.port
514
+ logger.info(f"Starting SSE server on {config.host}:{config.port}")
515
+ await mcp.run_streamable_http_async()
516
+ elif config.transport == "stdio":
517
+ await mcp.run_stdio_async()
518
+ else:
519
+ logger.error(f"Unknown transport: {config.transport}")
520
+ return 1
513
521
  return 0
514
522
 
515
523
 
@@ -140,9 +140,9 @@ class AgentMCPServer:
140
140
  print("Press Ctrl+C again to force exit.")
141
141
  self._graceful_shutdown_event.set()
142
142
 
143
- def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000) -> None:
143
+ def run(self, transport: str = "http", host: str = "0.0.0.0", port: int = 8000) -> None:
144
144
  """Run the MCP server synchronously."""
145
- if transport == "sse":
145
+ if transport in ["sse", "http"]:
146
146
  self.mcp_server.settings.host = host
147
147
  self.mcp_server.settings.port = port
148
148
 
@@ -180,12 +180,12 @@ class AgentMCPServer:
180
180
  asyncio.run(self._cleanup_stdio())
181
181
 
182
182
  async def run_async(
183
- self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000
183
+ self, transport: str = "http", host: str = "0.0.0.0", port: int = 8000
184
184
  ) -> None:
185
185
  """Run the MCP server asynchronously with improved shutdown handling."""
186
186
  # Use different handling strategies based on transport type
187
- if transport == "sse":
188
- # For SSE, use our enhanced shutdown handling
187
+ if transport in ["sse", "http"]:
188
+ # For SSE/HTTP, use our enhanced shutdown handling
189
189
  self._setup_signal_handlers()
190
190
 
191
191
  self.mcp_server.settings.host = host
@@ -236,9 +236,9 @@ class AgentMCPServer:
236
236
 
237
237
  async def _run_server_with_shutdown(self, transport: str):
238
238
  """Run the server with proper shutdown handling."""
239
- # This method is only used for SSE transport
240
- if transport != "sse":
241
- raise ValueError("This method should only be used with SSE transport")
239
+ # This method is used for SSE/HTTP transport
240
+ if transport not in ["sse", "http"]:
241
+ raise ValueError("This method should only be used with SSE or HTTP transport")
242
242
 
243
243
  # Start a monitor task for shutdown
244
244
  shutdown_monitor = asyncio.create_task(self._monitor_shutdown())
@@ -262,8 +262,11 @@ class AgentMCPServer:
262
262
  # Replace with our tracking version
263
263
  self.mcp_server._sse_transport.connect_sse = tracked_connect_sse
264
264
 
265
- # Run the server (SSE only)
266
- await self.mcp_server.run_sse_async()
265
+ # Run the server based on transport type
266
+ if transport == "sse":
267
+ await self.mcp_server.run_sse_async()
268
+ elif transport == "http":
269
+ await self.mcp_server.run_streamable_http_async()
267
270
  finally:
268
271
  # Cancel the monitor when the server exits
269
272
  shutdown_monitor.cancel()
@@ -18,6 +18,7 @@ from mcp.client.stdio import (
18
18
  StdioServerParameters,
19
19
  get_default_environment,
20
20
  )
21
+ from mcp.client.streamable_http import GetSessionIdCallback, streamablehttp_client
21
22
 
22
23
  from mcp_agent.config import (
23
24
  MCPServerAuthSettings,
@@ -27,7 +28,10 @@ from mcp_agent.config import (
27
28
  )
28
29
  from mcp_agent.logging.logger import get_logger
29
30
  from mcp_agent.mcp.logger_textio import get_stderr_handler
30
- from mcp_agent.mcp.mcp_connection_manager import MCPConnectionManager
31
+ from mcp_agent.mcp.mcp_connection_manager import (
32
+ MCPConnectionManager,
33
+ _add_none_to_context,
34
+ )
31
35
 
32
36
  logger = get_logger(__name__)
33
37
 
@@ -93,7 +97,12 @@ class ServerRegistry:
93
97
  self,
94
98
  server_name: str,
95
99
  client_session_factory: Callable[
96
- [MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
100
+ [
101
+ MemoryObjectReceiveStream,
102
+ MemoryObjectSendStream,
103
+ timedelta | None,
104
+ GetSessionIdCallback | None,
105
+ ],
97
106
  ClientSession,
98
107
  ] = ClientSession,
99
108
  ) -> AsyncGenerator[ClientSession, None]:
@@ -132,14 +141,18 @@ class ServerRegistry:
132
141
  )
133
142
 
134
143
  # Create a stderr handler that logs to our application logger
135
- async with stdio_client(server_params, errlog=get_stderr_handler(server_name)) as (
144
+ async with _add_none_to_context(
145
+ stdio_client(server_params, errlog=get_stderr_handler(server_name))
146
+ ) as (
136
147
  read_stream,
137
148
  write_stream,
149
+ _,
138
150
  ):
139
151
  session = client_session_factory(
140
152
  read_stream,
141
153
  write_stream,
142
154
  read_timeout_seconds,
155
+ None, # No callback for stdio
143
156
  )
144
157
  async with session:
145
158
  logger.info(f"{server_name}: Connected to server using stdio transport.")
@@ -153,15 +166,18 @@ class ServerRegistry:
153
166
  raise ValueError(f"URL is required for SSE transport: {server_name}")
154
167
 
155
168
  # Use sse_client to get the read and write streams
156
- async with sse_client(
157
- config.url,
158
- config.headers,
159
- sse_read_timeout=config.read_transport_sse_timeout_seconds,
160
- ) as (read_stream, write_stream):
169
+ async with _add_none_to_context(
170
+ sse_client(
171
+ config.url,
172
+ config.headers,
173
+ sse_read_timeout=config.read_transport_sse_timeout_seconds,
174
+ )
175
+ ) as (read_stream, write_stream, _):
161
176
  session = client_session_factory(
162
177
  read_stream,
163
178
  write_stream,
164
179
  read_timeout_seconds,
180
+ None, # No callback for stdio
165
181
  )
166
182
  async with session:
167
183
  logger.info(f"{server_name}: Connected to server using SSE transport.")
@@ -169,6 +185,27 @@ class ServerRegistry:
169
185
  yield session
170
186
  finally:
171
187
  logger.debug(f"{server_name}: Closed session to server")
188
+ elif config.transport == "http":
189
+ if not config.url:
190
+ raise ValueError(f"URL is required for SSE transport: {server_name}")
191
+
192
+ async with streamablehttp_client(config.url, config.headers) as (
193
+ read_stream,
194
+ write_stream,
195
+ _,
196
+ ):
197
+ session = client_session_factory(
198
+ read_stream,
199
+ write_stream,
200
+ read_timeout_seconds,
201
+ None, # No callback for stdio
202
+ )
203
+ async with session:
204
+ logger.info(f"{server_name}: Connected to server using HTTP transport.")
205
+ try:
206
+ yield session
207
+ finally:
208
+ logger.debug(f"{server_name}: Closed session to server")
172
209
 
173
210
  # Unsupported transport
174
211
  else:
@@ -179,7 +216,12 @@ class ServerRegistry:
179
216
  self,
180
217
  server_name: str,
181
218
  client_session_factory: Callable[
182
- [MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
219
+ [
220
+ MemoryObjectReceiveStream,
221
+ MemoryObjectSendStream,
222
+ timedelta | None,
223
+ GetSessionIdCallback,
224
+ ],
183
225
  ClientSession,
184
226
  ] = ClientSession,
185
227
  init_hook: InitHookCallable = None,
@@ -23,5 +23,5 @@ logger:
23
23
  mcp:
24
24
  servers:
25
25
  agent_one:
26
- transport: sse
27
- url: http://localhost:8001/sse
26
+ transport: http
27
+ url: http://localhost:8001/mcp
@@ -25,6 +25,7 @@ class ConsoleDisplay:
25
25
  config: Configuration object containing display preferences
26
26
  """
27
27
  self.config = config
28
+ self._markup = config.logger.enable_markup if config else True
28
29
 
29
30
  def show_tool_result(self, result: CallToolResult) -> None:
30
31
  """Display a tool result in a formatted panel."""
@@ -46,7 +47,7 @@ class ConsoleDisplay:
46
47
  if len(str(result.content)) > 360:
47
48
  panel.height = 8
48
49
 
49
- console.console.print(panel)
50
+ console.console.print(panel, markup=self._markup)
50
51
  console.console.print("\n")
51
52
 
52
53
  def show_oai_tool_result(self, result) -> None:
@@ -67,7 +68,7 @@ class ConsoleDisplay:
67
68
  if len(str(result)) > 360:
68
69
  panel.height = 8
69
70
 
70
- console.console.print(panel)
71
+ console.console.print(panel, markup=self._markup)
71
72
  console.console.print("\n")
72
73
 
73
74
  def show_tool_call(self, available_tools, tool_name, tool_args) -> None:
@@ -92,7 +93,7 @@ class ConsoleDisplay:
92
93
  if len(str(tool_args)) > 360:
93
94
  panel.height = 8
94
95
 
95
- console.console.print(panel)
96
+ console.console.print(panel, markup=self._markup)
96
97
  console.console.print("\n")
97
98
 
98
99
  def _format_tool_list(self, available_tools, selected_tool_name):
@@ -172,7 +173,7 @@ class ConsoleDisplay:
172
173
  subtitle=display_server_list,
173
174
  subtitle_align="left",
174
175
  )
175
- console.console.print(panel)
176
+ console.console.print(panel, markup=self._markup)
176
177
  console.console.print("\n")
177
178
 
178
179
  def show_user_message(
@@ -196,7 +197,7 @@ class ConsoleDisplay:
196
197
  subtitle=subtitle_text,
197
198
  subtitle_align="left",
198
199
  )
199
- console.console.print(panel)
200
+ console.console.print(panel, markup=self._markup)
200
201
  console.console.print("\n")
201
202
 
202
203
  async def show_prompt_loaded(
@@ -270,5 +271,5 @@ class ConsoleDisplay:
270
271
  subtitle_align="left",
271
272
  )
272
273
 
273
- console.console.print(panel)
274
+ console.console.print(panel, markup=self._markup)
274
275
  console.console.print("\n")