fast-agent-mcp 0.2.56__py3-none-any.whl → 0.2.58__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.
- {fast_agent_mcp-0.2.56.dist-info → fast_agent_mcp-0.2.58.dist-info}/METADATA +2 -2
- {fast_agent_mcp-0.2.56.dist-info → fast_agent_mcp-0.2.58.dist-info}/RECORD +25 -24
- mcp_agent/agents/agent.py +10 -3
- mcp_agent/agents/base_agent.py +7 -2
- mcp_agent/config.py +3 -0
- mcp_agent/core/agent_app.py +18 -6
- mcp_agent/core/enhanced_prompt.py +10 -2
- mcp_agent/core/fastagent.py +2 -0
- mcp_agent/core/request_params.py +5 -0
- mcp_agent/event_progress.py +3 -0
- mcp_agent/human_input/elicitation_form.py +45 -33
- mcp_agent/llm/augmented_llm.py +16 -0
- mcp_agent/llm/providers/augmented_llm_anthropic.py +1 -0
- mcp_agent/llm/providers/augmented_llm_bedrock.py +890 -602
- mcp_agent/llm/providers/augmented_llm_google_native.py +1 -0
- mcp_agent/llm/providers/augmented_llm_openai.py +1 -0
- mcp_agent/llm/providers/bedrock_utils.py +216 -0
- mcp_agent/mcp/mcp_agent_client_session.py +105 -2
- mcp_agent/mcp/mcp_aggregator.py +92 -29
- mcp_agent/mcp/mcp_connection_manager.py +19 -0
- mcp_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +25 -3
- mcp_agent/ui/console_display.py +105 -15
- {fast_agent_mcp-0.2.56.dist-info → fast_agent_mcp-0.2.58.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.56.dist-info → fast_agent_mcp-0.2.58.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.56.dist-info → fast_agent_mcp-0.2.58.dist-info}/licenses/LICENSE +0 -0
mcp_agent/ui/console_display.py
CHANGED
|
@@ -19,6 +19,61 @@ from mcp_agent.mcp.mcp_aggregator import MCPAggregator
|
|
|
19
19
|
HUMAN_INPUT_TOOL_NAME = "__human_input__"
|
|
20
20
|
CODE_STYLE = "native"
|
|
21
21
|
|
|
22
|
+
HTML_ESCAPE_CHARS = {"&": "&", "<": "<", ">": ">", '"': """, "'": "'"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _prepare_markdown_content(content: str, escape_xml: bool = True) -> str:
|
|
26
|
+
"""Prepare content for markdown rendering by escaping HTML/XML tags
|
|
27
|
+
while preserving code blocks and inline code.
|
|
28
|
+
|
|
29
|
+
This ensures XML/HTML tags are displayed as visible text rather than
|
|
30
|
+
being interpreted as markup by the markdown renderer.
|
|
31
|
+
|
|
32
|
+
Note: This method does not handle overlapping code blocks (e.g., if inline
|
|
33
|
+
code appears within a fenced code block range). In practice, this is not
|
|
34
|
+
an issue since markdown syntax doesn't support such overlapping.
|
|
35
|
+
"""
|
|
36
|
+
if not escape_xml or not isinstance(content, str):
|
|
37
|
+
return content
|
|
38
|
+
|
|
39
|
+
protected_ranges = []
|
|
40
|
+
import re
|
|
41
|
+
|
|
42
|
+
# Protect fenced code blocks (don't escape anything inside these)
|
|
43
|
+
code_block_pattern = r"```[\s\S]*?```"
|
|
44
|
+
for match in re.finditer(code_block_pattern, content):
|
|
45
|
+
protected_ranges.append((match.start(), match.end()))
|
|
46
|
+
|
|
47
|
+
# Protect inline code (don't escape anything inside these)
|
|
48
|
+
inline_code_pattern = r"(?<!`)`(?!``)[^`\n]+`(?!`)"
|
|
49
|
+
for match in re.finditer(inline_code_pattern, content):
|
|
50
|
+
protected_ranges.append((match.start(), match.end()))
|
|
51
|
+
|
|
52
|
+
protected_ranges.sort(key=lambda x: x[0])
|
|
53
|
+
|
|
54
|
+
# Build the escaped content
|
|
55
|
+
result = []
|
|
56
|
+
last_end = 0
|
|
57
|
+
|
|
58
|
+
for start, end in protected_ranges:
|
|
59
|
+
# Escape everything outside protected ranges
|
|
60
|
+
unprotected_text = content[last_end:start]
|
|
61
|
+
for char, replacement in HTML_ESCAPE_CHARS.items():
|
|
62
|
+
unprotected_text = unprotected_text.replace(char, replacement)
|
|
63
|
+
result.append(unprotected_text)
|
|
64
|
+
|
|
65
|
+
# Keep protected ranges (code blocks) as-is
|
|
66
|
+
result.append(content[start:end])
|
|
67
|
+
last_end = end
|
|
68
|
+
|
|
69
|
+
# Escape any remaining content after the last protected range
|
|
70
|
+
remainder_text = content[last_end:]
|
|
71
|
+
for char, replacement in HTML_ESCAPE_CHARS.items():
|
|
72
|
+
remainder_text = remainder_text.replace(char, replacement)
|
|
73
|
+
result.append(remainder_text)
|
|
74
|
+
|
|
75
|
+
return "".join(result)
|
|
76
|
+
|
|
22
77
|
|
|
23
78
|
class ConsoleDisplay:
|
|
24
79
|
"""
|
|
@@ -35,6 +90,49 @@ class ConsoleDisplay:
|
|
|
35
90
|
"""
|
|
36
91
|
self.config = config
|
|
37
92
|
self._markup = config.logger.enable_markup if config else True
|
|
93
|
+
self._escape_xml = True
|
|
94
|
+
|
|
95
|
+
def _render_content_smartly(self, content: str, check_markdown_markers: bool = False) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Helper method to intelligently render content based on its type.
|
|
98
|
+
|
|
99
|
+
- Pure XML: Use syntax highlighting for readability
|
|
100
|
+
- Markdown (with markers): Use markdown rendering with proper escaping
|
|
101
|
+
- Plain text: Display as-is (when check_markdown_markers=True and no markers found)
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
content: The text content to render
|
|
105
|
+
check_markdown_markers: If True, only use markdown rendering when markers are present
|
|
106
|
+
"""
|
|
107
|
+
import re
|
|
108
|
+
|
|
109
|
+
from rich.markdown import Markdown
|
|
110
|
+
|
|
111
|
+
# Check if content appears to be primarily XML
|
|
112
|
+
xml_pattern = r"^<[a-zA-Z_][a-zA-Z0-9_-]*[^>]*>"
|
|
113
|
+
is_xml_content = bool(re.match(xml_pattern, content.strip())) and content.count("<") > 5
|
|
114
|
+
|
|
115
|
+
if is_xml_content:
|
|
116
|
+
# Display XML content with syntax highlighting for better readability
|
|
117
|
+
from rich.syntax import Syntax
|
|
118
|
+
|
|
119
|
+
syntax = Syntax(content, "xml", theme=CODE_STYLE, line_numbers=False)
|
|
120
|
+
console.console.print(syntax, markup=self._markup)
|
|
121
|
+
elif check_markdown_markers:
|
|
122
|
+
# Check for markdown markers before deciding to use markdown rendering
|
|
123
|
+
if any(marker in content for marker in ["##", "**", "*", "`", "---", "###"]):
|
|
124
|
+
# Has markdown markers - render as markdown with escaping
|
|
125
|
+
prepared_content = _prepare_markdown_content(content, self._escape_xml)
|
|
126
|
+
md = Markdown(prepared_content, code_theme=CODE_STYLE)
|
|
127
|
+
console.console.print(md, markup=self._markup)
|
|
128
|
+
else:
|
|
129
|
+
# Plain text - display as-is
|
|
130
|
+
console.console.print(content, markup=self._markup)
|
|
131
|
+
else:
|
|
132
|
+
# Always treat as markdown with proper escaping
|
|
133
|
+
prepared_content = _prepare_markdown_content(content, self._escape_xml)
|
|
134
|
+
md = Markdown(prepared_content, code_theme=CODE_STYLE)
|
|
135
|
+
console.console.print(md, markup=self._markup)
|
|
38
136
|
|
|
39
137
|
def show_tool_result(self, result: CallToolResult, name: str | None = None) -> None:
|
|
40
138
|
"""Display a tool result in the new visual style."""
|
|
@@ -341,7 +439,6 @@ class ConsoleDisplay:
|
|
|
341
439
|
model: str | None = None,
|
|
342
440
|
) -> None:
|
|
343
441
|
"""Display an assistant message in a formatted panel."""
|
|
344
|
-
from rich.markdown import Markdown
|
|
345
442
|
|
|
346
443
|
if not self.config or not self.config.logger.show_chat:
|
|
347
444
|
return
|
|
@@ -385,9 +482,8 @@ class ConsoleDisplay:
|
|
|
385
482
|
json = JSON(message_text)
|
|
386
483
|
console.console.print(json, markup=self._markup)
|
|
387
484
|
except (JSONDecodeError, TypeError, ValueError):
|
|
388
|
-
#
|
|
389
|
-
|
|
390
|
-
console.console.print(md, markup=self._markup)
|
|
485
|
+
# Use the smart rendering helper to handle XML vs Markdown
|
|
486
|
+
self._render_content_smartly(content)
|
|
391
487
|
else:
|
|
392
488
|
# Handle Rich Text objects directly
|
|
393
489
|
console.console.print(message_text, markup=self._markup)
|
|
@@ -475,7 +571,6 @@ class ConsoleDisplay:
|
|
|
475
571
|
self, message, model: str | None = None, chat_turn: int = 0, name: str | None = None
|
|
476
572
|
) -> None:
|
|
477
573
|
"""Display a user message in the new visual style."""
|
|
478
|
-
from rich.markdown import Markdown
|
|
479
574
|
|
|
480
575
|
if not self.config or not self.config.logger.show_chat:
|
|
481
576
|
return
|
|
@@ -495,9 +590,8 @@ class ConsoleDisplay:
|
|
|
495
590
|
|
|
496
591
|
# Display content as markdown if it looks like markdown, otherwise as text
|
|
497
592
|
if isinstance(message, str):
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
console.console.print(md, markup=self._markup)
|
|
593
|
+
# Use the smart rendering helper to handle XML vs Markdown
|
|
594
|
+
self._render_content_smartly(message)
|
|
501
595
|
else:
|
|
502
596
|
# Handle Text objects directly
|
|
503
597
|
console.console.print(message, markup=self._markup)
|
|
@@ -587,7 +681,7 @@ class ConsoleDisplay:
|
|
|
587
681
|
Args:
|
|
588
682
|
parallel_agent: The parallel agent containing fan_out_agents with results
|
|
589
683
|
"""
|
|
590
|
-
|
|
684
|
+
|
|
591
685
|
from rich.text import Text
|
|
592
686
|
|
|
593
687
|
if self.config and not self.config.logger.show_chat:
|
|
@@ -674,13 +768,9 @@ class ConsoleDisplay:
|
|
|
674
768
|
console.console.print(left + " " * padding + right, markup=self._markup)
|
|
675
769
|
console.console.print()
|
|
676
770
|
|
|
677
|
-
# Display content
|
|
771
|
+
# Display content based on its type (check for markdown markers in parallel results)
|
|
678
772
|
content = result["content"]
|
|
679
|
-
|
|
680
|
-
md = Markdown(content, code_theme=CODE_STYLE)
|
|
681
|
-
console.console.print(md, markup=self._markup)
|
|
682
|
-
else:
|
|
683
|
-
console.console.print(content, markup=self._markup)
|
|
773
|
+
self._render_content_smartly(content, check_markdown_markers=True)
|
|
684
774
|
|
|
685
775
|
# Summary
|
|
686
776
|
console.console.print()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|