diff --git a/.gitignore b/.gitignore index 0f5fc4dd5..988cc3f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -199,3 +199,4 @@ fastagent.jsonl # JetBrains IDEs .idea/ +tests/e2e/smoke/base/weather_location.txt diff --git a/src/mcp_agent/config.py b/src/mcp_agent/config.py index 122e988f3..30881a088 100644 --- a/src/mcp_agent/config.py +++ b/src/mcp_agent/config.py @@ -249,6 +249,8 @@ class LoggerSettings(BaseModel): """Show MCP Sever tool calls on the console""" truncate_tools: bool = True """Truncate display of long tool calls""" + enable_markup: bool = True + """Enable markup in console output. Disable for outputs that may conflict with rich console formatting""" class Settings(BaseSettings): diff --git a/src/mcp_agent/ui/console_display.py b/src/mcp_agent/ui/console_display.py index 029f21762..9673843b2 100644 --- a/src/mcp_agent/ui/console_display.py +++ b/src/mcp_agent/ui/console_display.py @@ -25,6 +25,7 @@ def __init__(self, config=None) -> None: config: Configuration object containing display preferences """ self.config = config + self._markup = config.logger.enable_markup if config else True def show_tool_result(self, result: CallToolResult) -> None: """Display a tool result in a formatted panel.""" @@ -46,7 +47,7 @@ def show_tool_result(self, result: CallToolResult) -> None: if len(str(result.content)) > 360: panel.height = 8 - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") def show_oai_tool_result(self, result) -> None: @@ -67,7 +68,7 @@ def show_oai_tool_result(self, result) -> None: if len(str(result)) > 360: panel.height = 8 - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") def show_tool_call(self, available_tools, tool_name, tool_args) -> None: @@ -92,7 +93,7 @@ def show_tool_call(self, available_tools, tool_name, tool_args) -> None: if len(str(tool_args)) > 360: panel.height = 8 - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") def _format_tool_list(self, available_tools, selected_tool_name): @@ -172,7 +173,7 @@ async def show_assistant_message( subtitle=display_server_list, subtitle_align="left", ) - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") def show_user_message( @@ -196,7 +197,7 @@ def show_user_message( subtitle=subtitle_text, subtitle_align="left", ) - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") async def show_prompt_loaded( @@ -270,5 +271,5 @@ async def show_prompt_loaded( subtitle_align="left", ) - console.console.print(panel) + console.console.print(panel, markup=self._markup) console.console.print("\n") diff --git a/tests/integration/api/fastagent.config.markup.yaml b/tests/integration/api/fastagent.config.markup.yaml new file mode 100644 index 000000000..74251e742 --- /dev/null +++ b/tests/integration/api/fastagent.config.markup.yaml @@ -0,0 +1,54 @@ +# FastAgent Configuration File + +# Default Model Configuration: +# +# Takes format: +# .. (e.g. anthropic.claude-3-5-sonnet-20241022 or openai.o3-mini.low) +# Accepts aliases for Anthropic Models: haiku, haiku3, sonnet, sonnet35, opus, opus3 +# and OpenAI Models: gpt-4o-mini, gpt-4o, o1, o1-mini, o3-mini +# +# If not specified, defaults to "haiku". +# Can be overriden with a command line switch --model=, or within the Agent constructor. + +default_model: passthrough + +# Logging and Console Configuration: +logger: + # level: "debug" | "info" | "warning" | "error" + # type: "none" | "console" | "file" | "http" + # path: "/path/to/logfile.jsonl" + + # Switch the progress display on or off + progress_display: true + + # Show chat User/Assistant messages on the console + show_chat: true + # Show tool calls on the console + show_tools: true + # Truncate long tool responses on the console + truncate_tools: true + enable_markup: false + +# MCP Servers +mcp: + servers: + prompts: + command: "prompt-server" + args: ["playback.md"] + std_io: + command: "uv" + args: ["run", "integration_agent.py", "--server", "--transport", "stdio"] + sse: + transport: "sse" + url: "http://localhost:8723/sse" + card_test: + command: "uv" + args: ["run", "mcp_tools_server.py"] + hyphen-test: + command: "uv" + args: ["run", "mcp_tools_server.py"] + # borrows config from prompt-server + cwd_test: + command: "prompt-server" + args: ["multi.txt"] + cwd: "../prompt-server/" diff --git a/tests/integration/api/test_markup_config.py b/tests/integration/api/test_markup_config.py new file mode 100644 index 000000000..f57bdce9f --- /dev/null +++ b/tests/integration/api/test_markup_config.py @@ -0,0 +1,38 @@ +import pytest +from rich.errors import MarkupError + +from mcp_agent.core.prompt import Prompt + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_markup_raises_an_error(fast_agent): + """Test that the agent can process a multipart prompts using directory-specific config.""" + # Use the FastAgent instance from the test directory fixture + fast = fast_agent + + # Define the agent + @fast.agent( + "agent1", + instruction="You are a helpful AI Agent", + ) + async def agent_function(): + async with fast.run() as agent: + with pytest.raises(MarkupError): + assert "test1" in await agent.agent1.send(Prompt.user("'[/]test1")) + + await agent_function() + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_markup_disabled_does_not_error(markup_fast_agent): + @markup_fast_agent.agent( + "agent2", + instruction="You are a helpful AI Agent", + ) + async def agent_function(): + async with markup_fast_agent.run() as agent: + assert "test2" in await agent.agent2.send(Prompt.user("'[/]test2")) + + await agent_function() diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index d44dd15e9..009a25f8d 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -54,7 +54,41 @@ def fast_agent(request): # Explicitly create absolute path to the config file in the test directory config_file = os.path.join(test_dir, "fastagent.config.yaml") - + + # Create agent with local config using absolute path + agent = FastAgent( + "Test Agent", + config_path=config_file, # Use absolute path to local config in test directory + ignore_unknown_args=True, + ) + + # Provide the agent + yield agent + + # Restore original directory + os.chdir(original_cwd) + + +# Add a fixture that uses the test file's directory +@pytest.fixture +def markup_fast_agent(request): + """ + Creates a FastAgent with config from the test file's directory. + Automatically changes working directory to match the test file location. + """ + # Get the directory where the test file is located + test_module = request.module.__file__ + test_dir = os.path.dirname(test_module) + + # Save original directory + original_cwd = os.getcwd() + + # Change to the test file's directory + os.chdir(test_dir) + + # Explicitly create absolute path to the config file in the test directory + config_file = os.path.join(test_dir, "fastagent.config.markup.yaml") + # Create agent with local config using absolute path agent = FastAgent( "Test Agent", diff --git a/tests/integration/workflow/evaluator_optimizer/fastagent.config.yaml b/tests/integration/workflow/evaluator_optimizer/fastagent.config.yaml index 150591876..6b878d33f 100644 --- a/tests/integration/workflow/evaluator_optimizer/fastagent.config.yaml +++ b/tests/integration/workflow/evaluator_optimizer/fastagent.config.yaml @@ -1,7 +1,2 @@ -logging: - dir_path: null - file_path: null - level: DEBUG - mcp: - name: evaluator_optimizer_tests \ No newline at end of file + name: evaluator_optimizer_tests