fast-agent-mcp 0.3.12__py3-none-any.whl → 0.3.13__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/agents/llm_agent.py +1 -1
- fast_agent/llm/provider/google/google_converter.py +10 -3
- fast_agent/mcp/mcp_agent_client_session.py +13 -0
- fast_agent/mcp/mcp_connection_manager.py +58 -15
- fast_agent/ui/console_display.py +52 -2
- fast_agent/ui/mcp_display.py +51 -14
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.13.dist-info}/METADATA +15 -6
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.13.dist-info}/RECORD +11 -11
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.13.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.13.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.13.dist-info}/licenses/LICENSE +0 -0
fast_agent/agents/llm_agent.py
CHANGED
|
@@ -170,7 +170,7 @@ class LlmAgent(LlmDecorator):
|
|
|
170
170
|
combined += segment
|
|
171
171
|
additional_message_text = combined
|
|
172
172
|
|
|
173
|
-
message_text = message
|
|
173
|
+
message_text = message
|
|
174
174
|
|
|
175
175
|
# Use provided name/model or fall back to defaults
|
|
176
176
|
display_name = name if name is not None else self.name
|
|
@@ -53,6 +53,14 @@ class GoogleConverter:
|
|
|
53
53
|
if key in unsupported_keys:
|
|
54
54
|
continue # Skip this key
|
|
55
55
|
|
|
56
|
+
# Rewrite unsupported 'const' to a safe form for Gemini tools
|
|
57
|
+
# - For string const, convert to enum [value]
|
|
58
|
+
# - For non-string const (booleans/numbers), drop the constraint
|
|
59
|
+
if key == "const":
|
|
60
|
+
if isinstance(value, str):
|
|
61
|
+
cleaned_schema["enum"] = [value]
|
|
62
|
+
continue
|
|
63
|
+
|
|
56
64
|
if (
|
|
57
65
|
key == "format"
|
|
58
66
|
and schema.get("type") == "string"
|
|
@@ -140,9 +148,8 @@ class GoogleConverter:
|
|
|
140
148
|
)
|
|
141
149
|
elif is_resource_content(part_content):
|
|
142
150
|
assert isinstance(part_content, EmbeddedResource)
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
and isinstance(part_content.resource, BlobResourceContents)
|
|
151
|
+
if "application/pdf" == part_content.resource.mimeType and isinstance(
|
|
152
|
+
part_content.resource, BlobResourceContents
|
|
146
153
|
):
|
|
147
154
|
pdf_bytes = base64.b64decode(part_content.resource.blob)
|
|
148
155
|
parts.append(
|
|
@@ -4,6 +4,7 @@ It adds logging and supports sampling requests.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from datetime import timedelta
|
|
7
|
+
from time import perf_counter
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
9
10
|
from mcp import ClientSession, ServerNotification
|
|
@@ -207,6 +208,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
207
208
|
) -> ReceiveResultT:
|
|
208
209
|
logger.debug("send_request: request=", data=request.model_dump())
|
|
209
210
|
request_id = getattr(self, "_request_id", None)
|
|
211
|
+
start_time = perf_counter()
|
|
210
212
|
try:
|
|
211
213
|
result = await super().send_request(
|
|
212
214
|
request=request,
|
|
@@ -220,6 +222,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
220
222
|
data=result.model_dump() if result is not None else "no response returned",
|
|
221
223
|
)
|
|
222
224
|
self._attach_transport_channel(request_id, result)
|
|
225
|
+
self._attach_transport_elapsed(result, perf_counter() - start_time)
|
|
223
226
|
return result
|
|
224
227
|
except Exception as e:
|
|
225
228
|
# Handle connection errors cleanly
|
|
@@ -250,6 +253,16 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
250
253
|
# If result cannot be mutated, ignore silently
|
|
251
254
|
pass
|
|
252
255
|
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _attach_transport_elapsed(result, elapsed: float | None) -> None:
|
|
258
|
+
if result is None or elapsed is None:
|
|
259
|
+
return
|
|
260
|
+
try:
|
|
261
|
+
setattr(result, "transport_elapsed", max(elapsed, 0.0))
|
|
262
|
+
except Exception:
|
|
263
|
+
# Ignore if result is immutable
|
|
264
|
+
pass
|
|
265
|
+
|
|
253
266
|
async def _received_notification(self, notification: ServerNotification) -> None:
|
|
254
267
|
"""
|
|
255
268
|
Can be overridden by subclasses to handle a notification without needing
|
|
@@ -38,6 +38,8 @@ from fast_agent.mcp.streamable_http_tracking import tracking_streamablehttp_clie
|
|
|
38
38
|
from fast_agent.mcp.transport_tracking import TransportChannelMetrics
|
|
39
39
|
|
|
40
40
|
if TYPE_CHECKING:
|
|
41
|
+
from mcp.client.auth import OAuthClientProvider
|
|
42
|
+
|
|
41
43
|
from fast_agent.context import Context
|
|
42
44
|
from fast_agent.mcp_server_registry import ServerRegistry
|
|
43
45
|
|
|
@@ -65,6 +67,38 @@ def _add_none_to_context(context_manager):
|
|
|
65
67
|
return StreamingContextAdapter(context_manager)
|
|
66
68
|
|
|
67
69
|
|
|
70
|
+
def _prepare_headers_and_auth(
|
|
71
|
+
server_config: MCPServerSettings,
|
|
72
|
+
) -> tuple[dict[str, str], Optional["OAuthClientProvider"], set[str]]:
|
|
73
|
+
"""
|
|
74
|
+
Prepare request headers and determine if OAuth authentication should be used.
|
|
75
|
+
|
|
76
|
+
Returns a copy of the headers, an OAuth auth provider when applicable, and the set
|
|
77
|
+
of user-supplied authorization header keys.
|
|
78
|
+
"""
|
|
79
|
+
headers: dict[str, str] = dict(server_config.headers or {})
|
|
80
|
+
auth_header_keys = {"authorization", "x-hf-authorization"}
|
|
81
|
+
user_provided_auth_keys = {key for key in headers if key.lower() in auth_header_keys}
|
|
82
|
+
|
|
83
|
+
# OAuth is only relevant for SSE/HTTP transports and should be skipped when the
|
|
84
|
+
# user has already supplied explicit Authorization headers.
|
|
85
|
+
if server_config.transport not in ("sse", "http") or user_provided_auth_keys:
|
|
86
|
+
return headers, None, user_provided_auth_keys
|
|
87
|
+
|
|
88
|
+
oauth_auth = build_oauth_provider(server_config)
|
|
89
|
+
if oauth_auth is not None:
|
|
90
|
+
# Scrub Authorization headers so OAuth-managed credentials are the only ones sent.
|
|
91
|
+
for header_name in (
|
|
92
|
+
"Authorization",
|
|
93
|
+
"authorization",
|
|
94
|
+
"X-HF-Authorization",
|
|
95
|
+
"x-hf-authorization",
|
|
96
|
+
):
|
|
97
|
+
headers.pop(header_name, None)
|
|
98
|
+
|
|
99
|
+
return headers, oauth_auth, user_provided_auth_keys
|
|
100
|
+
|
|
101
|
+
|
|
68
102
|
class ServerConnection:
|
|
69
103
|
"""
|
|
70
104
|
Represents a long-lived MCP server connection, including:
|
|
@@ -113,7 +147,9 @@ class ServerConnection:
|
|
|
113
147
|
self.server_implementation: Implementation | None = None
|
|
114
148
|
self.client_capabilities: dict | None = None
|
|
115
149
|
self.server_instructions_available: bool = False
|
|
116
|
-
self.server_instructions_enabled: bool =
|
|
150
|
+
self.server_instructions_enabled: bool = (
|
|
151
|
+
server_config.include_instructions if server_config else True
|
|
152
|
+
)
|
|
117
153
|
self.session_id: str | None = None
|
|
118
154
|
self._get_session_id_cb: GetSessionIdCallback | None = None
|
|
119
155
|
self.transport_metrics: TransportChannelMetrics | None = None
|
|
@@ -404,7 +440,9 @@ class MCPConnectionManager(ContextDependent):
|
|
|
404
440
|
|
|
405
441
|
logger.debug(f"{server_name}: Found server configuration=", data=config.model_dump())
|
|
406
442
|
|
|
407
|
-
transport_metrics =
|
|
443
|
+
transport_metrics = (
|
|
444
|
+
TransportChannelMetrics() if config.transport in ("http", "stdio") else None
|
|
445
|
+
)
|
|
408
446
|
|
|
409
447
|
def transport_context_factory():
|
|
410
448
|
if config.transport == "stdio":
|
|
@@ -425,7 +463,9 @@ class MCPConnectionManager(ContextDependent):
|
|
|
425
463
|
|
|
426
464
|
channel_hook = transport_metrics.record_event if transport_metrics else None
|
|
427
465
|
return _add_none_to_context(
|
|
428
|
-
tracking_stdio_client(
|
|
466
|
+
tracking_stdio_client(
|
|
467
|
+
server_params, channel_hook=channel_hook, errlog=error_handler
|
|
468
|
+
)
|
|
429
469
|
)
|
|
430
470
|
elif config.transport == "sse":
|
|
431
471
|
if not config.url:
|
|
@@ -434,12 +474,12 @@ class MCPConnectionManager(ContextDependent):
|
|
|
434
474
|
)
|
|
435
475
|
# Suppress MCP library error spam
|
|
436
476
|
self._suppress_mcp_sse_errors()
|
|
437
|
-
oauth_auth =
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
477
|
+
headers, oauth_auth, user_auth_keys = _prepare_headers_and_auth(config)
|
|
478
|
+
if user_auth_keys:
|
|
479
|
+
logger.debug(
|
|
480
|
+
f"{server_name}: Using user-specified auth header(s); skipping OAuth provider.",
|
|
481
|
+
user_auth_headers=sorted(user_auth_keys),
|
|
482
|
+
)
|
|
443
483
|
return _add_none_to_context(
|
|
444
484
|
sse_client(
|
|
445
485
|
config.url,
|
|
@@ -453,19 +493,22 @@ class MCPConnectionManager(ContextDependent):
|
|
|
453
493
|
raise ValueError(
|
|
454
494
|
f"Server '{server_name}' uses http transport but no url is specified"
|
|
455
495
|
)
|
|
456
|
-
oauth_auth =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
496
|
+
headers, oauth_auth, user_auth_keys = _prepare_headers_and_auth(config)
|
|
497
|
+
if user_auth_keys:
|
|
498
|
+
logger.debug(
|
|
499
|
+
f"{server_name}: Using user-specified auth header(s); skipping OAuth provider.",
|
|
500
|
+
user_auth_headers=sorted(user_auth_keys),
|
|
501
|
+
)
|
|
461
502
|
channel_hook = None
|
|
462
503
|
if transport_metrics is not None:
|
|
504
|
+
|
|
463
505
|
def channel_hook(event):
|
|
464
506
|
try:
|
|
465
507
|
transport_metrics.record_event(event)
|
|
466
508
|
except Exception: # pragma: no cover - defensive guard
|
|
467
509
|
logger.debug(
|
|
468
|
-
"%s: transport metrics hook failed",
|
|
510
|
+
"%s: transport metrics hook failed",
|
|
511
|
+
server_name,
|
|
469
512
|
exc_info=True,
|
|
470
513
|
)
|
|
471
514
|
|
fast_agent/ui/console_display.py
CHANGED
|
@@ -6,6 +6,7 @@ from mcp.types import CallToolResult
|
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
9
|
+
from fast_agent.constants import REASONING
|
|
9
10
|
from fast_agent.ui import console
|
|
10
11
|
from fast_agent.ui.mcp_ui_utils import UILink
|
|
11
12
|
from fast_agent.ui.mermaid_utils import (
|
|
@@ -144,6 +145,25 @@ class ConsoleDisplay:
|
|
|
144
145
|
self._markup = config.logger.enable_markup if config else True
|
|
145
146
|
self._escape_xml = True
|
|
146
147
|
|
|
148
|
+
@staticmethod
|
|
149
|
+
def _format_elapsed(elapsed: float) -> str:
|
|
150
|
+
"""Format elapsed seconds for display."""
|
|
151
|
+
if elapsed < 0:
|
|
152
|
+
elapsed = 0.0
|
|
153
|
+
if elapsed < 0.001:
|
|
154
|
+
return "<1ms"
|
|
155
|
+
if elapsed < 1:
|
|
156
|
+
return f"{elapsed * 1000:.0f}ms"
|
|
157
|
+
if elapsed < 10:
|
|
158
|
+
return f"{elapsed:.2f}s"
|
|
159
|
+
if elapsed < 60:
|
|
160
|
+
return f"{elapsed:.1f}s"
|
|
161
|
+
minutes, seconds = divmod(elapsed, 60)
|
|
162
|
+
if minutes < 60:
|
|
163
|
+
return f"{int(minutes)}m {seconds:02.0f}s"
|
|
164
|
+
hours, minutes = divmod(int(minutes), 60)
|
|
165
|
+
return f"{hours}h {minutes:02d}m"
|
|
166
|
+
|
|
147
167
|
def display_message(
|
|
148
168
|
self,
|
|
149
169
|
content: Any,
|
|
@@ -156,6 +176,7 @@ class ConsoleDisplay:
|
|
|
156
176
|
is_error: bool = False,
|
|
157
177
|
truncate_content: bool = True,
|
|
158
178
|
additional_message: Text | None = None,
|
|
179
|
+
pre_content: Text | None = None,
|
|
159
180
|
) -> None:
|
|
160
181
|
"""
|
|
161
182
|
Unified method to display formatted messages to the console.
|
|
@@ -170,6 +191,8 @@ class ConsoleDisplay:
|
|
|
170
191
|
max_item_length: Optional max length for bottom metadata items (with ellipsis)
|
|
171
192
|
is_error: For tool results, whether this is an error (uses red color)
|
|
172
193
|
truncate_content: Whether to truncate long content
|
|
194
|
+
additional_message: Optional Rich Text appended after the main content
|
|
195
|
+
pre_content: Optional Rich Text shown before the main content
|
|
173
196
|
"""
|
|
174
197
|
# Get configuration for this message type
|
|
175
198
|
config = MESSAGE_CONFIGS[message_type]
|
|
@@ -191,6 +214,8 @@ class ConsoleDisplay:
|
|
|
191
214
|
self._create_combined_separator_status(left, right_info)
|
|
192
215
|
|
|
193
216
|
# Display the content
|
|
217
|
+
if pre_content and pre_content.plain:
|
|
218
|
+
console.console.print(pre_content, markup=self._markup)
|
|
194
219
|
self._display_content(
|
|
195
220
|
content, truncate_content, is_error, message_type, check_markdown_markers=False
|
|
196
221
|
)
|
|
@@ -544,7 +569,7 @@ class ConsoleDisplay:
|
|
|
544
569
|
|
|
545
570
|
# Build transport channel info for bottom bar
|
|
546
571
|
channel = getattr(result, "transport_channel", None)
|
|
547
|
-
|
|
572
|
+
bottom_metadata_items: List[str] = []
|
|
548
573
|
if channel:
|
|
549
574
|
# Format channel info for bottom bar
|
|
550
575
|
if channel == "post-json":
|
|
@@ -560,7 +585,13 @@ class ConsoleDisplay:
|
|
|
560
585
|
else:
|
|
561
586
|
transport_info = channel.upper()
|
|
562
587
|
|
|
563
|
-
|
|
588
|
+
bottom_metadata_items.append(transport_info)
|
|
589
|
+
|
|
590
|
+
elapsed = getattr(result, "transport_elapsed", None)
|
|
591
|
+
if isinstance(elapsed, (int, float)):
|
|
592
|
+
bottom_metadata_items.append(self._format_elapsed(float(elapsed)))
|
|
593
|
+
|
|
594
|
+
bottom_metadata = bottom_metadata_items or None
|
|
564
595
|
|
|
565
596
|
# Build right info (without channel info)
|
|
566
597
|
right_info = f"[dim]tool result - {status}[/dim]"
|
|
@@ -724,8 +755,26 @@ class ConsoleDisplay:
|
|
|
724
755
|
# Extract text from PromptMessageExtended if needed
|
|
725
756
|
from fast_agent.types import PromptMessageExtended
|
|
726
757
|
|
|
758
|
+
pre_content: Text | None = None
|
|
759
|
+
|
|
727
760
|
if isinstance(message_text, PromptMessageExtended):
|
|
728
761
|
display_text = message_text.last_text() or ""
|
|
762
|
+
|
|
763
|
+
channels = message_text.channels or {}
|
|
764
|
+
reasoning_blocks = channels.get(REASONING) or []
|
|
765
|
+
if reasoning_blocks:
|
|
766
|
+
from fast_agent.mcp.helpers.content_helpers import get_text
|
|
767
|
+
|
|
768
|
+
reasoning_segments = []
|
|
769
|
+
for block in reasoning_blocks:
|
|
770
|
+
text = get_text(block)
|
|
771
|
+
if text:
|
|
772
|
+
reasoning_segments.append(text)
|
|
773
|
+
|
|
774
|
+
if reasoning_segments:
|
|
775
|
+
joined = "\n".join(reasoning_segments)
|
|
776
|
+
if joined.strip():
|
|
777
|
+
pre_content = Text(joined, style="dim default")
|
|
729
778
|
else:
|
|
730
779
|
display_text = message_text
|
|
731
780
|
|
|
@@ -743,6 +792,7 @@ class ConsoleDisplay:
|
|
|
743
792
|
max_item_length=max_item_length,
|
|
744
793
|
truncate_content=False, # Assistant messages shouldn't be truncated
|
|
745
794
|
additional_message=additional_message,
|
|
795
|
+
pre_content=pre_content,
|
|
746
796
|
)
|
|
747
797
|
|
|
748
798
|
# Handle mermaid diagrams separately (after the main message)
|
fast_agent/ui/mcp_display.py
CHANGED
|
@@ -54,6 +54,17 @@ class Colours:
|
|
|
54
54
|
TEXT_CYAN = "cyan"
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
# Symbol definitions for timelines and legends
|
|
58
|
+
SYMBOL_IDLE = "·"
|
|
59
|
+
SYMBOL_ERROR = "●"
|
|
60
|
+
SYMBOL_RESPONSE = "▼"
|
|
61
|
+
SYMBOL_NOTIFICATION = "●"
|
|
62
|
+
SYMBOL_REQUEST = "◆"
|
|
63
|
+
SYMBOL_STDIO_ACTIVITY = "●"
|
|
64
|
+
SYMBOL_PING = "●"
|
|
65
|
+
SYMBOL_DISABLED = "▽"
|
|
66
|
+
|
|
67
|
+
|
|
57
68
|
# Color mappings for different contexts
|
|
58
69
|
TIMELINE_COLORS = {
|
|
59
70
|
"error": Colours.ERROR,
|
|
@@ -263,11 +274,19 @@ def _build_inline_timeline(buckets: Iterable[str]) -> str:
|
|
|
263
274
|
for state in buckets:
|
|
264
275
|
color = TIMELINE_COLORS.get(state, Colours.NONE)
|
|
265
276
|
if state in {"idle", "none"}:
|
|
266
|
-
symbol =
|
|
277
|
+
symbol = SYMBOL_IDLE
|
|
267
278
|
elif state == "request":
|
|
268
|
-
symbol =
|
|
279
|
+
symbol = SYMBOL_REQUEST
|
|
280
|
+
elif state == "notification":
|
|
281
|
+
symbol = SYMBOL_NOTIFICATION
|
|
282
|
+
elif state == "error":
|
|
283
|
+
symbol = SYMBOL_ERROR
|
|
284
|
+
elif state == "ping":
|
|
285
|
+
symbol = SYMBOL_PING
|
|
286
|
+
elif state == "disabled":
|
|
287
|
+
symbol = SYMBOL_DISABLED
|
|
269
288
|
else:
|
|
270
|
-
symbol =
|
|
289
|
+
symbol = SYMBOL_RESPONSE
|
|
271
290
|
timeline += f"[bold {color}]{symbol}[/bold {color}]"
|
|
272
291
|
timeline += " [dim]now[/dim]"
|
|
273
292
|
return timeline
|
|
@@ -416,7 +435,12 @@ def _render_channel_summary(status: ServerStatus, indent: str, total_width: int)
|
|
|
416
435
|
elif arrow_style == Colours.ARROW_ERROR and "GET" in label:
|
|
417
436
|
# Highlight GET stream errors in red to match the arrow indicator
|
|
418
437
|
label_style = Colours.TEXT_ERROR
|
|
419
|
-
elif
|
|
438
|
+
elif (
|
|
439
|
+
channel.request_count == 0
|
|
440
|
+
and channel.response_count == 0
|
|
441
|
+
and channel.notification_count == 0
|
|
442
|
+
and (channel.ping_count or 0) == 0
|
|
443
|
+
):
|
|
420
444
|
# No activity = dim
|
|
421
445
|
label_style = Colours.TEXT_DIM
|
|
422
446
|
else:
|
|
@@ -431,19 +455,26 @@ def _render_channel_summary(status: ServerStatus, indent: str, total_width: int)
|
|
|
431
455
|
for bucket_state in channel.activity_buckets:
|
|
432
456
|
color = timeline_color_map.get(bucket_state, "dim")
|
|
433
457
|
if bucket_state in {"idle", "none"}:
|
|
434
|
-
symbol =
|
|
458
|
+
symbol = SYMBOL_IDLE
|
|
435
459
|
elif is_stdio:
|
|
436
|
-
|
|
437
|
-
symbol = "●"
|
|
460
|
+
symbol = SYMBOL_STDIO_ACTIVITY
|
|
438
461
|
elif bucket_state == "request":
|
|
439
|
-
symbol =
|
|
462
|
+
symbol = SYMBOL_REQUEST
|
|
463
|
+
elif bucket_state == "notification":
|
|
464
|
+
symbol = SYMBOL_NOTIFICATION
|
|
465
|
+
elif bucket_state == "error":
|
|
466
|
+
symbol = SYMBOL_ERROR
|
|
467
|
+
elif bucket_state == "ping":
|
|
468
|
+
symbol = SYMBOL_PING
|
|
469
|
+
elif bucket_state == "disabled":
|
|
470
|
+
symbol = SYMBOL_DISABLED
|
|
440
471
|
else:
|
|
441
|
-
symbol =
|
|
472
|
+
symbol = SYMBOL_RESPONSE
|
|
442
473
|
line.append(symbol, style=f"bold {color}")
|
|
443
474
|
else:
|
|
444
475
|
# Show dim dots for no activity
|
|
445
476
|
for _ in range(20):
|
|
446
|
-
line.append(
|
|
477
|
+
line.append(SYMBOL_IDLE, style="black dim")
|
|
447
478
|
line.append(" now", style="dim")
|
|
448
479
|
|
|
449
480
|
# Metrics - different layouts for stdio vs HTTP
|
|
@@ -548,11 +579,17 @@ def _render_channel_summary(status: ServerStatus, indent: str, total_width: int)
|
|
|
548
579
|
if i > 0:
|
|
549
580
|
footer.append(" ", style="dim")
|
|
550
581
|
if name == "idle":
|
|
551
|
-
symbol =
|
|
582
|
+
symbol = SYMBOL_IDLE
|
|
552
583
|
elif name == "request":
|
|
553
|
-
symbol =
|
|
584
|
+
symbol = SYMBOL_REQUEST
|
|
585
|
+
elif name == "notification":
|
|
586
|
+
symbol = SYMBOL_NOTIFICATION
|
|
587
|
+
elif name == "error":
|
|
588
|
+
symbol = SYMBOL_ERROR
|
|
589
|
+
elif name == "ping":
|
|
590
|
+
symbol = SYMBOL_PING
|
|
554
591
|
else:
|
|
555
|
-
symbol =
|
|
592
|
+
symbol = SYMBOL_RESPONSE
|
|
556
593
|
footer.append(symbol, style=f"{color}")
|
|
557
594
|
footer.append(f" {name}", style="dim")
|
|
558
595
|
|
|
@@ -619,7 +656,7 @@ async def render_mcp_status(agent, indent: str = "") -> None:
|
|
|
619
656
|
|
|
620
657
|
header_label = Text(indent)
|
|
621
658
|
header_label.append("▎", style=Colours.TEXT_CYAN)
|
|
622
|
-
header_label.append(
|
|
659
|
+
header_label.append(SYMBOL_RESPONSE, style=f"dim {Colours.TEXT_CYAN}")
|
|
623
660
|
header_label.append(f" [{index:2}] ", style=Colours.TEXT_CYAN)
|
|
624
661
|
header_label.append(server, style=f"{Colours.TEXT_INFO} bold")
|
|
625
662
|
render_header(header_label)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fast-agent-mcp
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Summary: Define, Prompt and Test MCP enabled Agents and Workflows
|
|
5
5
|
Author-email: Shaun Smith <fastagent@llmindset.co.uk>
|
|
6
6
|
License: Apache License
|
|
@@ -208,7 +208,7 @@ License-File: LICENSE
|
|
|
208
208
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
209
209
|
Classifier: Operating System :: OS Independent
|
|
210
210
|
Classifier: Programming Language :: Python :: 3
|
|
211
|
-
Requires-Python:
|
|
211
|
+
Requires-Python: <3.14,>=3.13.5
|
|
212
212
|
Requires-Dist: a2a-sdk>=0.3.6
|
|
213
213
|
Requires-Dist: aiohttp>=3.11.13
|
|
214
214
|
Requires-Dist: anthropic>=0.69.0
|
|
@@ -249,15 +249,24 @@ Description-Content-Type: text/markdown
|
|
|
249
249
|
## Overview
|
|
250
250
|
|
|
251
251
|
> [!TIP]
|
|
252
|
-
>
|
|
252
|
+
> Please see : https://fast-agent.ai for latest documentation. There is also an LLMs.txt [here](https://fast-agent.ai/llms.txt)
|
|
253
253
|
|
|
254
|
-
**`fast-agent`** enables you to create and interact with sophisticated Agents and Workflows in minutes. It is the first framework with complete, end-to-end tested MCP Feature support including Sampling
|
|
254
|
+
**`fast-agent`** enables you to create and interact with sophisticated multimodal Agents and Workflows in minutes. It is the first framework with complete, end-to-end tested MCP Feature support including Sampling and Elicitations.
|
|
255
255
|
|
|
256
|
-

|
|
256
|
+
<!--  -->
|
|
257
257
|
|
|
258
258
|
The simple declarative syntax lets you concentrate on composing your Prompts and MCP Servers to [build effective agents](https://www.anthropic.com/research/building-effective-agents).
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
Model support is comprehensive with native support for Anthropic, OpenAI and Google providers as well as Azure, Ollama, Deepseek and dozens of others via TensorZero. Structured Outputs, PDF and Vision support is simple to use and well tested. Passthrough and Playback LLMs enable rapid development and test of Python glue-code for your applications.
|
|
261
|
+
|
|
262
|
+
<img width="800" alt="MCP Transport Diagnostics" src="https://github.com/user-attachments/assets/e26472de-58d9-4726-8bdd-01eb407414cf" />
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
`fast-agent` is the only tool that allows you to inspect Streamable HTTP Transport usage - a critical feature for ensuring reliable, compliant deployments. OAuth is supported with KeyRing storage for secrets. Use the `fast-agent auth` command to manage.
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
261
270
|
|
|
262
271
|
> [!IMPORTANT]
|
|
263
272
|
>
|
|
@@ -9,7 +9,7 @@ fast_agent/mcp_server_registry.py,sha256=TDCNpQIehsh1PK4y7AWp_rkQMcwYx7FaouUbK3k
|
|
|
9
9
|
fast_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
fast_agent/agents/__init__.py,sha256=WfgtR9MgmJUJI7rb-CUH_10s7308LjxYIYzJRIBZ_9Y,2644
|
|
11
11
|
fast_agent/agents/agent_types.py,sha256=xAFEFWIvOtSnTNDcqqfaAnBx_lFLhnS2_b4hm9xKMo8,1719
|
|
12
|
-
fast_agent/agents/llm_agent.py,sha256=
|
|
12
|
+
fast_agent/agents/llm_agent.py,sha256=1lO-DtgeaOoYshHxGKNUIViR_gFJjW6GudEWxRFdVU4,10697
|
|
13
13
|
fast_agent/agents/llm_decorator.py,sha256=OezFrnjLwcl3W-1K1sGPtTqD_zVp67wlBUb4SxTesKw,29702
|
|
14
14
|
fast_agent/agents/mcp_agent.py,sha256=O9CrjjUrkmbNYsXQevZy2b8EG4B0KdNSH3wtnbe2HI4,39279
|
|
15
15
|
fast_agent/agents/tool_agent.py,sha256=bR0V06zCjuPVtOpOQQzym2Mmyt6EsDzAodJmQOEMoFI,8883
|
|
@@ -80,7 +80,7 @@ fast_agent/llm/provider/anthropic/llm_anthropic.py,sha256=3QGmJitHKqmYUSH3l1c_lX
|
|
|
80
80
|
fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py,sha256=szHqhd5i-OclNWM7EHVT66kTiHJ5B0y1qd-JqgJIOO4,16529
|
|
81
81
|
fast_agent/llm/provider/bedrock/bedrock_utils.py,sha256=mqWCCeB1gUQSL2KRUMqpFjvHZ0ZTJugCsd5YOrx6U30,7750
|
|
82
82
|
fast_agent/llm/provider/bedrock/llm_bedrock.py,sha256=Yl3luBfgM4wNGuMpPN9yg8DGHr_ncfYg0EemvKi2Rxc,100199
|
|
83
|
-
fast_agent/llm/provider/google/google_converter.py,sha256=
|
|
83
|
+
fast_agent/llm/provider/google/google_converter.py,sha256=cGb2ty_uBgdATa0Nib_BzvEbWCpBf3Mwzu9pMxMziA8,17599
|
|
84
84
|
fast_agent/llm/provider/google/llm_google_native.py,sha256=pbCld68BFlN1vUQSHWQzIAbFPOBTnd8ZVBEjGmxVS_Q,19296
|
|
85
85
|
fast_agent/llm/provider/openai/llm_aliyun.py,sha256=ti7VHTpwl0AG3ytwBERpDzVtacvCfamKnl2bAnTE96s,1213
|
|
86
86
|
fast_agent/llm/provider/openai/llm_azure.py,sha256=dePpuOf7yD4-zQc1PEHm4yaVznrZe9RaIz7nxsbZhlU,6061
|
|
@@ -103,9 +103,9 @@ fast_agent/mcp/gen_client.py,sha256=Q0hhCVzC659GsvTLJIbhUBgGwsAJRL8b3ejTFokyjn4,
|
|
|
103
103
|
fast_agent/mcp/hf_auth.py,sha256=ndDvR7E9LCc5dBiMsStFXtvvX9lYrL-edCq_qJw4lDw,4476
|
|
104
104
|
fast_agent/mcp/interfaces.py,sha256=xCWONGXe4uQSmmBlMZRD3mflPegTJnz2caVNihEl3ok,2411
|
|
105
105
|
fast_agent/mcp/logger_textio.py,sha256=4YLVXlXghdGm1s_qp1VoAWEX_eWufBfD2iD7l08yoak,3170
|
|
106
|
-
fast_agent/mcp/mcp_agent_client_session.py,sha256=
|
|
106
|
+
fast_agent/mcp/mcp_agent_client_session.py,sha256=VTPEjNjPMrxZopeCr7bXjlvr_2MTzdqEjjBOeQri04g,16050
|
|
107
107
|
fast_agent/mcp/mcp_aggregator.py,sha256=HzNyKuUelAlp5JBkjMAaOB2JvdMgpm0ItEPGjidI9l0,67198
|
|
108
|
-
fast_agent/mcp/mcp_connection_manager.py,sha256=
|
|
108
|
+
fast_agent/mcp/mcp_connection_manager.py,sha256=g19Y-gNH6wmMtFcN9awgQ9yRGmqWa1wi9CpaV80pdfk,25367
|
|
109
109
|
fast_agent/mcp/mcp_content.py,sha256=F9bgJ57EO9sgWg1m-eTNM6xd9js79mHKf4e9O8K8jrI,8829
|
|
110
110
|
fast_agent/mcp/mime_utils.py,sha256=D6YXNdZJ351BjacSW5o0sVF_hrWuRHD6UyWS4TDlLZI,2915
|
|
111
111
|
fast_agent/mcp/oauth_client.py,sha256=3shN3iwsJNXrk7nbcfUgrzNos3i2RuMuLXA80nR8r6Y,17104
|
|
@@ -191,21 +191,21 @@ fast_agent/types/__init__.py,sha256=y-53m-C4drf4Rx8Bbnk_GAhko9LdNYCyRUWya8e0mos,
|
|
|
191
191
|
fast_agent/types/llm_stop_reason.py,sha256=bWe97OfhALUe8uQeAQOnTdPlYzJiabIfo8u38kPgj3Q,2293
|
|
192
192
|
fast_agent/ui/__init__.py,sha256=MXxTQjFdF7mI_3JHxBPd-aoZYLlxV_-51-Trqgv5-3w,1104
|
|
193
193
|
fast_agent/ui/console.py,sha256=Gjf2QLFumwG1Lav__c07X_kZxxEUSkzV-1_-YbAwcwo,813
|
|
194
|
-
fast_agent/ui/console_display.py,sha256=
|
|
194
|
+
fast_agent/ui/console_display.py,sha256=brDhUR-VoQWV3-YWrmvWS1q9zYFL3eJkO1Uxs_1AwA4,44017
|
|
195
195
|
fast_agent/ui/elicitation_form.py,sha256=t3UhBG44YmxTLu1RjCnHwW36eQQaroE45CiBGJB2czg,29410
|
|
196
196
|
fast_agent/ui/elicitation_style.py,sha256=-WqXgVjVs65oNwhCDw3E0A9cCyw95IOe6LYCJgjT6ok,3939
|
|
197
197
|
fast_agent/ui/enhanced_prompt.py,sha256=TPJmC1zKQaiqDbtlI0WcxUoH5uw1sY23uJ8uEeYiMCY,45018
|
|
198
198
|
fast_agent/ui/history_display.py,sha256=b7l-pXohSnn1YK1g-8BUmY479x-d-wf5sG2pItG2_ps,19024
|
|
199
199
|
fast_agent/ui/interactive_prompt.py,sha256=KI2jSO4roWNivfOeyibiu44J9pu5RU6PiB7Fd59iCYw,46699
|
|
200
|
-
fast_agent/ui/mcp_display.py,sha256=
|
|
200
|
+
fast_agent/ui/mcp_display.py,sha256=piDn0F98yqvlHx1cxuKqTfefpvZkGiGK2QHujJltMtA,28422
|
|
201
201
|
fast_agent/ui/mcp_ui_utils.py,sha256=hV7z-yHX86BgdH6CMmN5qyOUjyiegQXLJOa5n5A1vQs,8476
|
|
202
202
|
fast_agent/ui/mermaid_utils.py,sha256=MpcRyVCPMTwU1XeIxnyFg0fQLjcyXZduWRF8NhEqvXE,5332
|
|
203
203
|
fast_agent/ui/notification_tracker.py,sha256=-hiBwR47SdnwhvrGIXcgsVaqMlMudmrKAf9Xi_E5Eok,5850
|
|
204
204
|
fast_agent/ui/progress_display.py,sha256=hajDob65PttiJ2mPS6FsCtnmTcnyvDWGn-UqQboXqkQ,361
|
|
205
205
|
fast_agent/ui/rich_progress.py,sha256=4n5NmsRQTT1GX18faP43yLPhB_gZJqJeWX6-j7g1_zI,7731
|
|
206
206
|
fast_agent/ui/usage_display.py,sha256=ltJpn_sDzo8PDNSXWx-QdEUbQWUnhmajCItNt5mA5rM,7285
|
|
207
|
-
fast_agent_mcp-0.3.
|
|
208
|
-
fast_agent_mcp-0.3.
|
|
209
|
-
fast_agent_mcp-0.3.
|
|
210
|
-
fast_agent_mcp-0.3.
|
|
211
|
-
fast_agent_mcp-0.3.
|
|
207
|
+
fast_agent_mcp-0.3.13.dist-info/METADATA,sha256=BJ3SgQEi3XsnCjuK6aiPTtvGKq6khx_w8HD5NAE0gX8,32003
|
|
208
|
+
fast_agent_mcp-0.3.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
209
|
+
fast_agent_mcp-0.3.13.dist-info/entry_points.txt,sha256=i6Ujja9J-hRxttOKqTYdbYP_tyaS4gLHg53vupoCSsg,199
|
|
210
|
+
fast_agent_mcp-0.3.13.dist-info/licenses/LICENSE,sha256=Gx1L3axA4PnuK4FxsbX87jQ1opoOkSFfHHSytW6wLUU,10935
|
|
211
|
+
fast_agent_mcp-0.3.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|