fast-agent-mcp 0.3.3__py3-none-any.whl → 0.3.5__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/__init__.py +6 -3
- fast_agent/agents/__init__.py +63 -14
- fast_agent/cli/__main__.py +8 -5
- fast_agent/cli/commands/auth.py +370 -0
- fast_agent/cli/commands/check_config.py +54 -3
- fast_agent/cli/commands/go.py +1 -1
- fast_agent/cli/commands/quickstart.py +3 -1
- fast_agent/cli/commands/server_helpers.py +10 -2
- fast_agent/cli/commands/setup.py +3 -2
- fast_agent/cli/constants.py +1 -1
- fast_agent/cli/main.py +3 -1
- fast_agent/config.py +63 -8
- fast_agent/core/__init__.py +38 -37
- fast_agent/core/direct_factory.py +1 -1
- fast_agent/mcp/mcp_connection_manager.py +21 -3
- fast_agent/mcp/oauth_client.py +481 -0
- fast_agent/mcp/ui_agent.py +1 -1
- fast_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
- fast_agent/resources/examples/data-analysis/analysis.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/forms_demo.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/game_character.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/tool_call.py +1 -1
- fast_agent/resources/examples/mcp/state-transfer/agent_one.py +1 -1
- fast_agent/resources/examples/mcp/state-transfer/agent_two.py +1 -1
- fast_agent/resources/examples/researcher/researcher-eval.py +1 -1
- fast_agent/resources/examples/researcher/researcher-imp.py +1 -1
- fast_agent/resources/examples/researcher/researcher.py +1 -1
- fast_agent/resources/examples/tensorzero/agent.py +1 -1
- fast_agent/resources/examples/tensorzero/image_demo.py +1 -1
- fast_agent/resources/examples/tensorzero/simple_agent.py +1 -1
- fast_agent/resources/examples/workflows/chaining.py +1 -1
- fast_agent/resources/examples/workflows/evaluator.py +1 -1
- fast_agent/resources/examples/workflows/human_input.py +1 -1
- fast_agent/resources/examples/workflows/orchestrator.py +1 -1
- fast_agent/resources/examples/workflows/parallel.py +1 -1
- fast_agent/resources/examples/workflows/router.py +1 -1
- fast_agent/resources/setup/agent.py +1 -1
- fast_agent/resources/setup/fastagent.config.yaml +2 -2
- fast_agent/ui/mcp_ui_utils.py +12 -1
- {fast_agent_mcp-0.3.3.dist-info → fast_agent_mcp-0.3.5.dist-info}/METADATA +40 -3
- {fast_agent_mcp-0.3.3.dist-info → fast_agent_mcp-0.3.5.dist-info}/RECORD +44 -42
- {fast_agent_mcp-0.3.3.dist-info → fast_agent_mcp-0.3.5.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.3.dist-info → fast_agent_mcp-0.3.5.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.3.dist-info → fast_agent_mcp-0.3.5.dist-info}/licenses/LICENSE +0 -0
fast_agent/cli/commands/setup.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
|
-
from rich.console import Console
|
|
5
4
|
from rich.prompt import Confirm
|
|
6
5
|
|
|
6
|
+
from fast_agent.ui.console import console as shared_console
|
|
7
|
+
|
|
7
8
|
app = typer.Typer()
|
|
8
|
-
console =
|
|
9
|
+
console = shared_console
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def load_template_text(filename: str) -> str:
|
fast_agent/cli/constants.py
CHANGED
|
@@ -22,4 +22,4 @@ GO_SPECIFIC_OPTIONS = {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
# Known subcommands that should not trigger auto-routing
|
|
25
|
-
KNOWN_SUBCOMMANDS = {"go", "setup", "check", "bootstrap", "quickstart", "--help", "-h", "--version"}
|
|
25
|
+
KNOWN_SUBCOMMANDS = {"go", "setup", "check", "auth", "bootstrap", "quickstart", "--help", "-h", "--version"}
|
fast_agent/cli/main.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import typer
|
|
4
4
|
from rich.table import Table
|
|
5
5
|
|
|
6
|
-
from fast_agent.cli.commands import check_config, go, quickstart, setup
|
|
6
|
+
from fast_agent.cli.commands import auth, check_config, go, quickstart, setup
|
|
7
7
|
from fast_agent.cli.terminal import Application
|
|
8
8
|
from fast_agent.ui.console import console as shared_console
|
|
9
9
|
|
|
@@ -16,6 +16,7 @@ app = typer.Typer(
|
|
|
16
16
|
app.add_typer(go.app, name="go", help="Run an interactive agent directly from the command line")
|
|
17
17
|
app.add_typer(setup.app, name="setup", help="Set up a new agent project")
|
|
18
18
|
app.add_typer(check_config.app, name="check", help="Show or diagnose fast-agent configuration")
|
|
19
|
+
app.add_typer(auth.app, name="auth", help="Manage OAuth authentication for MCP servers")
|
|
19
20
|
app.add_typer(quickstart.app, name="bootstrap", help="Create example applications")
|
|
20
21
|
app.add_typer(quickstart.app, name="quickstart", help="Create example applications")
|
|
21
22
|
|
|
@@ -62,6 +63,7 @@ def show_welcome() -> None:
|
|
|
62
63
|
|
|
63
64
|
table.add_row("[bold]go[/bold]", "Start an interactive session")
|
|
64
65
|
table.add_row("check", "Show current configuration")
|
|
66
|
+
table.add_row("auth", "Manage OAuth tokens and keyring")
|
|
65
67
|
table.add_row("setup", "Create agent template and configuration")
|
|
66
68
|
table.add_row("quickstart", "Create example applications (workflow, researcher, etc.)")
|
|
67
69
|
|
fast_agent/config.py
CHANGED
|
@@ -9,14 +9,29 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, Dict, List, Literal, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
from mcp import Implementation
|
|
12
|
-
from pydantic import BaseModel, ConfigDict, field_validator
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
|
13
13
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class MCPServerAuthSettings(BaseModel):
|
|
17
|
-
"""Represents authentication configuration for a server.
|
|
17
|
+
"""Represents authentication configuration for a server.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Minimal OAuth v2.1 support with sensible defaults.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# Enable OAuth for SSE/HTTP transports. If None is provided for the auth block,
|
|
23
|
+
# the system will assume OAuth is enabled by default.
|
|
24
|
+
oauth: bool = True
|
|
25
|
+
|
|
26
|
+
# Local callback server configuration
|
|
27
|
+
redirect_port: int = 3030
|
|
28
|
+
redirect_path: str = "/callback"
|
|
29
|
+
|
|
30
|
+
# Optional scope override. If set to a list, values are space-joined.
|
|
31
|
+
scope: str | list[str] | None = None
|
|
32
|
+
|
|
33
|
+
# Token persistence: use OS keychain via 'keyring' by default; fallback to 'memory'.
|
|
34
|
+
persist: Literal["keyring", "memory"] = "keyring"
|
|
20
35
|
|
|
21
36
|
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
22
37
|
|
|
@@ -109,6 +124,42 @@ class MCPServerSettings(BaseModel):
|
|
|
109
124
|
|
|
110
125
|
implementation: Implementation | None = None
|
|
111
126
|
|
|
127
|
+
@model_validator(mode="before")
|
|
128
|
+
@classmethod
|
|
129
|
+
def validate_transport_inference(cls, values):
|
|
130
|
+
"""Automatically infer transport type based on url/command presence."""
|
|
131
|
+
import warnings
|
|
132
|
+
|
|
133
|
+
if isinstance(values, dict):
|
|
134
|
+
# Check if transport was explicitly provided in the input
|
|
135
|
+
transport_explicit = "transport" in values
|
|
136
|
+
url = values.get("url")
|
|
137
|
+
command = values.get("command")
|
|
138
|
+
|
|
139
|
+
# Only infer if transport was not explicitly set
|
|
140
|
+
if not transport_explicit:
|
|
141
|
+
# Check if we have both url and command specified
|
|
142
|
+
has_url = url is not None and str(url).strip()
|
|
143
|
+
has_command = command is not None and str(command).strip()
|
|
144
|
+
|
|
145
|
+
if has_url and has_command:
|
|
146
|
+
warnings.warn(
|
|
147
|
+
f"MCP Server config has both 'url' ({url}) and 'command' ({command}) specified. "
|
|
148
|
+
"Preferring HTTP transport and ignoring command.",
|
|
149
|
+
UserWarning,
|
|
150
|
+
stacklevel=4,
|
|
151
|
+
)
|
|
152
|
+
values["transport"] = "http"
|
|
153
|
+
values["command"] = None # Clear command to avoid confusion
|
|
154
|
+
elif has_url and not has_command:
|
|
155
|
+
values["transport"] = "http"
|
|
156
|
+
elif has_command and not has_url:
|
|
157
|
+
# Keep default "stdio" for command-based servers
|
|
158
|
+
values["transport"] = "stdio"
|
|
159
|
+
# If neither url nor command is specified, keep default "stdio"
|
|
160
|
+
|
|
161
|
+
return values
|
|
162
|
+
|
|
112
163
|
|
|
113
164
|
class MCPSettings(BaseModel):
|
|
114
165
|
"""Configuration for all MCP servers."""
|
|
@@ -260,8 +311,8 @@ class TensorZeroSettings(BaseModel):
|
|
|
260
311
|
Settings for using TensorZero via its OpenAI-compatible API.
|
|
261
312
|
"""
|
|
262
313
|
|
|
263
|
-
base_url:
|
|
264
|
-
api_key:
|
|
314
|
+
base_url: str | None = None
|
|
315
|
+
api_key: str | None = None
|
|
265
316
|
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
266
317
|
|
|
267
318
|
|
|
@@ -287,7 +338,7 @@ class HuggingFaceSettings(BaseModel):
|
|
|
287
338
|
Settings for HuggingFace authentication (used for MCP connections).
|
|
288
339
|
"""
|
|
289
340
|
|
|
290
|
-
api_key:
|
|
341
|
+
api_key: str | None = None
|
|
291
342
|
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
292
343
|
|
|
293
344
|
|
|
@@ -408,7 +459,7 @@ class Settings(BaseSettings):
|
|
|
408
459
|
execution_engine: Literal["asyncio"] = "asyncio"
|
|
409
460
|
"""Execution engine for the fast-agent application"""
|
|
410
461
|
|
|
411
|
-
default_model: str | None = "
|
|
462
|
+
default_model: str | None = "gpt-5-mini.low"
|
|
412
463
|
"""
|
|
413
464
|
Default model for agents. Format is provider.model_name.<reasoning_effort>, for example openai.o3-mini.low
|
|
414
465
|
Aliases are provided for common models e.g. sonnet, haiku, gpt-4.1, o3-mini etc.
|
|
@@ -459,7 +510,7 @@ class Settings(BaseSettings):
|
|
|
459
510
|
groq: GroqSettings | None = None
|
|
460
511
|
"""Settings for using the Groq provider in the fast-agent application"""
|
|
461
512
|
|
|
462
|
-
logger: LoggerSettings
|
|
513
|
+
logger: LoggerSettings = LoggerSettings()
|
|
463
514
|
"""Logger settings for the fast-agent application"""
|
|
464
515
|
|
|
465
516
|
# MCP UI integration mode for handling ui:// embedded resources from MCP tool results
|
|
@@ -470,6 +521,10 @@ class Settings(BaseSettings):
|
|
|
470
521
|
- "auto": Extract and automatically open ui:// resources.
|
|
471
522
|
"""
|
|
472
523
|
|
|
524
|
+
# Output directory for MCP-UI generated HTML files (relative to CWD if not absolute)
|
|
525
|
+
mcp_ui_output_dir: str = ".fast-agent/ui"
|
|
526
|
+
"""Directory where MCP-UI HTML files are written. Relative paths are resolved from CWD."""
|
|
527
|
+
|
|
473
528
|
@classmethod
|
|
474
529
|
def find_config(cls) -> Path | None:
|
|
475
530
|
"""Find the config file in the current directory or parent directories."""
|
fast_agent/core/__init__.py
CHANGED
|
@@ -2,45 +2,32 @@
|
|
|
2
2
|
Core interfaces and decorators for fast-agent.
|
|
3
3
|
|
|
4
4
|
Public API:
|
|
5
|
-
- `Core`: The core application container
|
|
6
|
-
- `
|
|
5
|
+
- `Core`: The core application container
|
|
6
|
+
- `AgentApp`: Container for interacting with agents
|
|
7
|
+
- `FastAgent`: High-level, decorator-driven application class
|
|
7
8
|
- Decorators: `agent`, `custom`, `orchestrator`, `iterative_planner`,
|
|
8
|
-
`router`, `chain`, `parallel`, `evaluator_optimizer`
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from typing import TYPE_CHECKING as _TYPE_CHECKING
|
|
9
|
+
`router`, `chain`, `parallel`, `evaluator_optimizer`
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Exports are resolved lazily to avoid circular imports during package init.
|
|
12
|
+
"""
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
"Core",
|
|
17
|
-
"AgentApp",
|
|
18
|
-
"FastAgent",
|
|
19
|
-
# Decorators
|
|
20
|
-
"agent",
|
|
21
|
-
"custom",
|
|
22
|
-
"orchestrator",
|
|
23
|
-
"iterative_planner",
|
|
24
|
-
"router",
|
|
25
|
-
"chain",
|
|
26
|
-
"parallel",
|
|
27
|
-
"evaluator_optimizer",
|
|
28
|
-
]
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
29
15
|
|
|
30
16
|
|
|
31
17
|
def __getattr__(name: str):
|
|
32
|
-
# Lazy imports to avoid heavy dependencies and circular imports at init time
|
|
33
|
-
if name == "FastAgent":
|
|
34
|
-
from .fastagent import FastAgent
|
|
35
|
-
|
|
36
|
-
return FastAgent
|
|
37
18
|
if name == "AgentApp":
|
|
38
19
|
from .agent_app import AgentApp
|
|
39
20
|
|
|
40
21
|
return AgentApp
|
|
22
|
+
elif name == "Core":
|
|
23
|
+
from .core_app import Core
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
25
|
+
return Core
|
|
26
|
+
elif name == "FastAgent":
|
|
27
|
+
from .fastagent import FastAgent
|
|
28
|
+
|
|
29
|
+
return FastAgent
|
|
30
|
+
elif name in (
|
|
44
31
|
"agent",
|
|
45
32
|
"custom",
|
|
46
33
|
"orchestrator",
|
|
@@ -49,20 +36,22 @@ def __getattr__(name: str):
|
|
|
49
36
|
"chain",
|
|
50
37
|
"parallel",
|
|
51
38
|
"evaluator_optimizer",
|
|
52
|
-
|
|
39
|
+
):
|
|
53
40
|
from . import direct_decorators as _dd
|
|
54
41
|
|
|
55
|
-
return getattr(
|
|
56
|
-
|
|
42
|
+
return getattr(
|
|
43
|
+
_dd,
|
|
44
|
+
name,
|
|
45
|
+
)
|
|
57
46
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
58
47
|
|
|
59
48
|
|
|
60
|
-
#
|
|
61
|
-
if _TYPE_CHECKING: # pragma: no cover - typing aid only
|
|
49
|
+
if TYPE_CHECKING: # pragma: no cover - typing aid only
|
|
62
50
|
from .agent_app import AgentApp as AgentApp # noqa: F401
|
|
63
|
-
from .
|
|
51
|
+
from .core_app import Core as Core # noqa: F401
|
|
52
|
+
from .direct_decorators import ( # noqa: F401
|
|
64
53
|
agent as agent,
|
|
65
|
-
)
|
|
54
|
+
)
|
|
66
55
|
from .direct_decorators import (
|
|
67
56
|
chain as chain,
|
|
68
57
|
)
|
|
@@ -87,5 +76,17 @@ if _TYPE_CHECKING: # pragma: no cover - typing aid only
|
|
|
87
76
|
from .fastagent import FastAgent as FastAgent # noqa: F401
|
|
88
77
|
|
|
89
78
|
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
__all__ = [
|
|
80
|
+
"Core",
|
|
81
|
+
"AgentApp",
|
|
82
|
+
"FastAgent",
|
|
83
|
+
# Decorators
|
|
84
|
+
"agent",
|
|
85
|
+
"custom",
|
|
86
|
+
"orchestrator",
|
|
87
|
+
"iterative_planner",
|
|
88
|
+
"router",
|
|
89
|
+
"chain",
|
|
90
|
+
"parallel",
|
|
91
|
+
"evaluator_optimizer",
|
|
92
|
+
]
|
|
@@ -5,9 +5,9 @@ Implements type-safe factories with improved error handling.
|
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Optional, Protocol, TypeVar
|
|
7
7
|
|
|
8
|
+
from fast_agent.agents import McpAgent
|
|
8
9
|
from fast_agent.agents.agent_types import AgentConfig, AgentType
|
|
9
10
|
from fast_agent.agents.llm_agent import LlmAgent
|
|
10
|
-
from fast_agent.agents.mcp_agent import McpAgent
|
|
11
11
|
from fast_agent.agents.workflow.evaluator_optimizer import (
|
|
12
12
|
EvaluatorOptimizerAgent,
|
|
13
13
|
QualityRating,
|
|
@@ -33,6 +33,7 @@ from fast_agent.core.logging.logger import get_logger
|
|
|
33
33
|
from fast_agent.event_progress import ProgressAction
|
|
34
34
|
from fast_agent.mcp.logger_textio import get_stderr_handler
|
|
35
35
|
from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
|
|
36
|
+
from fast_agent.mcp.oauth_client import build_oauth_provider
|
|
36
37
|
|
|
37
38
|
if TYPE_CHECKING:
|
|
38
39
|
from fast_agent.context import Context
|
|
@@ -341,6 +342,8 @@ class MCPConnectionManager(ContextDependent):
|
|
|
341
342
|
|
|
342
343
|
def transport_context_factory():
|
|
343
344
|
if config.transport == "stdio":
|
|
345
|
+
if not config.command:
|
|
346
|
+
raise ValueError(f"Server '{server_name}' uses stdio transport but no command is specified")
|
|
344
347
|
server_params = StdioServerParameters(
|
|
345
348
|
command=config.command,
|
|
346
349
|
args=config.args if config.args is not None else [],
|
|
@@ -353,18 +356,33 @@ class MCPConnectionManager(ContextDependent):
|
|
|
353
356
|
logger.debug(f"{server_name}: Creating stdio client with custom error handler")
|
|
354
357
|
return _add_none_to_context(stdio_client(server_params, errlog=error_handler))
|
|
355
358
|
elif config.transport == "sse":
|
|
359
|
+
if not config.url:
|
|
360
|
+
raise ValueError(f"Server '{server_name}' uses sse transport but no url is specified")
|
|
356
361
|
# Suppress MCP library error spam
|
|
357
362
|
self._suppress_mcp_sse_errors()
|
|
358
|
-
|
|
363
|
+
oauth_auth = build_oauth_provider(config)
|
|
364
|
+
# If using OAuth, strip any pre-existing Authorization headers to avoid conflicts
|
|
365
|
+
headers = dict(config.headers or {})
|
|
366
|
+
if oauth_auth is not None:
|
|
367
|
+
headers.pop("Authorization", None)
|
|
368
|
+
headers.pop("X-HF-Authorization", None)
|
|
359
369
|
return _add_none_to_context(
|
|
360
370
|
sse_client(
|
|
361
371
|
config.url,
|
|
362
|
-
|
|
372
|
+
headers,
|
|
363
373
|
sse_read_timeout=config.read_transport_sse_timeout_seconds,
|
|
374
|
+
auth=oauth_auth,
|
|
364
375
|
)
|
|
365
376
|
)
|
|
366
377
|
elif config.transport == "http":
|
|
367
|
-
|
|
378
|
+
if not config.url:
|
|
379
|
+
raise ValueError(f"Server '{server_name}' uses http transport but no url is specified")
|
|
380
|
+
oauth_auth = build_oauth_provider(config)
|
|
381
|
+
headers = dict(config.headers or {})
|
|
382
|
+
if oauth_auth is not None:
|
|
383
|
+
headers.pop("Authorization", None)
|
|
384
|
+
headers.pop("X-HF-Authorization", None)
|
|
385
|
+
return streamablehttp_client(config.url, headers, auth=oauth_auth)
|
|
368
386
|
else:
|
|
369
387
|
raise ValueError(f"Unsupported transport: {config.transport}")
|
|
370
388
|
|