fast-agent-mcp 0.4.7__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.
- fast_agent/__init__.py +183 -0
- fast_agent/acp/__init__.py +19 -0
- fast_agent/acp/acp_aware_mixin.py +304 -0
- fast_agent/acp/acp_context.py +437 -0
- fast_agent/acp/content_conversion.py +136 -0
- fast_agent/acp/filesystem_runtime.py +427 -0
- fast_agent/acp/permission_store.py +269 -0
- fast_agent/acp/server/__init__.py +5 -0
- fast_agent/acp/server/agent_acp_server.py +1472 -0
- fast_agent/acp/slash_commands.py +1050 -0
- fast_agent/acp/terminal_runtime.py +408 -0
- fast_agent/acp/tool_permission_adapter.py +125 -0
- fast_agent/acp/tool_permissions.py +474 -0
- fast_agent/acp/tool_progress.py +814 -0
- fast_agent/agents/__init__.py +85 -0
- fast_agent/agents/agent_types.py +64 -0
- fast_agent/agents/llm_agent.py +350 -0
- fast_agent/agents/llm_decorator.py +1139 -0
- fast_agent/agents/mcp_agent.py +1337 -0
- fast_agent/agents/tool_agent.py +271 -0
- fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
- fast_agent/agents/workflow/chain_agent.py +212 -0
- fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
- fast_agent/agents/workflow/iterative_planner.py +652 -0
- fast_agent/agents/workflow/maker_agent.py +379 -0
- fast_agent/agents/workflow/orchestrator_models.py +218 -0
- fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
- fast_agent/agents/workflow/parallel_agent.py +250 -0
- fast_agent/agents/workflow/router_agent.py +353 -0
- fast_agent/cli/__init__.py +0 -0
- fast_agent/cli/__main__.py +73 -0
- fast_agent/cli/commands/acp.py +159 -0
- fast_agent/cli/commands/auth.py +404 -0
- fast_agent/cli/commands/check_config.py +783 -0
- fast_agent/cli/commands/go.py +514 -0
- fast_agent/cli/commands/quickstart.py +557 -0
- fast_agent/cli/commands/serve.py +143 -0
- fast_agent/cli/commands/server_helpers.py +114 -0
- fast_agent/cli/commands/setup.py +174 -0
- fast_agent/cli/commands/url_parser.py +190 -0
- fast_agent/cli/constants.py +40 -0
- fast_agent/cli/main.py +115 -0
- fast_agent/cli/terminal.py +24 -0
- fast_agent/config.py +798 -0
- fast_agent/constants.py +41 -0
- fast_agent/context.py +279 -0
- fast_agent/context_dependent.py +50 -0
- fast_agent/core/__init__.py +92 -0
- fast_agent/core/agent_app.py +448 -0
- fast_agent/core/core_app.py +137 -0
- fast_agent/core/direct_decorators.py +784 -0
- fast_agent/core/direct_factory.py +620 -0
- fast_agent/core/error_handling.py +27 -0
- fast_agent/core/exceptions.py +90 -0
- fast_agent/core/executor/__init__.py +0 -0
- fast_agent/core/executor/executor.py +280 -0
- fast_agent/core/executor/task_registry.py +32 -0
- fast_agent/core/executor/workflow_signal.py +324 -0
- fast_agent/core/fastagent.py +1186 -0
- fast_agent/core/logging/__init__.py +5 -0
- fast_agent/core/logging/events.py +138 -0
- fast_agent/core/logging/json_serializer.py +164 -0
- fast_agent/core/logging/listeners.py +309 -0
- fast_agent/core/logging/logger.py +278 -0
- fast_agent/core/logging/transport.py +481 -0
- fast_agent/core/prompt.py +9 -0
- fast_agent/core/prompt_templates.py +183 -0
- fast_agent/core/validation.py +326 -0
- fast_agent/event_progress.py +62 -0
- fast_agent/history/history_exporter.py +49 -0
- fast_agent/human_input/__init__.py +47 -0
- fast_agent/human_input/elicitation_handler.py +123 -0
- fast_agent/human_input/elicitation_state.py +33 -0
- fast_agent/human_input/form_elements.py +59 -0
- fast_agent/human_input/form_fields.py +256 -0
- fast_agent/human_input/simple_form.py +113 -0
- fast_agent/human_input/types.py +40 -0
- fast_agent/interfaces.py +310 -0
- fast_agent/llm/__init__.py +9 -0
- fast_agent/llm/cancellation.py +22 -0
- fast_agent/llm/fastagent_llm.py +931 -0
- fast_agent/llm/internal/passthrough.py +161 -0
- fast_agent/llm/internal/playback.py +129 -0
- fast_agent/llm/internal/silent.py +41 -0
- fast_agent/llm/internal/slow.py +38 -0
- fast_agent/llm/memory.py +275 -0
- fast_agent/llm/model_database.py +490 -0
- fast_agent/llm/model_factory.py +388 -0
- fast_agent/llm/model_info.py +102 -0
- fast_agent/llm/prompt_utils.py +155 -0
- fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
- fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
- fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
- fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
- fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
- fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
- fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
- fast_agent/llm/provider/google/google_converter.py +466 -0
- fast_agent/llm/provider/google/llm_google_native.py +681 -0
- fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
- fast_agent/llm/provider/openai/llm_azure.py +143 -0
- fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
- fast_agent/llm/provider/openai/llm_generic.py +35 -0
- fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
- fast_agent/llm/provider/openai/llm_groq.py +42 -0
- fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
- fast_agent/llm/provider/openai/llm_openai.py +1195 -0
- fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
- fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
- fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
- fast_agent/llm/provider/openai/llm_xai.py +38 -0
- fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
- fast_agent/llm/provider/openai/openai_multipart.py +169 -0
- fast_agent/llm/provider/openai/openai_utils.py +67 -0
- fast_agent/llm/provider/openai/responses.py +133 -0
- fast_agent/llm/provider_key_manager.py +139 -0
- fast_agent/llm/provider_types.py +34 -0
- fast_agent/llm/request_params.py +61 -0
- fast_agent/llm/sampling_converter.py +98 -0
- fast_agent/llm/stream_types.py +9 -0
- fast_agent/llm/usage_tracking.py +445 -0
- fast_agent/mcp/__init__.py +56 -0
- fast_agent/mcp/common.py +26 -0
- fast_agent/mcp/elicitation_factory.py +84 -0
- fast_agent/mcp/elicitation_handlers.py +164 -0
- fast_agent/mcp/gen_client.py +83 -0
- fast_agent/mcp/helpers/__init__.py +36 -0
- fast_agent/mcp/helpers/content_helpers.py +352 -0
- fast_agent/mcp/helpers/server_config_helpers.py +25 -0
- fast_agent/mcp/hf_auth.py +147 -0
- fast_agent/mcp/interfaces.py +92 -0
- fast_agent/mcp/logger_textio.py +108 -0
- fast_agent/mcp/mcp_agent_client_session.py +411 -0
- fast_agent/mcp/mcp_aggregator.py +2175 -0
- fast_agent/mcp/mcp_connection_manager.py +723 -0
- fast_agent/mcp/mcp_content.py +262 -0
- fast_agent/mcp/mime_utils.py +108 -0
- fast_agent/mcp/oauth_client.py +509 -0
- fast_agent/mcp/prompt.py +159 -0
- fast_agent/mcp/prompt_message_extended.py +155 -0
- fast_agent/mcp/prompt_render.py +84 -0
- fast_agent/mcp/prompt_serialization.py +580 -0
- fast_agent/mcp/prompts/__init__.py +0 -0
- fast_agent/mcp/prompts/__main__.py +7 -0
- fast_agent/mcp/prompts/prompt_constants.py +18 -0
- fast_agent/mcp/prompts/prompt_helpers.py +238 -0
- fast_agent/mcp/prompts/prompt_load.py +186 -0
- fast_agent/mcp/prompts/prompt_server.py +552 -0
- fast_agent/mcp/prompts/prompt_template.py +438 -0
- fast_agent/mcp/resource_utils.py +215 -0
- fast_agent/mcp/sampling.py +200 -0
- fast_agent/mcp/server/__init__.py +4 -0
- fast_agent/mcp/server/agent_server.py +613 -0
- fast_agent/mcp/skybridge.py +44 -0
- fast_agent/mcp/sse_tracking.py +287 -0
- fast_agent/mcp/stdio_tracking_simple.py +59 -0
- fast_agent/mcp/streamable_http_tracking.py +309 -0
- fast_agent/mcp/tool_execution_handler.py +137 -0
- fast_agent/mcp/tool_permission_handler.py +88 -0
- fast_agent/mcp/transport_tracking.py +634 -0
- fast_agent/mcp/types.py +24 -0
- fast_agent/mcp/ui_agent.py +48 -0
- fast_agent/mcp/ui_mixin.py +209 -0
- fast_agent/mcp_server_registry.py +89 -0
- fast_agent/py.typed +0 -0
- fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
- fast_agent/resources/examples/data-analysis/analysis.py +68 -0
- fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
- fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
- fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
- fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
- fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
- fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
- fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
- fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
- fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
- fast_agent/resources/examples/researcher/researcher.py +36 -0
- fast_agent/resources/examples/tensorzero/.env.sample +2 -0
- fast_agent/resources/examples/tensorzero/Makefile +31 -0
- fast_agent/resources/examples/tensorzero/README.md +56 -0
- fast_agent/resources/examples/tensorzero/agent.py +35 -0
- fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
- fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
- fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
- fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
- fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
- fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
- fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
- fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
- fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
- fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
- fast_agent/resources/examples/workflows/chaining.py +37 -0
- fast_agent/resources/examples/workflows/evaluator.py +77 -0
- fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
- fast_agent/resources/examples/workflows/graded_report.md +89 -0
- fast_agent/resources/examples/workflows/human_input.py +28 -0
- fast_agent/resources/examples/workflows/maker.py +156 -0
- fast_agent/resources/examples/workflows/orchestrator.py +70 -0
- fast_agent/resources/examples/workflows/parallel.py +56 -0
- fast_agent/resources/examples/workflows/router.py +69 -0
- fast_agent/resources/examples/workflows/short_story.md +13 -0
- fast_agent/resources/examples/workflows/short_story.txt +19 -0
- fast_agent/resources/setup/.gitignore +30 -0
- fast_agent/resources/setup/agent.py +28 -0
- fast_agent/resources/setup/fastagent.config.yaml +65 -0
- fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
- fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
- fast_agent/skills/__init__.py +9 -0
- fast_agent/skills/registry.py +235 -0
- fast_agent/tools/elicitation.py +369 -0
- fast_agent/tools/shell_runtime.py +402 -0
- fast_agent/types/__init__.py +59 -0
- fast_agent/types/conversation_summary.py +294 -0
- fast_agent/types/llm_stop_reason.py +78 -0
- fast_agent/types/message_search.py +249 -0
- fast_agent/ui/__init__.py +38 -0
- fast_agent/ui/console.py +59 -0
- fast_agent/ui/console_display.py +1080 -0
- fast_agent/ui/elicitation_form.py +946 -0
- fast_agent/ui/elicitation_style.py +59 -0
- fast_agent/ui/enhanced_prompt.py +1400 -0
- fast_agent/ui/history_display.py +734 -0
- fast_agent/ui/interactive_prompt.py +1199 -0
- fast_agent/ui/markdown_helpers.py +104 -0
- fast_agent/ui/markdown_truncator.py +1004 -0
- fast_agent/ui/mcp_display.py +857 -0
- fast_agent/ui/mcp_ui_utils.py +235 -0
- fast_agent/ui/mermaid_utils.py +169 -0
- fast_agent/ui/message_primitives.py +50 -0
- fast_agent/ui/notification_tracker.py +205 -0
- fast_agent/ui/plain_text_truncator.py +68 -0
- fast_agent/ui/progress_display.py +10 -0
- fast_agent/ui/rich_progress.py +195 -0
- fast_agent/ui/streaming.py +774 -0
- fast_agent/ui/streaming_buffer.py +449 -0
- fast_agent/ui/tool_display.py +422 -0
- fast_agent/ui/usage_display.py +204 -0
- fast_agent/utils/__init__.py +5 -0
- fast_agent/utils/reasoning_stream_parser.py +77 -0
- fast_agent/utils/time.py +22 -0
- fast_agent/workflow_telemetry.py +261 -0
- fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
- fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
- fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
- fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
- fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
ACP = "acp"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InstanceScope(str, Enum):
|
|
23
|
+
SHARED = "shared"
|
|
24
|
+
CONNECTION = "connection"
|
|
25
|
+
REQUEST = "request"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
app = typer.Typer(
|
|
29
|
+
help="Run FastAgent as an MCP server without writing an agent.py file",
|
|
30
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.callback(invoke_without_command=True, no_args_is_help=False)
|
|
35
|
+
def serve(
|
|
36
|
+
ctx: typer.Context,
|
|
37
|
+
name: str = typer.Option("fast-agent", "--name", help="Name for the MCP server"),
|
|
38
|
+
instruction: str | None = typer.Option(
|
|
39
|
+
None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
|
|
40
|
+
),
|
|
41
|
+
config_path: str | None = typer.Option(None, "--config-path", "-c", help="Path to config file"),
|
|
42
|
+
servers: str | None = typer.Option(
|
|
43
|
+
None, "--servers", help="Comma-separated list of server names to enable from config"
|
|
44
|
+
),
|
|
45
|
+
urls: str | None = typer.Option(
|
|
46
|
+
None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
|
|
47
|
+
),
|
|
48
|
+
auth: str | None = typer.Option(
|
|
49
|
+
None, "--auth", help="Bearer token for authorization with URL-based servers"
|
|
50
|
+
),
|
|
51
|
+
model: str | None = typer.Option(
|
|
52
|
+
None, "--model", "--models", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
|
|
53
|
+
),
|
|
54
|
+
skills_dir: Path | None = typer.Option(
|
|
55
|
+
None,
|
|
56
|
+
"--skills-dir",
|
|
57
|
+
"--skills",
|
|
58
|
+
help="Override the default skills directory",
|
|
59
|
+
),
|
|
60
|
+
npx: str | None = typer.Option(
|
|
61
|
+
None, "--npx", help="NPX package and args to run as MCP server (quoted)"
|
|
62
|
+
),
|
|
63
|
+
uvx: str | None = typer.Option(
|
|
64
|
+
None, "--uvx", help="UVX package and args to run as MCP server (quoted)"
|
|
65
|
+
),
|
|
66
|
+
stdio: str | None = typer.Option(
|
|
67
|
+
None, "--stdio", help="Command to run as STDIO MCP server (quoted)"
|
|
68
|
+
),
|
|
69
|
+
description: str | None = typer.Option(
|
|
70
|
+
None,
|
|
71
|
+
"--description",
|
|
72
|
+
"-d",
|
|
73
|
+
help="Description used for the exposed send tool (use {agent} to reference the agent name)",
|
|
74
|
+
),
|
|
75
|
+
transport: ServeTransport = typer.Option(
|
|
76
|
+
ServeTransport.HTTP,
|
|
77
|
+
"--transport",
|
|
78
|
+
help="Transport protocol to expose (http, sse, stdio, acp)",
|
|
79
|
+
),
|
|
80
|
+
host: str = typer.Option(
|
|
81
|
+
"0.0.0.0",
|
|
82
|
+
"--host",
|
|
83
|
+
help="Host address to bind when using HTTP or SSE transport",
|
|
84
|
+
),
|
|
85
|
+
port: int = typer.Option(
|
|
86
|
+
8000,
|
|
87
|
+
"--port",
|
|
88
|
+
help="Port to use when running as a server with HTTP or SSE transport",
|
|
89
|
+
),
|
|
90
|
+
shell: bool = typer.Option(
|
|
91
|
+
False,
|
|
92
|
+
"--shell",
|
|
93
|
+
"-x",
|
|
94
|
+
help="Enable a local shell runtime and expose the execute tool (bash or pwsh).",
|
|
95
|
+
),
|
|
96
|
+
instance_scope: InstanceScope = typer.Option(
|
|
97
|
+
InstanceScope.SHARED,
|
|
98
|
+
"--instance-scope",
|
|
99
|
+
help="Control how MCP clients receive isolated agent instances (shared, connection, request)",
|
|
100
|
+
),
|
|
101
|
+
no_permissions: bool = typer.Option(
|
|
102
|
+
False,
|
|
103
|
+
"--no-permissions",
|
|
104
|
+
help="Disable tool permission requests (allow all tool executions without asking) - ACP only",
|
|
105
|
+
),
|
|
106
|
+
) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Run FastAgent as an MCP server.
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
fast-agent serve --model=haiku --instruction=./instruction.md --transport=http --port=8000
|
|
112
|
+
fast-agent serve --url=http://localhost:8001/mcp --auth=YOUR_API_TOKEN
|
|
113
|
+
fast-agent serve --stdio "python my_server.py --debug"
|
|
114
|
+
fast-agent serve --npx "@modelcontextprotocol/server-filesystem /path/to/data"
|
|
115
|
+
fast-agent serve --description "Interact with the {agent} assistant"
|
|
116
|
+
"""
|
|
117
|
+
stdio_commands = collect_stdio_commands(npx, uvx, stdio)
|
|
118
|
+
shell_enabled = shell
|
|
119
|
+
|
|
120
|
+
resolved_instruction, agent_name = resolve_instruction_option(instruction)
|
|
121
|
+
|
|
122
|
+
run_async_agent(
|
|
123
|
+
name=name,
|
|
124
|
+
instruction=resolved_instruction,
|
|
125
|
+
config_path=config_path,
|
|
126
|
+
servers=servers,
|
|
127
|
+
urls=urls,
|
|
128
|
+
auth=auth,
|
|
129
|
+
model=model,
|
|
130
|
+
message=None,
|
|
131
|
+
prompt_file=None,
|
|
132
|
+
stdio_commands=stdio_commands,
|
|
133
|
+
agent_name=agent_name,
|
|
134
|
+
skills_directory=skills_dir,
|
|
135
|
+
shell_enabled=shell_enabled,
|
|
136
|
+
mode="serve",
|
|
137
|
+
transport=transport.value,
|
|
138
|
+
host=host,
|
|
139
|
+
port=port,
|
|
140
|
+
tool_description=description,
|
|
141
|
+
instance_scope=instance_scope.value,
|
|
142
|
+
permissions_enabled=not no_permissions,
|
|
143
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Helper functions for server configuration and naming."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def generate_server_name(identifier: str) -> str:
|
|
7
|
+
"""Generate a clean server name from various identifiers.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
identifier: Package name, file path, or other identifier
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
Clean server name with only alphanumeric and underscore characters
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
>>> generate_server_name("@modelcontextprotocol/server-filesystem")
|
|
17
|
+
'server_filesystem'
|
|
18
|
+
>>> generate_server_name("./src/my-server.py")
|
|
19
|
+
'src_my_server'
|
|
20
|
+
>>> generate_server_name("my-mcp-server")
|
|
21
|
+
'my_mcp_server'
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Remove leading ./ if present
|
|
25
|
+
if identifier.startswith("./"):
|
|
26
|
+
identifier = identifier[2:]
|
|
27
|
+
|
|
28
|
+
# Handle npm package names with org prefix (only if no file extension)
|
|
29
|
+
has_file_ext = any(identifier.endswith(ext) for ext in [".py", ".js", ".ts"])
|
|
30
|
+
if "/" in identifier and not has_file_ext:
|
|
31
|
+
# This is likely an npm package, take the part after the last slash
|
|
32
|
+
identifier = identifier.split("/")[-1]
|
|
33
|
+
|
|
34
|
+
# Remove file extension for common script files
|
|
35
|
+
for ext in [".py", ".js", ".ts"]:
|
|
36
|
+
if identifier.endswith(ext):
|
|
37
|
+
identifier = identifier[: -len(ext)]
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
# Replace special characters with underscores
|
|
41
|
+
# Remove @ prefix if present
|
|
42
|
+
identifier = identifier.lstrip("@")
|
|
43
|
+
|
|
44
|
+
# Replace non-alphanumeric characters with underscores
|
|
45
|
+
server_name = ""
|
|
46
|
+
for char in identifier:
|
|
47
|
+
if char.isalnum():
|
|
48
|
+
server_name += char
|
|
49
|
+
else:
|
|
50
|
+
server_name += "_"
|
|
51
|
+
|
|
52
|
+
# Clean up multiple underscores
|
|
53
|
+
while "__" in server_name:
|
|
54
|
+
server_name = server_name.replace("__", "_")
|
|
55
|
+
|
|
56
|
+
# Remove leading/trailing underscores
|
|
57
|
+
server_name = server_name.strip("_")
|
|
58
|
+
|
|
59
|
+
return server_name
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def add_servers_to_config(fast_app: Any, servers: dict[str, dict[str, Any]]) -> None:
|
|
63
|
+
"""Add server configurations to the FastAgent app config.
|
|
64
|
+
|
|
65
|
+
This function handles the repetitive initialization and configuration
|
|
66
|
+
of MCP servers, ensuring the app is initialized and the config
|
|
67
|
+
structure exists before adding servers.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
fast_app: The FastAgent instance
|
|
71
|
+
servers: Dictionary of server configurations
|
|
72
|
+
"""
|
|
73
|
+
if not servers:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
from fast_agent.config import MCPServerSettings, MCPSettings
|
|
77
|
+
|
|
78
|
+
# Initialize the app to ensure context is ready
|
|
79
|
+
await fast_app.app.initialize()
|
|
80
|
+
|
|
81
|
+
# Initialize mcp settings if needed
|
|
82
|
+
if not hasattr(fast_app.app.context.config, "mcp"):
|
|
83
|
+
fast_app.app.context.config.mcp = MCPSettings()
|
|
84
|
+
|
|
85
|
+
# Initialize servers dictionary if needed
|
|
86
|
+
if (
|
|
87
|
+
not hasattr(fast_app.app.context.config.mcp, "servers")
|
|
88
|
+
or fast_app.app.context.config.mcp.servers is None
|
|
89
|
+
):
|
|
90
|
+
fast_app.app.context.config.mcp.servers = {}
|
|
91
|
+
|
|
92
|
+
# Add each server to the config (and keep the runtime registry in sync)
|
|
93
|
+
for server_name, server_config in servers.items():
|
|
94
|
+
# Build server settings based on transport type
|
|
95
|
+
server_settings = {"transport": server_config["transport"]}
|
|
96
|
+
|
|
97
|
+
# Add transport-specific settings
|
|
98
|
+
if server_config["transport"] == "stdio":
|
|
99
|
+
server_settings["command"] = server_config["command"]
|
|
100
|
+
server_settings["args"] = server_config["args"]
|
|
101
|
+
elif server_config["transport"] in ["http", "sse"]:
|
|
102
|
+
server_settings["url"] = server_config["url"]
|
|
103
|
+
if "headers" in server_config:
|
|
104
|
+
server_settings["headers"] = server_config["headers"]
|
|
105
|
+
|
|
106
|
+
mcp_server = MCPServerSettings(**server_settings)
|
|
107
|
+
# Update config model
|
|
108
|
+
fast_app.app.context.config.mcp.servers[server_name] = mcp_server
|
|
109
|
+
# Ensure ServerRegistry sees dynamic additions even when no config file exists
|
|
110
|
+
if (
|
|
111
|
+
hasattr(fast_app.app.context, "server_registry")
|
|
112
|
+
and fast_app.app.context.server_registry is not None
|
|
113
|
+
):
|
|
114
|
+
fast_app.app.context.server_registry.registry[server_name] = mcp_server
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.prompt import Confirm
|
|
5
|
+
|
|
6
|
+
from fast_agent.ui.console import console as shared_console
|
|
7
|
+
|
|
8
|
+
app = typer.Typer()
|
|
9
|
+
console = shared_console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_template_text(filename: str) -> str:
|
|
13
|
+
"""Load template text from packaged resources only.
|
|
14
|
+
|
|
15
|
+
Special-case: when requesting 'fastagent.secrets.yaml', read the
|
|
16
|
+
'fastagent.secrets.yaml.example' template from resources, but still
|
|
17
|
+
return its contents so we can write out the real secrets file name
|
|
18
|
+
in the destination project.
|
|
19
|
+
"""
|
|
20
|
+
from importlib.resources import files
|
|
21
|
+
|
|
22
|
+
# Map requested filenames to resource templates
|
|
23
|
+
if filename == "fastagent.secrets.yaml":
|
|
24
|
+
res_name = "fastagent.secrets.yaml.example"
|
|
25
|
+
elif filename == "pyproject.toml":
|
|
26
|
+
res_name = "pyproject.toml.tmpl"
|
|
27
|
+
else:
|
|
28
|
+
res_name = filename
|
|
29
|
+
resource_path = files("fast_agent").joinpath("resources").joinpath("setup").joinpath(res_name)
|
|
30
|
+
if resource_path.is_file():
|
|
31
|
+
return resource_path.read_text()
|
|
32
|
+
|
|
33
|
+
raise RuntimeError(
|
|
34
|
+
f"Setup template missing: '{filename}'.\n"
|
|
35
|
+
f"Expected packaged resource at: {resource_path}.\n"
|
|
36
|
+
"This indicates a packaging issue. Please rebuild/reinstall fast-agent."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# (No embedded template defaults; templates are the single source of truth.)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def find_gitignore(path: Path) -> bool:
|
|
44
|
+
"""Check if a .gitignore file exists in this directory or any parent."""
|
|
45
|
+
current = path
|
|
46
|
+
while current != current.parent: # Stop at root directory
|
|
47
|
+
if (current / ".gitignore").exists():
|
|
48
|
+
return True
|
|
49
|
+
current = current.parent
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_file(path: Path, content: str, force: bool = False) -> bool:
|
|
54
|
+
"""Create a file with given content if it doesn't exist or force is True."""
|
|
55
|
+
if path.exists() and not force:
|
|
56
|
+
should_overwrite = Confirm.ask(
|
|
57
|
+
f"[yellow]Warning:[/yellow] {path} already exists. Overwrite?",
|
|
58
|
+
default=False,
|
|
59
|
+
)
|
|
60
|
+
if not should_overwrite:
|
|
61
|
+
console.print(f"Skipping {path}")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
path.write_text(content.strip() + "\n")
|
|
65
|
+
console.print(f"[green]Created[/green] {path}")
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.callback(invoke_without_command=True)
|
|
70
|
+
def init(
|
|
71
|
+
config_dir: str = typer.Option(
|
|
72
|
+
".",
|
|
73
|
+
"--config-dir",
|
|
74
|
+
"-c",
|
|
75
|
+
help="Directory where configuration files will be created",
|
|
76
|
+
),
|
|
77
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Initialize a new FastAgent project with configuration files and example agent."""
|
|
80
|
+
|
|
81
|
+
config_path = Path(config_dir).resolve()
|
|
82
|
+
if not config_path.exists():
|
|
83
|
+
should_create = Confirm.ask(
|
|
84
|
+
f"Directory {config_path} does not exist. Create it?", default=True
|
|
85
|
+
)
|
|
86
|
+
if should_create:
|
|
87
|
+
config_path.mkdir(parents=True)
|
|
88
|
+
else:
|
|
89
|
+
raise typer.Abort()
|
|
90
|
+
|
|
91
|
+
# Check for existing .gitignore
|
|
92
|
+
needs_gitignore = not find_gitignore(config_path)
|
|
93
|
+
|
|
94
|
+
console.print("\n[bold]fast-agent setup[/bold]\n")
|
|
95
|
+
console.print("This will create the following files:")
|
|
96
|
+
console.print(f" - {config_path}/fastagent.config.yaml")
|
|
97
|
+
console.print(f" - {config_path}/fastagent.secrets.yaml")
|
|
98
|
+
console.print(f" - {config_path}/agent.py")
|
|
99
|
+
console.print(f" - {config_path}/pyproject.toml")
|
|
100
|
+
if needs_gitignore:
|
|
101
|
+
console.print(f" - {config_path}/.gitignore")
|
|
102
|
+
|
|
103
|
+
if not Confirm.ask("\nContinue?", default=True):
|
|
104
|
+
raise typer.Abort()
|
|
105
|
+
|
|
106
|
+
# Create configuration files
|
|
107
|
+
created = []
|
|
108
|
+
if create_file(
|
|
109
|
+
config_path / "fastagent.config.yaml", load_template_text("fastagent.config.yaml"), force
|
|
110
|
+
):
|
|
111
|
+
created.append("fastagent.yaml")
|
|
112
|
+
|
|
113
|
+
if create_file(
|
|
114
|
+
config_path / "fastagent.secrets.yaml", load_template_text("fastagent.secrets.yaml"), force
|
|
115
|
+
):
|
|
116
|
+
created.append("fastagent.secrets.yaml")
|
|
117
|
+
|
|
118
|
+
if create_file(config_path / "agent.py", load_template_text("agent.py"), force):
|
|
119
|
+
created.append("agent.py")
|
|
120
|
+
|
|
121
|
+
# Create a minimal pyproject.toml so `uv run` installs dependencies
|
|
122
|
+
def _render_pyproject(template_text: str) -> str:
|
|
123
|
+
# Determine Python requirement from installed fast-agent-mcp metadata, fall back if missing
|
|
124
|
+
py_req = ">=3.13.7"
|
|
125
|
+
try:
|
|
126
|
+
from importlib.metadata import metadata
|
|
127
|
+
|
|
128
|
+
md = metadata("fast-agent-mcp")
|
|
129
|
+
req = md.get("Requires-Python")
|
|
130
|
+
if req:
|
|
131
|
+
py_req = req
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
# Always use latest fast-agent-mcp (no version pin)
|
|
136
|
+
fast_agent_dep = '"fast-agent-mcp"'
|
|
137
|
+
|
|
138
|
+
return template_text.replace("{{python_requires}}", py_req).replace(
|
|
139
|
+
"{{fast_agent_dep}}", fast_agent_dep
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
pyproject_template = load_template_text("pyproject.toml")
|
|
143
|
+
pyproject_text = _render_pyproject(pyproject_template)
|
|
144
|
+
if create_file(config_path / "pyproject.toml", pyproject_text, force):
|
|
145
|
+
created.append("pyproject.toml")
|
|
146
|
+
|
|
147
|
+
# Only create .gitignore if none exists in parent directories
|
|
148
|
+
if needs_gitignore and create_file(
|
|
149
|
+
config_path / ".gitignore", load_template_text(".gitignore"), force
|
|
150
|
+
):
|
|
151
|
+
created.append(".gitignore")
|
|
152
|
+
|
|
153
|
+
if created:
|
|
154
|
+
console.print("\n[green]Setup completed successfully![/green]")
|
|
155
|
+
if not needs_gitignore:
|
|
156
|
+
console.print(
|
|
157
|
+
"[yellow]Note:[/yellow] Found an existing .gitignore in this or a parent directory. "
|
|
158
|
+
"Ensure it ignores 'fastagent.secrets.yaml' to avoid committing secrets."
|
|
159
|
+
)
|
|
160
|
+
if "fastagent.secrets.yaml" in created:
|
|
161
|
+
console.print("\n[yellow]Important:[/yellow] Remember to:")
|
|
162
|
+
console.print(
|
|
163
|
+
"1. Add your API keys to fastagent.secrets.yaml, or set environment variables. Use [cyan]fast-agent check[/cyan] to verify."
|
|
164
|
+
)
|
|
165
|
+
console.print(
|
|
166
|
+
"2. Keep fastagent.secrets.yaml secure and never commit it to version control"
|
|
167
|
+
)
|
|
168
|
+
console.print(
|
|
169
|
+
"3. Update fastagent.config.yaml to set a default model (currently system default is 'gpt-5-mini.low')"
|
|
170
|
+
)
|
|
171
|
+
console.print("\nTo get started, run:")
|
|
172
|
+
console.print(" uv run agent.py")
|
|
173
|
+
else:
|
|
174
|
+
console.print("\n[yellow]No files were created or modified.[/yellow]")
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
URL parsing utility for the fast-agent CLI.
|
|
3
|
+
Provides functions to parse URLs and determine MCP server configurations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import re
|
|
8
|
+
from typing import Literal
|
|
9
|
+
from urllib.parse import urlparse
|
|
10
|
+
|
|
11
|
+
from fast_agent.mcp.hf_auth import add_hf_auth_header
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_server_url(
|
|
15
|
+
url: str,
|
|
16
|
+
) -> tuple[str, Literal["http", "sse"], str]:
|
|
17
|
+
"""
|
|
18
|
+
Parse a server URL and determine the transport type and server name.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
url: The URL to parse
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Tuple containing:
|
|
25
|
+
- server_name: A generated name for the server
|
|
26
|
+
- transport_type: Either "http" or "sse" based on URL
|
|
27
|
+
- url: The parsed and validated URL
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValueError: If the URL is invalid or unsupported
|
|
31
|
+
"""
|
|
32
|
+
# Basic URL validation
|
|
33
|
+
if not url:
|
|
34
|
+
raise ValueError("URL cannot be empty")
|
|
35
|
+
|
|
36
|
+
# Parse the URL
|
|
37
|
+
parsed_url = urlparse(url)
|
|
38
|
+
|
|
39
|
+
# Ensure scheme is present and is either http or https
|
|
40
|
+
if not parsed_url.scheme or parsed_url.scheme not in ("http", "https"):
|
|
41
|
+
raise ValueError(f"URL must have http or https scheme: {url}")
|
|
42
|
+
|
|
43
|
+
# Ensure netloc (hostname) is present
|
|
44
|
+
if not parsed_url.netloc:
|
|
45
|
+
raise ValueError(f"URL must include a hostname: {url}")
|
|
46
|
+
|
|
47
|
+
# Determine transport type based on URL path
|
|
48
|
+
transport_type: Literal["http", "sse"] = "http"
|
|
49
|
+
path = parsed_url.path or ""
|
|
50
|
+
normalized_path = path.rstrip("/")
|
|
51
|
+
if normalized_path.endswith("/sse"):
|
|
52
|
+
transport_type = "sse"
|
|
53
|
+
elif not normalized_path.endswith("/mcp"):
|
|
54
|
+
# If path doesn't end with /mcp or /sse (handling trailing slash), append /mcp once
|
|
55
|
+
url = f"{url.rstrip('/')}" + "/mcp"
|
|
56
|
+
|
|
57
|
+
# Generate a server name based on hostname and port
|
|
58
|
+
server_name = generate_server_name(url)
|
|
59
|
+
|
|
60
|
+
return server_name, transport_type, url
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def generate_server_name(url: str) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Generate a unique and readable server name from a URL.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
url: The URL to generate a name for
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
A server name string
|
|
72
|
+
"""
|
|
73
|
+
parsed_url = urlparse(url)
|
|
74
|
+
|
|
75
|
+
# Extract hostname and port
|
|
76
|
+
hostname, _, port_str = parsed_url.netloc.partition(":")
|
|
77
|
+
|
|
78
|
+
# Clean the hostname for use in a server name
|
|
79
|
+
# Replace non-alphanumeric characters with underscores
|
|
80
|
+
clean_hostname = re.sub(r"[^a-zA-Z0-9]", "_", hostname)
|
|
81
|
+
|
|
82
|
+
if len(clean_hostname) > 15:
|
|
83
|
+
clean_hostname = clean_hostname[:9] + clean_hostname[-5:]
|
|
84
|
+
|
|
85
|
+
# If it's localhost or an IP, add a more unique identifier
|
|
86
|
+
if clean_hostname in ("localhost", "127_0_0_1") or re.match(r"^(\d+_){3}\d+$", clean_hostname):
|
|
87
|
+
# Use the path as part of the name for uniqueness
|
|
88
|
+
path = parsed_url.path.strip("/")
|
|
89
|
+
path = re.sub(r"[^a-zA-Z0-9]", "_", path)
|
|
90
|
+
|
|
91
|
+
# Include port if specified
|
|
92
|
+
port = f"_{port_str}" if port_str else ""
|
|
93
|
+
|
|
94
|
+
if path:
|
|
95
|
+
return f"{clean_hostname}{port}_{path[:20]}" # Limit path length
|
|
96
|
+
else:
|
|
97
|
+
# Use a hash if no path for uniqueness
|
|
98
|
+
url_hash = hashlib.md5(url.encode()).hexdigest()[:8]
|
|
99
|
+
return f"{clean_hostname}{port}_{url_hash}"
|
|
100
|
+
|
|
101
|
+
return clean_hostname
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def parse_server_urls(
|
|
105
|
+
urls_param: str, auth_token: str | None = None
|
|
106
|
+
) -> list[tuple[str, Literal["http", "sse"], str, dict[str, str] | None]]:
|
|
107
|
+
"""
|
|
108
|
+
Parse a comma-separated list of URLs into server configurations.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
urls_param: Comma-separated list of URLs
|
|
112
|
+
auth_token: Optional bearer token for authorization
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of tuples containing (server_name, transport_type, url, headers)
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ValueError: If any URL is invalid
|
|
119
|
+
"""
|
|
120
|
+
if not urls_param:
|
|
121
|
+
return []
|
|
122
|
+
|
|
123
|
+
# Split by comma and strip whitespace
|
|
124
|
+
url_list = [url.strip() for url in urls_param.split(",")]
|
|
125
|
+
|
|
126
|
+
# Prepare headers if auth token is provided
|
|
127
|
+
headers = None
|
|
128
|
+
if auth_token:
|
|
129
|
+
headers = {"Authorization": f"Bearer {auth_token}"}
|
|
130
|
+
|
|
131
|
+
# Parse each URL
|
|
132
|
+
result = []
|
|
133
|
+
for url in url_list:
|
|
134
|
+
server_name, transport_type, parsed_url = parse_server_url(url)
|
|
135
|
+
|
|
136
|
+
# Apply HuggingFace authentication if appropriate
|
|
137
|
+
final_headers = add_hf_auth_header(parsed_url, headers)
|
|
138
|
+
|
|
139
|
+
result.append((server_name, transport_type, parsed_url, final_headers))
|
|
140
|
+
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def generate_server_configs(
|
|
145
|
+
parsed_urls: list[tuple[str, Literal["http", "sse"], str, dict[str, str] | None]],
|
|
146
|
+
) -> dict[str, dict[str, str | dict[str, str]]]:
|
|
147
|
+
"""
|
|
148
|
+
Generate server configurations from parsed URLs.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
parsed_urls: List of tuples containing (server_name, transport_type, url, headers)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Dictionary of server configurations
|
|
155
|
+
"""
|
|
156
|
+
server_configs: dict[str, dict[str, str | dict[str, str]]] = {}
|
|
157
|
+
# Keep track of server name occurrences to handle collisions
|
|
158
|
+
name_counts = {}
|
|
159
|
+
|
|
160
|
+
for server_name, transport_type, url, headers in parsed_urls:
|
|
161
|
+
# Handle name collisions by adding a suffix
|
|
162
|
+
final_name = server_name
|
|
163
|
+
if server_name in server_configs:
|
|
164
|
+
# Initialize counter if we haven't seen this name yet
|
|
165
|
+
if server_name not in name_counts:
|
|
166
|
+
name_counts[server_name] = 1
|
|
167
|
+
|
|
168
|
+
# Generate a new name with suffix
|
|
169
|
+
suffix = name_counts[server_name]
|
|
170
|
+
final_name = f"{server_name}_{suffix}"
|
|
171
|
+
name_counts[server_name] += 1
|
|
172
|
+
|
|
173
|
+
# Ensure the new name is also unique
|
|
174
|
+
while final_name in server_configs:
|
|
175
|
+
suffix = name_counts[server_name]
|
|
176
|
+
final_name = f"{server_name}_{suffix}"
|
|
177
|
+
name_counts[server_name] += 1
|
|
178
|
+
|
|
179
|
+
config: dict[str, str | dict[str, str]] = {
|
|
180
|
+
"transport": transport_type,
|
|
181
|
+
"url": url,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# Add headers if provided
|
|
185
|
+
if headers:
|
|
186
|
+
config["headers"] = headers
|
|
187
|
+
|
|
188
|
+
server_configs[final_name] = config
|
|
189
|
+
|
|
190
|
+
return server_configs
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Shared constants for CLI routing and commands."""
|
|
2
|
+
|
|
3
|
+
# Options that should automatically route to the 'go' command
|
|
4
|
+
GO_SPECIFIC_OPTIONS = {
|
|
5
|
+
"--npx",
|
|
6
|
+
"--uvx",
|
|
7
|
+
"--stdio",
|
|
8
|
+
"--url",
|
|
9
|
+
"--model",
|
|
10
|
+
"--models",
|
|
11
|
+
"--instruction",
|
|
12
|
+
"-i",
|
|
13
|
+
"--message",
|
|
14
|
+
"-m",
|
|
15
|
+
"--prompt-file",
|
|
16
|
+
"-p",
|
|
17
|
+
"--servers",
|
|
18
|
+
"--auth",
|
|
19
|
+
"--name",
|
|
20
|
+
"--config-path",
|
|
21
|
+
"-c",
|
|
22
|
+
"--shell",
|
|
23
|
+
"-x",
|
|
24
|
+
"--skills",
|
|
25
|
+
"--skills-dir",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Known subcommands that should not trigger auto-routing
|
|
29
|
+
KNOWN_SUBCOMMANDS = {
|
|
30
|
+
"go",
|
|
31
|
+
"serve",
|
|
32
|
+
"setup",
|
|
33
|
+
"check",
|
|
34
|
+
"auth",
|
|
35
|
+
"bootstrap",
|
|
36
|
+
"quickstart",
|
|
37
|
+
"--help",
|
|
38
|
+
"-h",
|
|
39
|
+
"--version",
|
|
40
|
+
}
|