Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/mcp_agent/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,35 @@
}

KNOWN = {
"go",
"check",
"chat",
"dev",
"invoke",
"serve",
# Curated top-level commands
"init",
"quickstart",
"config",
"keys",
"models",
"server",
"build",
"logs",
"doctor",
"configure",
"deploy",
"login",
"whoami",
"logout",
"cloud",
# Umbrella group
"dev",
}


def main():
if len(sys.argv) > 1:
first = sys.argv[1]
if first not in KNOWN:
# Back-compat: allow `mcp-agent go ...`
if first == "go":
sys.argv.insert(1, "dev")
elif first not in KNOWN:
for i, arg in enumerate(sys.argv[1:], 1):
if arg in GO_OPTIONS or any(
arg.startswith(opt + "=") for opt in GO_OPTIONS
):
sys.argv.insert(i, "go")
# Route bare chat-like invocations to dev go (legacy behavior)
sys.argv.insert(i, "dev")
sys.argv.insert(i + 1, "go")
break
app()

Expand Down
64 changes: 47 additions & 17 deletions src/mcp_agent/cli/commands/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
attach_stdio_servers,
attach_url_servers,
load_user_app,
detect_default_script,
select_servers_from_config,
)
from mcp_agent.cli.utils.url_parser import generate_server_configs, parse_server_urls
from mcp_agent.workflows.factory import create_llm
from mcp_agent.agents.agent import Agent
from mcp_agent.config import get_settings


app = typer.Typer(help="Ephemeral REPL for quick iteration")
Expand Down Expand Up @@ -99,14 +102,17 @@ def chat(
npx: Optional[str] = typer.Option(None, "--npx"),
uvx: Optional[str] = typer.Option(None, "--uvx"),
stdio: Optional[str] = typer.Option(None, "--stdio"),
script: Optional[Path] = typer.Option(Path("agent.py"), "--script"),
script: Optional[Path] = typer.Option(None, "--script"),
list_servers: bool = typer.Option(False, "--list-servers"),
list_tools: bool = typer.Option(False, "--list-tools"),
list_resources: bool = typer.Option(False, "--list-resources"),
server: Optional[str] = typer.Option(
None, "--server", help="Filter to a single server"
),
) -> None:
# Resolve script with auto-detection
script = detect_default_script(script)

server_list = servers_csv.split(",") if servers_csv else None

