fast-agent-mcp 0.3.17__py3-none-any.whl → 0.3.18__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/cli/commands/go.py +78 -36
- fast_agent/cli/commands/serve.py +136 -0
- fast_agent/cli/constants.py +1 -0
- fast_agent/cli/main.py +4 -2
- fast_agent/core/fastagent.py +102 -16
- fast_agent/mcp/server/agent_server.py +175 -41
- {fast_agent_mcp-0.3.17.dist-info → fast_agent_mcp-0.3.18.dist-info}/METADATA +9 -1
- {fast_agent_mcp-0.3.17.dist-info → fast_agent_mcp-0.3.18.dist-info}/RECORD +11 -10
- {fast_agent_mcp-0.3.17.dist-info → fast_agent_mcp-0.3.18.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.17.dist-info → fast_agent_mcp-0.3.18.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.17.dist-info → fast_agent_mcp-0.3.18.dist-info}/licenses/LICENSE +0 -0
fast_agent/cli/commands/go.py
CHANGED
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import shlex
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
from typing import Literal
|
|
8
9
|
|
|
9
10
|
import typer
|
|
10
11
|
|
|
@@ -23,6 +24,50 @@ app = typer.Typer(
|
|
|
23
24
|
default_instruction = DEFAULT_AGENT_INSTRUCTION
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def resolve_instruction_option(instruction: str | None) -> tuple[str, str]:
|
|
28
|
+
"""
|
|
29
|
+
Resolve the instruction option (file or URL) to the instruction string and agent name.
|
|
30
|
+
Returns (resolved_instruction, agent_name).
|
|
31
|
+
"""
|
|
32
|
+
resolved_instruction = default_instruction
|
|
33
|
+
agent_name = "agent"
|
|
34
|
+
|
|
35
|
+
if instruction:
|
|
36
|
+
try:
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
from pydantic import AnyUrl
|
|
40
|
+
|
|
41
|
+
from fast_agent.core.direct_decorators import _resolve_instruction
|
|
42
|
+
|
|
43
|
+
if instruction.startswith(("http://", "https://")):
|
|
44
|
+
resolved_instruction = _resolve_instruction(AnyUrl(instruction))
|
|
45
|
+
else:
|
|
46
|
+
resolved_instruction = _resolve_instruction(Path(instruction))
|
|
47
|
+
instruction_path = Path(instruction)
|
|
48
|
+
if instruction_path.exists() and instruction_path.is_file():
|
|
49
|
+
agent_name = instruction_path.stem
|
|
50
|
+
except Exception as e:
|
|
51
|
+
typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
return resolved_instruction, agent_name
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def collect_stdio_commands(npx: str | None, uvx: str | None, stdio: str | None) -> list[str]:
|
|
58
|
+
"""Collect STDIO command definitions from convenience options."""
|
|
59
|
+
stdio_commands: list[str] = []
|
|
60
|
+
|
|
61
|
+
if npx:
|
|
62
|
+
stdio_commands.append(f"npx {npx}")
|
|
63
|
+
if uvx:
|
|
64
|
+
stdio_commands.append(f"uvx {uvx}")
|
|
65
|
+
if stdio:
|
|
66
|
+
stdio_commands.append(stdio)
|
|
67
|
+
|
|
68
|
+
return stdio_commands
|
|
69
|
+
|
|
70
|
+
|
|
26
71
|
def _set_asyncio_exception_handler(loop: asyncio.AbstractEventLoop) -> None:
|
|
27
72
|
"""Attach a detailed exception handler to the provided event loop."""
|
|
28
73
|
|
|
@@ -72,6 +117,12 @@ async def _run_agent(
|
|
|
72
117
|
agent_name: str | None = "agent",
|
|
73
118
|
skills_directory: Path | None = None,
|
|
74
119
|
shell_runtime: bool = False,
|
|
120
|
+
mode: Literal["interactive", "serve"] = "interactive",
|
|
121
|
+
transport: str = "http",
|
|
122
|
+
host: str = "0.0.0.0",
|
|
123
|
+
port: int = 8000,
|
|
124
|
+
tool_description: str | None = None,
|
|
125
|
+
instance_scope: str = "shared",
|
|
75
126
|
) -> None:
|
|
76
127
|
"""Async implementation to run an interactive agent."""
|
|
77
128
|
from fast_agent.mcp.prompts.prompt_load import load_prompt
|
|
@@ -84,6 +135,8 @@ async def _run_agent(
|
|
|
84
135
|
"ignore_unknown_args": True,
|
|
85
136
|
"parse_cli_args": False, # Don't parse CLI args, we're handling it ourselves
|
|
86
137
|
}
|
|
138
|
+
if mode == "serve":
|
|
139
|
+
fast_kwargs["quiet"] = True
|
|
87
140
|
if skills_directory is not None:
|
|
88
141
|
fast_kwargs["skills_directory"] = skills_directory
|
|
89
142
|
|
|
@@ -183,7 +236,16 @@ async def _run_agent(
|
|
|
183
236
|
await agent.interactive()
|
|
184
237
|
|
|
185
238
|
# Run the agent
|
|
186
|
-
|
|
239
|
+
if mode == "serve":
|
|
240
|
+
await fast.start_server(
|
|
241
|
+
transport=transport,
|
|
242
|
+
host=host,
|
|
243
|
+
port=port,
|
|
244
|
+
tool_description=tool_description,
|
|
245
|
+
instance_scope=instance_scope,
|
|
246
|
+
)
|
|
247
|
+
else:
|
|
248
|
+
await cli_agent()
|
|
187
249
|
|
|
188
250
|
|
|
189
251
|
def run_async_agent(
|
|
@@ -200,6 +262,12 @@ def run_async_agent(
|
|
|
200
262
|
agent_name: str | None = None,
|
|
201
263
|
skills_directory: Path | None = None,
|
|
202
264
|
shell_enabled: bool = False,
|
|
265
|
+
mode: Literal["interactive", "serve"] = "interactive",
|
|
266
|
+
transport: str = "http",
|
|
267
|
+
host: str = "0.0.0.0",
|
|
268
|
+
port: int = 8000,
|
|
269
|
+
tool_description: str | None = None,
|
|
270
|
+
instance_scope: str = "shared",
|
|
203
271
|
):
|
|
204
272
|
"""Run the async agent function with proper loop handling."""
|
|
205
273
|
server_list = servers.split(",") if servers else None
|
|
@@ -304,6 +372,12 @@ def run_async_agent(
|
|
|
304
372
|
agent_name=agent_name,
|
|
305
373
|
skills_directory=skills_directory,
|
|
306
374
|
shell_runtime=shell_enabled,
|
|
375
|
+
mode=mode,
|
|
376
|
+
transport=transport,
|
|
377
|
+
host=host,
|
|
378
|
+
port=port,
|
|
379
|
+
tool_description=tool_description,
|
|
380
|
+
instance_scope=instance_scope,
|
|
307
381
|
)
|
|
308
382
|
)
|
|
309
383
|
finally:
|
|
@@ -405,46 +479,13 @@ def go(
|
|
|
405
479
|
--stdio Command to run as STDIO MCP server (quoted)
|
|
406
480
|
"""
|
|
407
481
|
# Collect all stdio commands from convenience options
|
|
408
|
-
stdio_commands =
|
|
482
|
+
stdio_commands = collect_stdio_commands(npx, uvx, stdio)
|
|
409
483
|
shell_enabled = shell
|
|
410
484
|
|
|
411
|
-
if npx:
|
|
412
|
-
stdio_commands.append(f"npx {npx}")
|
|
413
|
-
|
|
414
|
-
if uvx:
|
|
415
|
-
stdio_commands.append(f"uvx {uvx}")
|
|
416
|
-
|
|
417
|
-
if stdio:
|
|
418
|
-
stdio_commands.append(stdio)
|
|
419
|
-
|
|
420
485
|
# When shell is enabled we don't add an MCP stdio server; handled inside the agent
|
|
421
486
|
|
|
422
487
|
# Resolve instruction from file/URL or use default
|
|
423
|
-
resolved_instruction =
|
|
424
|
-
agent_name = "agent"
|
|
425
|
-
|
|
426
|
-
if instruction:
|
|
427
|
-
try:
|
|
428
|
-
from pathlib import Path
|
|
429
|
-
|
|
430
|
-
from pydantic import AnyUrl
|
|
431
|
-
|
|
432
|
-
from fast_agent.core.direct_decorators import _resolve_instruction
|
|
433
|
-
|
|
434
|
-
# Check if it's a URL
|
|
435
|
-
if instruction.startswith(("http://", "https://")):
|
|
436
|
-
resolved_instruction = _resolve_instruction(AnyUrl(instruction))
|
|
437
|
-
else:
|
|
438
|
-
# Treat as file path
|
|
439
|
-
resolved_instruction = _resolve_instruction(Path(instruction))
|
|
440
|
-
# Extract filename without extension to use as agent name
|
|
441
|
-
instruction_path = Path(instruction)
|
|
442
|
-
if instruction_path.exists() and instruction_path.is_file():
|
|
443
|
-
# Get filename without extension
|
|
444
|
-
agent_name = instruction_path.stem
|
|
445
|
-
except Exception as e:
|
|
446
|
-
typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
|
|
447
|
-
raise typer.Exit(1)
|
|
488
|
+
resolved_instruction, agent_name = resolve_instruction_option(instruction)
|
|
448
489
|
|
|
449
490
|
run_async_agent(
|
|
450
491
|
name=name,
|
|
@@ -460,4 +501,5 @@ def go(
|
|
|
460
501
|
agent_name=agent_name,
|
|
461
502
|
skills_directory=skills_dir,
|
|
462
503
|
shell_enabled=shell_enabled,
|
|
504
|
+
instance_scope="shared",
|
|
463
505
|
)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Run FastAgent as an MCP server from the command line."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from fast_agent.cli.commands.go import (
|
|
9
|
+
collect_stdio_commands,
|
|
10
|
+
resolve_instruction_option,
|
|
11
|
+
run_async_agent,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ServeTransport(str, Enum):
|
|
16
|
+
HTTP = "http"
|
|
17
|
+
SSE = "sse"
|
|
18
|
+
STDIO = "stdio"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InstanceScope(str, Enum):
|
|
22
|
+
SHARED = "shared"
|
|
23
|
+
CONNECTION = "connection"
|
|
24
|
+
REQUEST = "request"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
app = typer.Typer(
|
|
28
|
+
help="Run FastAgent as an MCP server without writing an agent.py file",
|
|
29
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.callback(invoke_without_command=True, no_args_is_help=False)
|
|
34
|
+
def serve(
|
|
35
|
+
ctx: typer.Context,
|
|
36
|
+
name: str = typer.Option("fast-agent", "--name", help="Name for the MCP server"),
|
|
37
|
+
instruction: str | None = typer.Option(
|
|
38
|
+
None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
|
|
39
|
+
),
|
|
40
|
+
config_path: str | None = typer.Option(None, "--config-path", "-c", help="Path to config file"),
|
|
41
|
+
servers: str | None = typer.Option(
|
|
42
|
+
None, "--servers", help="Comma-separated list of server names to enable from config"
|
|
43
|
+
),
|
|
44
|
+
urls: str | None = typer.Option(
|
|
45
|
+
None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
|
|
46
|
+
),
|
|
47
|
+
auth: str | None = typer.Option(
|
|
48
|
+
None, "--auth", help="Bearer token for authorization with URL-based servers"
|
|
49
|
+
),
|
|
50
|
+
model: str | None = typer.Option(
|
|
51
|
+
None, "--model", "--models", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
|
|
52
|
+
),
|
|
53
|
+
skills_dir: Path | None = typer.Option(
|
|
54
|
+
None,
|
|
55
|
+
"--skills-dir",
|
|
56
|
+
"--skills",
|
|
57
|
+
help="Override the default skills directory",
|
|
58
|
+
),
|
|
59
|
+
npx: str | None = typer.Option(
|
|
60
|
+
None, "--npx", help="NPX package and args to run as MCP server (quoted)"
|
|
61
|
+
),
|
|
62
|
+
uvx: str | None = typer.Option(
|
|
63
|
+
None, "--uvx", help="UVX package and args to run as MCP server (quoted)"
|
|
64
|
+
),
|
|
65
|
+
stdio: str | None = typer.Option(
|
|
66
|
+
None, "--stdio", help="Command to run as STDIO MCP server (quoted)"
|
|
67
|
+
),
|
|
68
|
+
description: str | None = typer.Option(
|
|
69
|
+
None,
|
|
70
|
+
"--description",
|
|
71
|
+
"-d",
|
|
72
|
+
help="Description used for the exposed send tool (use {agent} to reference the agent name)",
|
|
73
|
+
),
|
|
74
|
+
transport: ServeTransport = typer.Option(
|
|
75
|
+
ServeTransport.HTTP,
|
|
76
|
+
"--transport",
|
|
77
|
+
help="Transport protocol to expose (http, sse, stdio)",
|
|
78
|
+
),
|
|
79
|
+
host: str = typer.Option(
|
|
80
|
+
"0.0.0.0",
|
|
81
|
+
"--host",
|
|
82
|
+
help="Host address to bind when using HTTP or SSE transport",
|
|
83
|
+
),
|
|
84
|
+
port: int = typer.Option(
|
|
85
|
+
8000,
|
|
86
|
+
"--port",
|
|
87
|
+
help="Port to use when running as a server with HTTP or SSE transport",
|
|
88
|
+
),
|
|
89
|
+
shell: bool = typer.Option(
|
|
90
|
+
False,
|
|
91
|
+
"--shell",
|
|
92
|
+
"-x",
|
|
93
|
+
help="Enable a local shell runtime and expose the execute tool (bash or pwsh).",
|
|
94
|
+
),
|
|
95
|
+
instance_scope: InstanceScope = typer.Option(
|
|
96
|
+
InstanceScope.SHARED,
|
|
97
|
+
"--instance-scope",
|
|
98
|
+
help="Control how MCP clients receive isolated agent instances (shared, connection, request)",
|
|
99
|
+
),
|
|
100
|
+
) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Run FastAgent as an MCP server.
|
|
103
|
+
|
|
104
|
+
Examples:
|
|
105
|
+
fast-agent serve --model=haiku --instruction=./instruction.md --transport=http --port=8000
|
|
106
|
+
fast-agent serve --url=http://localhost:8001/mcp --auth=YOUR_API_TOKEN
|
|
107
|
+
fast-agent serve --stdio "python my_server.py --debug"
|
|
108
|
+
fast-agent serve --npx "@modelcontextprotocol/server-filesystem /path/to/data"
|
|
109
|
+
fast-agent serve --description "Interact with the {agent} assistant"
|
|
110
|
+
"""
|
|
111
|
+
stdio_commands = collect_stdio_commands(npx, uvx, stdio)
|
|
112
|
+
shell_enabled = shell
|
|
113
|
+
|
|
114
|
+
resolved_instruction, agent_name = resolve_instruction_option(instruction)
|
|
115
|
+
|
|
116
|
+
run_async_agent(
|
|
117
|
+
name=name,
|
|
118
|
+
instruction=resolved_instruction,
|
|
119
|
+
config_path=config_path,
|
|
120
|
+
servers=servers,
|
|
121
|
+
urls=urls,
|
|
122
|
+
auth=auth,
|
|
123
|
+
model=model,
|
|
124
|
+
message=None,
|
|
125
|
+
prompt_file=None,
|
|
126
|
+
stdio_commands=stdio_commands,
|
|
127
|
+
agent_name=agent_name,
|
|
128
|
+
skills_directory=skills_dir,
|
|
129
|
+
shell_enabled=shell_enabled,
|
|
130
|
+
mode="serve",
|
|
131
|
+
transport=transport.value,
|
|
132
|
+
host=host,
|
|
133
|
+
port=port,
|
|
134
|
+
tool_description=description,
|
|
135
|
+
instance_scope=instance_scope.value,
|
|
136
|
+
)
|
fast_agent/cli/constants.py
CHANGED
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 auth, check_config, go, quickstart, setup
|
|
6
|
+
from fast_agent.cli.commands import auth, check_config, go, quickstart, serve, setup
|
|
7
7
|
from fast_agent.cli.terminal import Application
|
|
8
8
|
from fast_agent.ui.console import console as shared_console
|
|
9
9
|
|
|
@@ -14,6 +14,7 @@ app = typer.Typer(
|
|
|
14
14
|
|
|
15
15
|
# Subcommands
|
|
16
16
|
app.add_typer(go.app, name="go", help="Run an interactive agent directly from the command line")
|
|
17
|
+
app.add_typer(serve.app, name="serve", help="Run FastAgent as an MCP server")
|
|
17
18
|
app.add_typer(setup.app, name="setup", help="Set up a new agent project")
|
|
18
19
|
app.add_typer(check_config.app, name="check", help="Show or diagnose fast-agent configuration")
|
|
19
20
|
app.add_typer(auth.app, name="auth", help="Manage OAuth authentication for MCP servers")
|
|
@@ -62,7 +63,8 @@ def show_welcome() -> None:
|
|
|
62
63
|
table.add_column("Description", header_style="bold bright_white")
|
|
63
64
|
|
|
64
65
|
table.add_row("[bold]go[/bold]", "Start an interactive session")
|
|
65
|
-
table.add_row("go
|
|
66
|
+
table.add_row("go -x", "Start an interactive session with a local shell tool")
|
|
67
|
+
table.add_row("[bold]serve[/bold]", "Start fast-agent as an MCP server")
|
|
66
68
|
table.add_row("check", "Show current configuration")
|
|
67
69
|
table.add_row("auth", "Manage OAuth tokens and keyring")
|
|
68
70
|
table.add_row("setup", "Create agent template and configuration")
|
fast_agent/core/fastagent.py
CHANGED
|
@@ -6,9 +6,11 @@ directly creates Agent instances without proxies.
|
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
8
|
import asyncio
|
|
9
|
+
import inspect
|
|
9
10
|
import pathlib
|
|
10
11
|
import sys
|
|
11
12
|
from contextlib import asynccontextmanager
|
|
13
|
+
from dataclasses import dataclass
|
|
12
14
|
from importlib.metadata import version as get_version
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
from typing import (
|
|
@@ -127,6 +129,9 @@ class FastAgent:
|
|
|
127
129
|
Path(skills_directory).expanduser() if skills_directory else None
|
|
128
130
|
)
|
|
129
131
|
self._default_skill_manifests: List[SkillManifest] = []
|
|
132
|
+
self._server_instance_factory = None
|
|
133
|
+
self._server_instance_dispose = None
|
|
134
|
+
self._server_managed_instances: List[AgentInstance] = []
|
|
130
135
|
|
|
131
136
|
# --- Wrap argument parsing logic ---
|
|
132
137
|
if parse_cli_args:
|
|
@@ -181,6 +186,12 @@ class FastAgent:
|
|
|
181
186
|
default="0.0.0.0",
|
|
182
187
|
help="Host address to bind to when running as a server with SSE transport",
|
|
183
188
|
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--instance-scope",
|
|
191
|
+
choices=["shared", "connection", "request"],
|
|
192
|
+
default="shared",
|
|
193
|
+
help="Control MCP agent instancing behaviour (shared, connection, request)",
|
|
194
|
+
)
|
|
184
195
|
parser.add_argument(
|
|
185
196
|
"--skills",
|
|
186
197
|
help="Path to skills directory to use instead of default .claude/skills",
|
|
@@ -500,18 +511,34 @@ class FastAgent:
|
|
|
500
511
|
cli_model=cli_model_override, # Use the variable defined above
|
|
501
512
|
)
|
|
502
513
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
514
|
+
managed_instances: list[AgentInstance] = []
|
|
515
|
+
instance_lock = asyncio.Lock()
|
|
516
|
+
|
|
517
|
+
async def instantiate_agent_instance() -> AgentInstance:
|
|
518
|
+
async with instance_lock:
|
|
519
|
+
agents_map = await create_agents_in_dependency_order(
|
|
520
|
+
self.app,
|
|
521
|
+
self.agents,
|
|
522
|
+
model_factory_func,
|
|
523
|
+
)
|
|
524
|
+
validate_provider_keys_post_creation(agents_map)
|
|
525
|
+
instance = AgentInstance(AgentApp(agents_map), agents_map)
|
|
526
|
+
managed_instances.append(instance)
|
|
527
|
+
return instance
|
|
528
|
+
|
|
529
|
+
async def dispose_agent_instance(instance: AgentInstance) -> None:
|
|
530
|
+
async with instance_lock:
|
|
531
|
+
if instance in managed_instances:
|
|
532
|
+
managed_instances.remove(instance)
|
|
533
|
+
await instance.shutdown()
|
|
509
534
|
|
|
510
|
-
|
|
511
|
-
|
|
535
|
+
primary_instance = await instantiate_agent_instance()
|
|
536
|
+
wrapper = primary_instance.app
|
|
537
|
+
active_agents = primary_instance.agents
|
|
512
538
|
|
|
513
|
-
|
|
514
|
-
|
|
539
|
+
self._server_instance_factory = instantiate_agent_instance
|
|
540
|
+
self._server_instance_dispose = dispose_agent_instance
|
|
541
|
+
self._server_managed_instances = managed_instances
|
|
515
542
|
|
|
516
543
|
# Disable streaming if parallel agents are present
|
|
517
544
|
from fast_agent.agents.agent_types import AgentType
|
|
@@ -541,9 +568,18 @@ class FastAgent:
|
|
|
541
568
|
# Create the MCP server
|
|
542
569
|
from fast_agent.mcp.server import AgentMCPServer
|
|
543
570
|
|
|
571
|
+
tool_description = getattr(self.args, "tool_description", None)
|
|
572
|
+
server_description = getattr(self.args, "server_description", None)
|
|
573
|
+
server_name = getattr(self.args, "server_name", None)
|
|
574
|
+
instance_scope = getattr(self.args, "instance_scope", "shared")
|
|
544
575
|
mcp_server = AgentMCPServer(
|
|
545
|
-
|
|
546
|
-
|
|
576
|
+
primary_instance=primary_instance,
|
|
577
|
+
create_instance=self._server_instance_factory,
|
|
578
|
+
dispose_instance=self._server_instance_dispose,
|
|
579
|
+
instance_scope=instance_scope,
|
|
580
|
+
server_name=server_name or f"{self.name}-MCP-Server",
|
|
581
|
+
server_description=server_description,
|
|
582
|
+
tool_description=tool_description,
|
|
547
583
|
)
|
|
548
584
|
|
|
549
585
|
# Run the server directly (this is a blocking call)
|
|
@@ -647,11 +683,32 @@ class FastAgent:
|
|
|
647
683
|
pass
|
|
648
684
|
|
|
649
685
|
# Print usage report before cleanup (show for user exits too)
|
|
650
|
-
if
|
|
686
|
+
if (
|
|
687
|
+
getattr(self, "_server_managed_instances", None)
|
|
688
|
+
and not had_error
|
|
689
|
+
and not quiet_mode
|
|
690
|
+
and getattr(self.args, "server", False) is False
|
|
691
|
+
):
|
|
692
|
+
# Only show usage report for non-server interactive runs
|
|
693
|
+
if managed_instances:
|
|
694
|
+
instance = managed_instances[0]
|
|
695
|
+
self._print_usage_report(instance.agents)
|
|
696
|
+
elif active_agents and not had_error and not quiet_mode:
|
|
651
697
|
self._print_usage_report(active_agents)
|
|
652
698
|
|
|
653
699
|
# Clean up any active agents (always cleanup, even on errors)
|
|
654
|
-
if
|
|
700
|
+
if getattr(self, "_server_managed_instances", None) and getattr(
|
|
701
|
+
self, "_server_instance_dispose", None
|
|
702
|
+
):
|
|
703
|
+
# Dispose any remaining instances
|
|
704
|
+
remaining_instances = list(self._server_managed_instances)
|
|
705
|
+
for instance in remaining_instances:
|
|
706
|
+
try:
|
|
707
|
+
await self._server_instance_dispose(instance)
|
|
708
|
+
except Exception:
|
|
709
|
+
pass
|
|
710
|
+
self._server_managed_instances.clear()
|
|
711
|
+
elif active_agents:
|
|
655
712
|
for agent in active_agents.values():
|
|
656
713
|
try:
|
|
657
714
|
await agent.shutdown()
|
|
@@ -790,6 +847,8 @@ class FastAgent:
|
|
|
790
847
|
port: int = 8000,
|
|
791
848
|
server_name: Optional[str] = None,
|
|
792
849
|
server_description: Optional[str] = None,
|
|
850
|
+
tool_description: Optional[str] = None,
|
|
851
|
+
instance_scope: str = "shared",
|
|
793
852
|
) -> None:
|
|
794
853
|
"""
|
|
795
854
|
Start the application as an MCP server.
|
|
@@ -801,7 +860,9 @@ class FastAgent:
|
|
|
801
860
|
host: Host address for the server when using SSE
|
|
802
861
|
port: Port for the server when using SSE
|
|
803
862
|
server_name: Optional custom name for the MCP server
|
|
804
|
-
server_description: Optional description for the MCP server
|
|
863
|
+
server_description: Optional description/instructions for the MCP server
|
|
864
|
+
tool_description: Optional description template for the exposed send tool.
|
|
865
|
+
Use {agent} to reference the agent name.
|
|
805
866
|
"""
|
|
806
867
|
# This method simply updates the command line arguments and uses run()
|
|
807
868
|
# to ensure we follow the same initialization path for all operations
|
|
@@ -819,6 +880,10 @@ class FastAgent:
|
|
|
819
880
|
self.args.transport = transport
|
|
820
881
|
self.args.host = host
|
|
821
882
|
self.args.port = port
|
|
883
|
+
self.args.tool_description = tool_description
|
|
884
|
+
self.args.server_description = server_description
|
|
885
|
+
self.args.server_name = server_name
|
|
886
|
+
self.args.instance_scope = instance_scope
|
|
822
887
|
self.args.quiet = (
|
|
823
888
|
original_args.quiet if original_args and hasattr(original_args, "quiet") else False
|
|
824
889
|
)
|
|
@@ -842,6 +907,8 @@ class FastAgent:
|
|
|
842
907
|
port: int = 8000,
|
|
843
908
|
server_name: Optional[str] = None,
|
|
844
909
|
server_description: Optional[str] = None,
|
|
910
|
+
tool_description: Optional[str] = None,
|
|
911
|
+
instance_scope: str = "shared",
|
|
845
912
|
) -> None:
|
|
846
913
|
"""
|
|
847
914
|
Run the application and expose agents through an MCP server.
|
|
@@ -853,7 +920,8 @@ class FastAgent:
|
|
|
853
920
|
host: Host address for the server when using SSE
|
|
854
921
|
port: Port for the server when using SSE
|
|
855
922
|
server_name: Optional custom name for the MCP server
|
|
856
|
-
server_description: Optional description for the MCP server
|
|
923
|
+
server_description: Optional description/instructions for the MCP server
|
|
924
|
+
tool_description: Optional description template for the exposed send tool.
|
|
857
925
|
"""
|
|
858
926
|
await self.start_server(
|
|
859
927
|
transport=transport,
|
|
@@ -861,6 +929,8 @@ class FastAgent:
|
|
|
861
929
|
port=port,
|
|
862
930
|
server_name=server_name,
|
|
863
931
|
server_description=server_description,
|
|
932
|
+
tool_description=tool_description,
|
|
933
|
+
instance_scope=instance_scope,
|
|
864
934
|
)
|
|
865
935
|
|
|
866
936
|
async def main(self):
|
|
@@ -892,3 +962,19 @@ class FastAgent:
|
|
|
892
962
|
# Just check if the flag is set, no action here
|
|
893
963
|
# The actual server code will be handled by run()
|
|
894
964
|
return hasattr(self, "args") and self.args.server
|
|
965
|
+
@dataclass
|
|
966
|
+
class AgentInstance:
|
|
967
|
+
app: AgentApp
|
|
968
|
+
agents: Dict[str, "AgentProtocol"]
|
|
969
|
+
|
|
970
|
+
async def shutdown(self) -> None:
|
|
971
|
+
for agent in self.agents.values():
|
|
972
|
+
try:
|
|
973
|
+
shutdown = getattr(agent, "shutdown", None)
|
|
974
|
+
if shutdown is None:
|
|
975
|
+
continue
|
|
976
|
+
result = shutdown()
|
|
977
|
+
if inspect.isawaitable(result):
|
|
978
|
+
await result
|
|
979
|
+
except Exception:
|
|
980
|
+
pass
|
|
@@ -3,17 +3,19 @@ Enhanced AgentMCPServer with robust shutdown handling for SSE transport.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import logging
|
|
6
7
|
import os
|
|
7
8
|
import signal
|
|
9
|
+
import time
|
|
8
10
|
from contextlib import AsyncExitStack, asynccontextmanager
|
|
9
|
-
from typing import Set
|
|
11
|
+
from typing import Awaitable, Callable, Set
|
|
10
12
|
|
|
11
13
|
from mcp.server.fastmcp import Context as MCPContext
|
|
12
14
|
from mcp.server.fastmcp import FastMCP
|
|
13
15
|
|
|
14
16
|
import fast_agent.core
|
|
15
17
|
import fast_agent.core.prompt
|
|
16
|
-
from fast_agent.core.
|
|
18
|
+
from fast_agent.core.fastagent import AgentInstance
|
|
17
19
|
from fast_agent.core.logging.logger import get_logger
|
|
18
20
|
|
|
19
21
|
logger = get_logger(__name__)
|
|
@@ -24,17 +26,29 @@ class AgentMCPServer:
|
|
|
24
26
|
|
|
25
27
|
def __init__(
|
|
26
28
|
self,
|
|
27
|
-
|
|
29
|
+
primary_instance: AgentInstance,
|
|
30
|
+
create_instance: Callable[[], Awaitable[AgentInstance]],
|
|
31
|
+
dispose_instance: Callable[[AgentInstance], Awaitable[None]],
|
|
32
|
+
instance_scope: str,
|
|
28
33
|
server_name: str = "FastAgent-MCP-Server",
|
|
29
34
|
server_description: str | None = None,
|
|
35
|
+
tool_description: str | None = None,
|
|
30
36
|
) -> None:
|
|
31
37
|
"""Initialize the server with the provided agent app."""
|
|
32
|
-
self.
|
|
38
|
+
self.primary_instance = primary_instance
|
|
39
|
+
self._create_instance_task = create_instance
|
|
40
|
+
self._dispose_instance_task = dispose_instance
|
|
41
|
+
self._instance_scope = instance_scope
|
|
33
42
|
self.mcp_server: FastMCP = FastMCP(
|
|
34
43
|
name=server_name,
|
|
35
44
|
instructions=server_description
|
|
36
|
-
or f"This server provides access to {len(
|
|
45
|
+
or f"This server provides access to {len(primary_instance.agents)} agents",
|
|
37
46
|
)
|
|
47
|
+
if self._instance_scope == "request":
|
|
48
|
+
# Ensure FastMCP does not attempt to maintain sessions for stateless mode
|
|
49
|
+
self.mcp_server.settings.stateless_http = True
|
|
50
|
+
self._tool_description = tool_description
|
|
51
|
+
self._shared_instance_active = True
|
|
38
52
|
# Shutdown coordination
|
|
39
53
|
self._graceful_shutdown_event = asyncio.Event()
|
|
40
54
|
self._force_shutdown_event = asyncio.Event()
|
|
@@ -47,59 +61,187 @@ class AgentMCPServer:
|
|
|
47
61
|
# Server state
|
|
48
62
|
self._server_task = None
|
|
49
63
|
|
|
64
|
+
# Standard logging channel so we appear alongside Uvicorn/logging output
|
|
65
|
+
self.std_logger = logging.getLogger("fast_agent.server")
|
|
66
|
+
|
|
67
|
+
# Connection-scoped instance tracking
|
|
68
|
+
self._connection_instances: dict[int, AgentInstance] = {}
|
|
69
|
+
self._connection_cleanup_tasks: dict[int, Callable[[], Awaitable[None]]] = {}
|
|
70
|
+
self._connection_lock = asyncio.Lock()
|
|
71
|
+
|
|
50
72
|
# Set up agent tools
|
|
51
73
|
self.setup_tools()
|
|
52
74
|
|
|
53
|
-
logger.info(
|
|
75
|
+
logger.info(
|
|
76
|
+
f"AgentMCPServer initialized with {len(primary_instance.agents)} agents",
|
|
77
|
+
name="mcp_server_initialized",
|
|
78
|
+
agent_count=len(primary_instance.agents),
|
|
79
|
+
instance_scope=instance_scope,
|
|
80
|
+
)
|
|
54
81
|
|
|
55
82
|
def setup_tools(self) -> None:
|
|
56
83
|
"""Register all agents as MCP tools."""
|
|
57
|
-
for agent_name
|
|
58
|
-
self.register_agent_tools(agent_name
|
|
84
|
+
for agent_name in self.primary_instance.agents.keys():
|
|
85
|
+
self.register_agent_tools(agent_name)
|
|
59
86
|
|
|
60
|
-
def register_agent_tools(self, agent_name: str
|
|
87
|
+
def register_agent_tools(self, agent_name: str) -> None:
|
|
61
88
|
"""Register tools for a specific agent."""
|
|
62
89
|
|
|
63
90
|
# Basic send message tool
|
|
91
|
+
tool_description = (
|
|
92
|
+
self._tool_description.format(agent=agent_name)
|
|
93
|
+
if self._tool_description and "{agent}" in self._tool_description
|
|
94
|
+
else self._tool_description
|
|
95
|
+
)
|
|
96
|
+
|
|
64
97
|
@self.mcp_server.tool(
|
|
65
98
|
name=f"{agent_name}_send",
|
|
66
|
-
description=f"Send a message to the {agent_name} agent",
|
|
99
|
+
description=tool_description or f"Send a message to the {agent_name} agent",
|
|
67
100
|
structured_output=False,
|
|
68
101
|
# MCP 1.10.1 turns every tool in to a structured output
|
|
69
102
|
)
|
|
70
103
|
async def send_message(message: str, ctx: MCPContext) -> str:
|
|
71
104
|
"""Send a message to the agent and return its response."""
|
|
72
|
-
|
|
105
|
+
instance = await self._acquire_instance(ctx)
|
|
106
|
+
agent = instance.app[agent_name]
|
|
73
107
|
agent_context = getattr(agent, "context", None)
|
|
74
108
|
|
|
75
109
|
# Define the function to execute
|
|
76
110
|
async def execute_send():
|
|
77
|
-
|
|
111
|
+
start = time.perf_counter()
|
|
112
|
+
logger.info(
|
|
113
|
+
f"MCP request received for agent '{agent_name}'",
|
|
114
|
+
name="mcp_request_start",
|
|
115
|
+
agent=agent_name,
|
|
116
|
+
session=self._session_identifier(ctx),
|
|
117
|
+
)
|
|
118
|
+
self.std_logger.info(
|
|
119
|
+
"MCP request received for agent '%s' (scope=%s)",
|
|
120
|
+
agent_name,
|
|
121
|
+
self._instance_scope,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
response = await agent.send(message)
|
|
125
|
+
duration = time.perf_counter() - start
|
|
126
|
+
|
|
127
|
+
logger.info(
|
|
128
|
+
f"Agent '{agent_name}' completed MCP request",
|
|
129
|
+
name="mcp_request_complete",
|
|
130
|
+
agent=agent_name,
|
|
131
|
+
duration=duration,
|
|
132
|
+
session=self._session_identifier(ctx),
|
|
133
|
+
)
|
|
134
|
+
self.std_logger.info(
|
|
135
|
+
"Agent '%s' completed MCP request in %.2fs (scope=%s)",
|
|
136
|
+
agent_name,
|
|
137
|
+
duration,
|
|
138
|
+
self._instance_scope,
|
|
139
|
+
)
|
|
140
|
+
return response
|
|
78
141
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
142
|
+
try:
|
|
143
|
+
# Execute with bridged context
|
|
144
|
+
if agent_context and ctx:
|
|
145
|
+
return await self.with_bridged_context(agent_context, ctx, execute_send)
|
|
83
146
|
return await execute_send()
|
|
147
|
+
finally:
|
|
148
|
+
await self._release_instance(ctx, instance)
|
|
84
149
|
|
|
85
150
|
# Register a history prompt for this agent
|
|
86
151
|
@self.mcp_server.prompt(
|
|
87
152
|
name=f"{agent_name}_history",
|
|
88
153
|
description=f"Conversation history for the {agent_name} agent",
|
|
89
154
|
)
|
|
90
|
-
async def get_history_prompt() -> list:
|
|
155
|
+
async def get_history_prompt(ctx: MCPContext) -> list:
|
|
91
156
|
"""Return the conversation history as MCP messages."""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
instance = await self._acquire_instance(ctx)
|
|
158
|
+
agent = instance.app[agent_name]
|
|
159
|
+
try:
|
|
160
|
+
if not hasattr(agent, "_llm") or agent._llm is None:
|
|
161
|
+
return []
|
|
95
162
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
163
|
+
# Convert the multipart message history to standard PromptMessages
|
|
164
|
+
multipart_history = agent._llm.message_history
|
|
165
|
+
prompt_messages = fast_agent.core.prompt.Prompt.from_multipart(multipart_history)
|
|
99
166
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
167
|
+
# In FastMCP, we need to return the raw list of messages
|
|
168
|
+
return [{"role": msg.role, "content": msg.content} for msg in prompt_messages]
|
|
169
|
+
finally:
|
|
170
|
+
await self._release_instance(ctx, instance, reuse_connection=True)
|
|
171
|
+
|
|
172
|
+
async def _acquire_instance(self, ctx: MCPContext | None) -> AgentInstance:
|
|
173
|
+
if self._instance_scope == "shared":
|
|
174
|
+
return self.primary_instance
|
|
175
|
+
|
|
176
|
+
if self._instance_scope == "request":
|
|
177
|
+
return await self._create_instance_task()
|
|
178
|
+
|
|
179
|
+
# Connection scope
|
|
180
|
+
assert ctx is not None, "Context is required for connection-scoped instances"
|
|
181
|
+
session_key = self._connection_key(ctx)
|
|
182
|
+
async with self._connection_lock:
|
|
183
|
+
instance = self._connection_instances.get(session_key)
|
|
184
|
+
if instance is None:
|
|
185
|
+
instance = await self._create_instance_task()
|
|
186
|
+
self._connection_instances[session_key] = instance
|
|
187
|
+
self._register_session_cleanup(ctx, session_key)
|
|
188
|
+
return instance
|
|
189
|
+
|
|
190
|
+
async def _release_instance(
|
|
191
|
+
self,
|
|
192
|
+
ctx: MCPContext | None,
|
|
193
|
+
instance: AgentInstance,
|
|
194
|
+
*,
|
|
195
|
+
reuse_connection: bool = False,
|
|
196
|
+
) -> None:
|
|
197
|
+
if self._instance_scope == "request":
|
|
198
|
+
await self._dispose_instance_task(instance)
|
|
199
|
+
elif self._instance_scope == "connection" and reuse_connection is False:
|
|
200
|
+
# Connection-scoped instances persist until session cleanup
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
def _connection_key(self, ctx: MCPContext) -> int:
|
|
204
|
+
return id(ctx.session)
|
|
205
|
+
|
|
206
|
+
def _register_session_cleanup(self, ctx: MCPContext, session_key: int) -> None:
|
|
207
|
+
async def cleanup() -> None:
|
|
208
|
+
instance = self._connection_instances.pop(session_key, None)
|
|
209
|
+
if instance is not None:
|
|
210
|
+
await self._dispose_instance_task(instance)
|
|
211
|
+
|
|
212
|
+
exit_stack = getattr(ctx.session, "_exit_stack", None)
|
|
213
|
+
if exit_stack is not None:
|
|
214
|
+
exit_stack.push_async_callback(cleanup)
|
|
215
|
+
else:
|
|
216
|
+
self._connection_cleanup_tasks[session_key] = cleanup
|
|
217
|
+
|
|
218
|
+
def _session_identifier(self, ctx: MCPContext | None) -> str | None:
|
|
219
|
+
if ctx is None:
|
|
220
|
+
return None
|
|
221
|
+
request = getattr(ctx.request_context, "request", None)
|
|
222
|
+
if request is not None:
|
|
223
|
+
return request.headers.get("mcp-session-id")
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
async def _dispose_primary_instance(self) -> None:
|
|
227
|
+
if self._shared_instance_active:
|
|
228
|
+
try:
|
|
229
|
+
await self._dispose_instance_task(self.primary_instance)
|
|
230
|
+
finally:
|
|
231
|
+
self._shared_instance_active = False
|
|
232
|
+
|
|
233
|
+
async def _dispose_all_connection_instances(self) -> None:
|
|
234
|
+
pending_cleanups = list(self._connection_cleanup_tasks.values())
|
|
235
|
+
self._connection_cleanup_tasks.clear()
|
|
236
|
+
for cleanup in pending_cleanups:
|
|
237
|
+
await cleanup()
|
|
238
|
+
|
|
239
|
+
async with self._connection_lock:
|
|
240
|
+
instances = list(self._connection_instances.values())
|
|
241
|
+
self._connection_instances.clear()
|
|
242
|
+
|
|
243
|
+
for instance in instances:
|
|
244
|
+
await self._dispose_instance_task(instance)
|
|
103
245
|
|
|
104
246
|
def _setup_signal_handlers(self):
|
|
105
247
|
"""Set up signal handlers for graceful and forced shutdown."""
|
|
@@ -414,14 +556,8 @@ class AgentMCPServer:
|
|
|
414
556
|
"""Minimal cleanup for STDIO transport to avoid keeping process alive."""
|
|
415
557
|
logger.info("Performing minimal STDIO cleanup")
|
|
416
558
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
for agent_name, agent in self.agent_app._agents.items():
|
|
420
|
-
try:
|
|
421
|
-
if hasattr(agent, "shutdown"):
|
|
422
|
-
await agent.shutdown()
|
|
423
|
-
except Exception as e:
|
|
424
|
-
logger.error(f"Error shutting down agent {agent_name}: {e}")
|
|
559
|
+
await self._dispose_primary_instance()
|
|
560
|
+
await self._dispose_all_connection_instances()
|
|
425
561
|
|
|
426
562
|
logger.info("STDIO cleanup complete")
|
|
427
563
|
|
|
@@ -443,13 +579,11 @@ class AgentMCPServer:
|
|
|
443
579
|
# Close any resources in the exit stack
|
|
444
580
|
await self._exit_stack.aclose()
|
|
445
581
|
|
|
446
|
-
#
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
except Exception as e:
|
|
452
|
-
logger.error(f"Error shutting down agent {agent_name}: {e}")
|
|
582
|
+
# Dispose connection-scoped instances
|
|
583
|
+
await self._dispose_all_connection_instances()
|
|
584
|
+
|
|
585
|
+
# Dispose shared instance if still active
|
|
586
|
+
await self._dispose_primary_instance()
|
|
453
587
|
except Exception as e:
|
|
454
588
|
# Log any errors but don't let them prevent shutdown
|
|
455
589
|
logger.error(f"Error during shutdown: {e}", exc_info=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fast-agent-mcp
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
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
|
|
@@ -261,6 +261,14 @@ The simple declarative syntax lets you concentrate on composing your Prompts and
|
|
|
261
261
|
|
|
262
262
|
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.
|
|
263
263
|
|
|
264
|
+
Recent features include:
|
|
265
|
+
- Agent Skills (SKILL.md)
|
|
266
|
+
- MCP-UI Support |
|
|
267
|
+
- OpenAI Apps SDK (Skybridge)
|
|
268
|
+
- Shell Mode
|
|
269
|
+
- Advanced MCP Transport Diagnsotics
|
|
270
|
+
- MCP Elicitations
|
|
271
|
+
|
|
264
272
|
<img width="800" alt="MCP Transport Diagnostics" src="https://github.com/user-attachments/assets/e26472de-58d9-4726-8bdd-01eb407414cf" />
|
|
265
273
|
|
|
266
274
|
|
|
@@ -22,13 +22,14 @@ fast_agent/agents/workflow/parallel_agent.py,sha256=DlJXDURAfx-WBF297tKBLfH93gDF
|
|
|
22
22
|
fast_agent/agents/workflow/router_agent.py,sha256=gugcp0a-3Ldn42JbPJZ7AbO2G6FvqE0A3tsWcLorwSY,11400
|
|
23
23
|
fast_agent/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
fast_agent/cli/__main__.py,sha256=OWuKPFEeZRco6j5VA3IvtwTvZ351pDGxuO6rAiOTFNI,2588
|
|
25
|
-
fast_agent/cli/constants.py,sha256=
|
|
26
|
-
fast_agent/cli/main.py,sha256=
|
|
25
|
+
fast_agent/cli/constants.py,sha256=xLxQKuQzbv3Qkd6CB5wNwyDqyHBvdx97PZfsoK7-b4o,672
|
|
26
|
+
fast_agent/cli/main.py,sha256=yXglgZuQrThhqK3EgVstwpU3YRUf2-RWIDXDb6rQ66s,4650
|
|
27
27
|
fast_agent/cli/terminal.py,sha256=tDN1fJ91Nc_wZJTNafkQuD7Z7gFscvo1PHh-t7Wl-5s,1066
|
|
28
28
|
fast_agent/cli/commands/auth.py,sha256=nJEC7zrz5UXYUz5O6AgGZnfJPHIrgHk68CUwGo-7Nyg,15063
|
|
29
29
|
fast_agent/cli/commands/check_config.py,sha256=Iy5MHsRXqRcFLN-0Gs20jA3DqT-gN1VE9WtKuWlxJ9M,32597
|
|
30
|
-
fast_agent/cli/commands/go.py,sha256=
|
|
30
|
+
fast_agent/cli/commands/go.py,sha256=f0CrNkjzuIWWewUnCGeMWcO-93PdhyOqq5n3a-8cWVM,19005
|
|
31
31
|
fast_agent/cli/commands/quickstart.py,sha256=UOTqAbaVGLECHkTvpUNQ41PWXssqCijVvrqh30YUqnM,20624
|
|
32
|
+
fast_agent/cli/commands/serve.py,sha256=KU9QsP9MQVKV5eOrt8eBGi9gyx_Y20oaEwurDuWAc0k,4427
|
|
32
33
|
fast_agent/cli/commands/server_helpers.py,sha256=Nuded8sZb4Rybwoq5LbXXUgwtJZg-OO04xhmPUp6e98,4073
|
|
33
34
|
fast_agent/cli/commands/setup.py,sha256=n5hVjXkKTmuiW8-0ezItVcMHJ92W6NlE2JOGCYiKw0A,6388
|
|
34
35
|
fast_agent/cli/commands/url_parser.py,sha256=v9KoprPBEEST5Fo7qXgbW50GC5vMpxFteKqAT6mFkdI,5991
|
|
@@ -39,7 +40,7 @@ fast_agent/core/direct_decorators.py,sha256=Z8zM1Ep9THEiuNzlxW-WmQDm7x4JF0wn4xO2
|
|
|
39
40
|
fast_agent/core/direct_factory.py,sha256=SYYVtEqPQh7ElvAVC_445a5pZkKstM0RhKNGZ2CJQT8,18338
|
|
40
41
|
fast_agent/core/error_handling.py,sha256=tZkO8LnXO-qf6jD8a12Pv5fD4NhnN1Ag5_tJ6DwbXjg,631
|
|
41
42
|
fast_agent/core/exceptions.py,sha256=ENAD_qGG67foxy6vDkIvc-lgopIUQy6O7zvNPpPXaQg,2289
|
|
42
|
-
fast_agent/core/fastagent.py,sha256=
|
|
43
|
+
fast_agent/core/fastagent.py,sha256=u9TBdQxBlyV3OhZ5gfOB2TucdI3xI2xsEObW-c1WTdA,39865
|
|
43
44
|
fast_agent/core/prompt.py,sha256=qNUFlK3KtU7leYysYUglzBYQnEYiXu__iR_T8189zc0,203
|
|
44
45
|
fast_agent/core/validation.py,sha256=cesQeT8jfLlPqew6S9bq8ZJqde7ViVQXHF9fhAnyOHI,11950
|
|
45
46
|
fast_agent/core/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -135,7 +136,7 @@ fast_agent/mcp/prompts/prompt_load.py,sha256=1KixVXpkML3_kY3vC59AlMfcBXQVESrII24
|
|
|
135
136
|
fast_agent/mcp/prompts/prompt_server.py,sha256=ZuUFqYfiYYE8yTXw0qagGWkZkelQgETGBpzUIdIhLXc,20174
|
|
136
137
|
fast_agent/mcp/prompts/prompt_template.py,sha256=SAklXs9wujYqqEWv5vzRI_cdjSmGnWlDmPLZ5qkXZO4,15695
|
|
137
138
|
fast_agent/mcp/server/__init__.py,sha256=AJFNzdmuHPRL3jqFhDVDJste_zYE_KJ3gGYDsbghvl0,156
|
|
138
|
-
fast_agent/mcp/server/agent_server.py,sha256=
|
|
139
|
+
fast_agent/mcp/server/agent_server.py,sha256=svO0_RwJX1thRRfciLXkbLUFLokkzw5VspGIs5Ias40,25555
|
|
139
140
|
fast_agent/resources/examples/data-analysis/analysis-campaign.py,sha256=SnDQm_e_cm0IZEKdWizUedXxpIWWj-5K70wmcM1Tw2Y,7277
|
|
140
141
|
fast_agent/resources/examples/data-analysis/analysis.py,sha256=0W1dN3SAx-RxEKoH3shAq42HxglEz9hc8BadGYmnwAk,2653
|
|
141
142
|
fast_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=ini94PHyJCfgpjcjHKMMbGuHs6LIj46F1NwY0ll5HVk,1609
|
|
@@ -218,8 +219,8 @@ fast_agent/ui/streaming.py,sha256=SNLGx6s8xuRikxi6vNxnDCgQQKqFJ5rbTKU9ixnzmY0,22
|
|
|
218
219
|
fast_agent/ui/streaming_buffer.py,sha256=e-zwVUVBOQ_mKyHgLiTXFmShGs4DNQRZ9BZZwWgXoWM,16648
|
|
219
220
|
fast_agent/ui/tool_display.py,sha256=DK6kA8MBjxq8qpK1WLv7kFKAfOtEOfY5rtQ8t4XeM0s,16111
|
|
220
221
|
fast_agent/ui/usage_display.py,sha256=ltJpn_sDzo8PDNSXWx-QdEUbQWUnhmajCItNt5mA5rM,7285
|
|
221
|
-
fast_agent_mcp-0.3.
|
|
222
|
-
fast_agent_mcp-0.3.
|
|
223
|
-
fast_agent_mcp-0.3.
|
|
224
|
-
fast_agent_mcp-0.3.
|
|
225
|
-
fast_agent_mcp-0.3.
|
|
222
|
+
fast_agent_mcp-0.3.18.dist-info/METADATA,sha256=UluPke36Pp1VhO8z8hmPIlC-MjqN94xO6N02HF3EWbU,32259
|
|
223
|
+
fast_agent_mcp-0.3.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
224
|
+
fast_agent_mcp-0.3.18.dist-info/entry_points.txt,sha256=i6Ujja9J-hRxttOKqTYdbYP_tyaS4gLHg53vupoCSsg,199
|
|
225
|
+
fast_agent_mcp-0.3.18.dist-info/licenses/LICENSE,sha256=Gx1L3axA4PnuK4FxsbX87jQ1opoOkSFfHHSytW6wLUU,10935
|
|
226
|
+
fast_agent_mcp-0.3.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|