url_servers = None
Expand Down Expand Up @@ -140,12 +146,21 @@ def chat(
else:
server_list.extend(list(stdio_servers.keys()))

# Smart defaults for servers
resolved_server_list = select_servers_from_config(
servers_csv, url_servers, stdio_servers
)

Comment on lines +149 to +153
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Smart server defaults wired — good. One functional inconsistency remains.

/prompt still uses server_list instead of resolved_server_list, so dynamic/configured servers are ignored there.

-                                prompt_msgs = await ag.create_prompt(
+                                prompt_msgs = await ag.create_prompt(
                                     prompt_name=prompt_name,
                                     arguments=arguments,
-                                    server_names=server_list or [],
+                                    server_names=resolved_server_list or [],
                                 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Smart defaults for servers
resolved_server_list = select_servers_from_config(
servers_csv, url_servers, stdio_servers
)
# Smart defaults for servers
resolved_server_list = select_servers_from_config(
servers_csv, url_servers, stdio_servers
)
prompt_msgs = await ag.create_prompt(
prompt_name=prompt_name,
arguments=arguments,
server_names=resolved_server_list or [],
)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/chat.py around lines 149-153, the code computes
resolved_server_list but the /prompt handler still references server_list,
causing dynamic/configured servers to be ignored; update the /prompt command
handler to use resolved_server_list everywhere server_list is currently used
(pass resolved_server_list into any function calls, closures or command
construction), and add a safe fallback to resolved_server_list if server_list
may be None so the dynamic/configured server selection is honored.

💡 Verification agent

🧩 Analysis chain

Quick sweep for any other legacy server_list usages.

Run this to ensure no other call paths still rely on server_list for server_names.


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -nP --type=py -C2 '\bserver_names\s*=\s*server_list\b' || true
rg -nP --type=py -C2 '\bserver_names\s*=\s*server_list\s*\|\|\s*\[\]' || true
rg -nP --type=py -C2 'create_prompt\([^)]*server_names\s*=' || true

Length of output: 1248


Replace remaining legacy server_list usages with resolved_server_list.

rg found three call sites still passing server_list; update them to pass the resolved list (or call select_servers_from_config in that scope) and preserve or [] defaults where appropriate.

  • src/mcp_agent/cli/commands/invoke.py:65 — server_names=server_list
  • src/mcp_agent/cli/commands/go.py:113 — server_names=server_list or []
  • src/mcp_agent/cli/commands/chat.py:591 — server_names=server_list or []
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/chat.py around lines 149-153, you've already
assigned resolved_server_list from select_servers_from_config; replace the
remaining legacy server_list usages in the codebase to use that resolved list:
update src/mcp_agent/cli/commands/invoke.py at ~line 65 to pass
server_names=resolved_server_list (or call select_servers_from_config there to
obtain it), update src/mcp_agent/cli/commands/go.py at ~line 113 to pass
server_names=resolved_server_list or [], and update
src/mcp_agent/cli/commands/chat.py at ~line 591 to pass
server_names=resolved_server_list or []; preserve the existing "or []" defaults
where present.

# Listing mode (no generation)
if list_servers or list_tools or list_resources:
try:

async def _list():
app_obj = load_user_app(script or Path("agent.py"))
# Disable progress display for cleaner listing output
settings = get_settings()
if settings.logger:
settings.logger.progress_display = False
app_obj = load_user_app(script, settings_override=settings)
await app_obj.initialize()
attach_url_servers(app_obj, url_servers)
attach_stdio_servers(app_obj, stdio_servers)
Expand All @@ -163,7 +178,7 @@ async def _list():
agent = Agent(
name="chat-lister",
instruction="You list tools and resources",
server_names=target_servers,
server_names=resolved_server_list or target_servers,
context=app_obj.context,
)
Comment on lines +181 to 183
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Honor --server filter in Agent.server_names.

If resolved_server_list is non-empty, it currently overrides a provided --server. Prefer the explicit filter.

Apply:

-                        server_names=resolved_server_list or target_servers,
+                        server_names=([server] if server else (resolved_server_list or target_servers)),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
server_names=resolved_server_list or target_servers,
context=app_obj.context,
)
server_names=([server] if server else (resolved_server_list or target_servers)),
context=app_obj.context,
)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/chat.py around lines 181-183, the server_names
assignment currently uses "resolved_server_list or target_servers", which lets
the resolved list override an explicit --server filter; change the logic so the
explicit target_servers (the --server filter) takes precedence by using
"target_servers or resolved_server_list" (or equivalent truthy check), ensuring
that when a --server value is provided it is honoured, falling back to
resolved_server_list only when target_servers is empty/None.

async with agent:
Expand Down Expand Up @@ -203,7 +218,11 @@ async def _list():
):

async def _parallel_repl():
app_obj = load_user_app(script or Path("agent.py"))
# Disable progress display for cleaner multi-model REPL
settings = get_settings()
if settings.logger:
settings.logger.progress_display = False
app_obj = load_user_app(script, settings_override=settings)
await app_obj.initialize()
attach_url_servers(app_obj, url_servers)
attach_stdio_servers(app_obj, stdio_servers)
Expand All @@ -227,7 +246,7 @@ async def _parallel_repl():
provider = prov_guess
llm = create_llm(
agent_name=m,
server_names=server_list or [],
server_names=resolved_server_list or [],
provider=(provider or "openai"),
model=m,
context=app_obj.context,
Expand Down Expand Up @@ -276,7 +295,9 @@ async def _parallel_repl():
ag = _Agent(
name="chat-lister",
instruction="list tools",
server_names=[srv] if srv else (server_list or []),
server_names=[srv]
if srv
else (resolved_server_list or []),
context=app_obj.context,
)
async with ag:
Expand All @@ -294,7 +315,9 @@ async def _parallel_repl():
ag = _Agent(
name="chat-lister",
instruction="list resources",
server_names=[srv] if srv else (server_list or []),
server_names=[srv]
if srv
else (resolved_server_list or []),
context=app_obj.context,
)
async with ag:
Expand Down Expand Up @@ -367,8 +390,8 @@ async def _gen(llm_instance):
try:
out = asyncio.run(
_run_single_model(
script=script or Path("agent.py"),
servers=server_list,
script=script,
servers=resolved_server_list,
url_servers=url_servers,
stdio_servers=stdio_servers,
model=m,
Expand All @@ -392,9 +415,12 @@ async def _gen(llm_instance):
and not models
and not (list_servers or list_tools or list_resources)
):
# Interactive loop
# Interactive loop - disable progress display for cleaner REPL experience
async def _repl():
app_obj = load_user_app(script or Path("agent.py"))
settings = get_settings()
if settings.logger:
settings.logger.progress_display = False
app_obj = load_user_app(script, settings_override=settings)
await app_obj.initialize()
attach_url_servers(app_obj, url_servers)
attach_stdio_servers(app_obj, stdio_servers)
Expand All @@ -416,7 +442,7 @@ async def _repl():
provider = model_id.split(":", 1)[0]
llm = create_llm(
agent_name=(name or "chat"),
server_names=server_list or [],
server_names=resolved_server_list or [],
provider=(provider or "openai"),
model=model_id,
context=app_obj.context,
Expand Down Expand Up @@ -476,7 +502,7 @@ async def _repl():
# Recreate LLM with new model
llm_local = create_llm(
agent_name=(name or "chat"),
server_names=server_list or [],
server_names=resolved_server_list or [],
provider=(prov or "openai"),
model=model_id,
context=app_obj.context,
Expand All @@ -502,7 +528,9 @@ async def _repl():
ag = Agent(
name="chat-lister",
instruction="list tools",
server_names=[srv] if srv else (server_list or []),
server_names=[srv]
if srv
else (resolved_server_list or []),
context=app_obj.context,
)
async with ag:
Expand All @@ -521,7 +549,9 @@ async def _repl():
ag = Agent(
name="chat-lister",
instruction="list resources",
server_names=[srv] if srv else (server_list or []),
server_names=[srv]
if srv
else (resolved_server_list or []),
context=app_obj.context,
)
async with ag:
Expand Down Expand Up @@ -698,8 +728,8 @@ async def _repl():
else:
out = asyncio.run(
_run_single_model(
script=script or Path("agent.py"),
servers=server_list,
script=script,
servers=resolved_server_list,
url_servers=url_servers,
stdio_servers=stdio_servers,
model=model,
Expand Down
6 changes: 5 additions & 1 deletion src/mcp_agent/cli/commands/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
from rich.console import Console

from mcp_agent.config import get_settings
from mcp_agent.cli.core.utils import detect_default_script


app = typer.Typer(help="Run app locally with diagnostics")
console = Console()


@app.callback(invoke_without_command=True)
def dev(script: Path = typer.Option(Path("agent.py"), "--script")) -> None:
def dev(script: Path = typer.Option(None, "--script")) -> None:
"""Run the user's app script with optional live reload and preflight checks."""

def _preflight_ok() -> bool:
Expand All @@ -49,6 +50,9 @@ def _run_script() -> subprocess.Popen:
stdin=None, # Inherit stdin
)

# Resolve script path with auto-detection (main.py preferred)
script = detect_default_script(script)

# Simple preflight
_ = _preflight_ok()

Expand Down
2 changes: 1 addition & 1 deletion src/mcp_agent/cli/commands/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ def doctor() -> None:
"• Add API key: [cyan]mcp-agent keys set <provider> <key>[/cyan]\n"
"• Add server: [cyan]mcp-agent server add recipe filesystem[/cyan]\n"
"• Start chat: [cyan]mcp-agent chat --model anthropic.haiku[/cyan]\n"
"• Run agent: [cyan]mcp-agent dev --script agent.py[/cyan]",
"• Run agent: [cyan]mcp-agent dev start --script main.py[/cyan]",
title="Getting Started",
border_style="dim",
)
Expand Down
16 changes: 13 additions & 3 deletions src/mcp_agent/cli/commands/go.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
attach_stdio_servers,
attach_url_servers,
load_user_app,
detect_default_script,
select_servers_from_config,
)
from mcp_agent.cli.utils.url_parser import generate_server_configs, parse_server_urls
from mcp_agent.workflows.factory import create_llm
Expand Down Expand Up @@ -268,8 +270,11 @@ def go(
npx: Optional[str] = typer.Option(None, "--npx"),
uvx: Optional[str] = typer.Option(None, "--uvx"),
stdio: Optional[str] = typer.Option(None, "--stdio"),
script: Optional[Path] = typer.Option(Path("agent.py"), "--script"),
script: Optional[Path] = typer.Option(None, "--script"),
) -> None:
# Resolve script with auto-detection
script = detect_default_script(script)

# Parse server names from config if provided
server_list = servers.split(",") if servers else None

Expand Down Expand Up @@ -302,6 +307,11 @@ def go(
else:
server_list.extend(list(stdio_servers.keys()))

# Smart defaults from config if still unspecified
resolved_server_list = select_servers_from_config(
",".join(server_list) if server_list else None, url_servers, stdio_servers
)

# Multi-model support if comma-separated
if model and "," in model:
models = [m.strip() for m in model.split(",") if m.strip()]
Expand All @@ -311,7 +321,7 @@ def go(
asyncio.run(
_run_agent(
app_script=script,
server_list=server_list,
server_list=resolved_server_list,
model=m,
message=message,
prompt_file=prompt_file,
Expand All @@ -331,7 +341,7 @@ def go(
asyncio.run(
_run_agent(
app_script=script,
server_list=server_list,
server_list=resolved_server_list,
model=model,
message=message,
prompt_file=prompt_file,
Expand Down
Loading
Loading