Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 0 additions & 2 deletions src/mcp_agent/cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
invoke,
serve,
init,
quickstart,
config,
keys,
models,
Expand All @@ -31,7 +30,6 @@
"invoke",
"serve",
"init",
"quickstart",
"config",
"keys",
"models",
Expand Down
235 changes: 222 additions & 13 deletions src/mcp_agent/cli/commands/init.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""
Project scaffolding: mcp-agent init (scaffold minimal version).
Project scaffolding: mcp-agent init (scaffold minimal version or copy curated examples).
"""

from __future__ import annotations

import shutil
from pathlib import Path
from importlib import resources
from importlib.resources import files as _pkg_files

import typer
from rich.console import Console
Expand All @@ -14,6 +16,10 @@

app = typer.Typer(help="Scaffold a new mcp-agent project")
console = Console()
err_console = Console(stderr=True)

# Path to repository examples
EXAMPLE_ROOT = Path(__file__).parents[4] / "examples"
Copy link
Member

Choose a reason for hiding this comment

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

Can we use absolute path here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was taken from the old quickstart implementation. Not sure how we can have an absolute path from the repo root?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, this won't work. The quickstart samples need to ship with the package. i.e. look at how init materializes the basic agent from the data directory and use that logic.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, ok. This was confusing because we have the init command which uses the src/data/examples and src/data/templates to generate init projects from the mcp-agent src. This here is copying from the repo itself so it won't work if you run it in your own project folder. But looks like this is how quickstart currently does it (I just tried it in a new project dir and it fails for each of them).

So is the change needed here just to duplicate those examples under src/data/examples or try and fetch the examples from the repo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just duplicated them under data/examples in a followup PR



def _load_template(template_name: str) -> str:
Expand Down Expand Up @@ -74,11 +80,70 @@ def _write_readme(dir_path: Path, content: str, force: bool) -> str | None:
return None


def _copy_tree(src: Path, dst: Path, force: bool) -> int:
"""Copy a directory tree from src to dst.

Returns 1 on success, 0 on failure.
"""
if not src.exists():
err_console.print(f"[red]Source not found: {src}[/red]")
return 0
try:
if dst.exists():
if force:
shutil.rmtree(dst)
else:
return 0
shutil.copytree(src, dst)
return 1
except Exception as e:
err_console.print(f"[red]Error copying tree: {e}[/red]")
return 0


def _copy_pkg_tree(pkg_rel: str, dst: Path, force: bool) -> int:
"""Copy packaged examples from mcp_agent.data/examples/<pkg_rel> into dst.

Uses importlib.resources to locate files installed with the package.
Returns 1 on success, 0 on failure.
"""
try:
root = (
_pkg_files("mcp_agent")
.joinpath("data")
.joinpath("examples")
.joinpath(pkg_rel)
)
except Exception:
return 0
if not root.exists():
return 0

Comment on lines +110 to +121
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bug: importlib.resources Traversable has no .exists(); use .is_dir() and handle existing dst.

Calling root.exists() on a Traversable will raise AttributeError at runtime. Also, mirror _copy_tree semantics when dst exists.

Apply:

 def _copy_pkg_tree(pkg_rel: str, dst: Path, force: bool) -> int:
@@
-    try:
-        root = (
-            _pkg_files("mcp_agent")
-            .joinpath("data")
-            .joinpath("examples")
-            .joinpath(pkg_rel)
-        )
-    except Exception:
-        return 0
-    if not root.exists():
-        return 0
+    try:
+        root = (
+            _pkg_files("mcp_agent")
+            .joinpath("data")
+            .joinpath("examples")
+            .joinpath(pkg_rel)
+        )
+    except Exception:
+        return 0
+    # Traversable doesn't define `.exists()`; use `.is_dir()`
+    if not root.is_dir():
+        return 0
+
+    # Align behavior with _copy_tree when destination already exists
+    if dst.exists():
+        if force:
+            shutil.rmtree(dst)
+        else:
+            return 0
📝 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
try:
root = (
_pkg_files("mcp_agent")
.joinpath("data")
.joinpath("examples")
.joinpath(pkg_rel)
)
except Exception:
return 0
if not root.exists():
return 0
try:
root = (
_pkg_files("mcp_agent")
.joinpath("data")
.joinpath("examples")
.joinpath(pkg_rel)
)
except Exception:
return 0
# Traversable doesn't define `.exists()`; use `.is_dir()`
if not root.is_dir():
return 0
# Align behavior with _copy_tree when destination already exists
if dst.exists():
if force:
shutil.rmtree(dst)
else:
return 0
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/init.py around lines 110 to 121, the code calls
root.exists() on an importlib.resources Traversable which raises AttributeError;
replace that check with root.is_dir() (or root.is_file() as appropriate) and
ensure you treat a non-directory as absent; additionally, when copying the tree
to the destination, mirror the semantics of _copy_tree by handling an existing
dst (e.g., merge files, skip existing files, or overwrite according to the
project's policy) rather than unconditionally failing—update the copy logic to
check for dst existence and perform the merge/skip/overwrite behavior used by
_copy_tree.

# Mirror directory tree
def _copy_any(node, target: Path):
if node.is_dir():
target.mkdir(parents=True, exist_ok=True)
for child in node.iterdir():
_copy_any(child, target / child.name)
else:
if target.exists() and not force:
return
with node.open("rb") as rf:
data = rf.read()
target.parent.mkdir(parents=True, exist_ok=True)
with open(target, "wb") as wf:
wf.write(data)

_copy_any(root, dst)
return 1


@app.callback(invoke_without_command=True)
def init(
ctx: typer.Context,
dir: Path = typer.Option(Path("."), "--dir", "-d", help="Target directory"),
template: str = typer.Option("basic", "--template", "-t", help="Template to use"),
quickstart: str = typer.Option(None, "--quickstart", help="Quickstart mode: copy example without config files"),
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing files"),
no_gitignore: bool = typer.Option(
False, "--no-gitignore", help="Skip creating .gitignore"
Expand All @@ -87,34 +152,122 @@ def init(
False, "--list", "-l", help="List available templates"
),
) -> None:
"""Initialize a new MCP-Agent project with configuration and example files."""
"""Initialize a new MCP-Agent project with configuration and example files.

Use --template for full project initialization with config files.
Use --quickstart for copying examples only."""

# Available templates with descriptions
templates = {
# Organized into scaffolding templates and full example templates
scaffolding_templates = {
"basic": "Simple agent with filesystem and fetch capabilities",
"server": "MCP server with workflow and parallel agents",
"token": "Token counting example with monitoring",
"factory": "Agent factory with router-based selection",
"minimal": "Minimal configuration files only",
}

example_templates = {
"workflow": "Workflow examples (from examples/workflows)",
"researcher": "MCP researcher use case (from examples/usecases/mcp_researcher)",
"data-analysis": "Financial data analysis example",
"state-transfer": "Workflow router with state transfer",
"mcp-basic-agent": "Basic MCP agent example",
"token-counter": "Token counting with monitoring",
"agent-factory": "Agent factory pattern",
"basic-agent-server": "Basic agent server (asyncio)",
"reference-agent-server": "Reference agent server implementation",
"elicitation": "Elicitation server example",
"sampling": "Sampling server example",
"notifications": "Notifications server example",
}

Comment on lines +170 to +184
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unlisted quickstarts: examples present in mapping but not shown with --list.

hello-world, mcp, temporal, chatgpt-app are selectable via mapping but omitted from example_templates. Add them so users can discover them.

Apply:

     example_templates = {
         "workflow": "Workflow examples (from examples/workflows)",
         "researcher": "MCP researcher use case (from examples/usecases/mcp_researcher)",
         "data-analysis": "Financial data analysis example",
         "state-transfer": "Workflow router with state transfer",
         "mcp-basic-agent": "Basic MCP agent example",
         "token-counter": "Token counting with monitoring",
         "agent-factory": "Agent factory pattern",
         "basic-agent-server": "Basic agent server (asyncio)",
         "reference-agent-server": "Reference agent server implementation",
         "elicitation": "Elicitation server example",
         "sampling": "Sampling server example",
         "notifications": "Notifications server example",
+        "hello-world": "Cloud hello world example",
+        "mcp": "Cloud MCP example",
+        "temporal": "Cloud Temporal example",
+        "chatgpt-app": "Cloud ChatGPT app example",
     }
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/init.py around lines 170 to 184, the
example_templates mapping is missing entries for quickstarts that exist
elsewhere (hello-world, mcp, temporal, chatgpt-app); update the
example_templates dict to include those four keys with descriptive values (e.g.,
"Hello World quickstart", "MCP quickstart", "Temporal quickstart", "ChatGPT app
quickstart") so they appear when users run --list.

templates = {**scaffolding_templates, **example_templates}

if list_templates:
console.print("\n[bold]Available Templates:[/bold]\n")
table = Table(show_header=True, header_style="cyan")
table.add_column("Template", style="green")
table.add_column("Description")

for name, desc in templates.items():
table.add_row(name, desc)
# Templates table
console.print("[bold cyan]Templates:[/bold cyan]")
console.print("[dim]Creates minimal project structure with config files[/dim]\n")
table1 = Table(show_header=True, header_style="cyan")
table1.add_column("Template", style="green")
table1.add_column("Description")
for name, desc in scaffolding_templates.items():
table1.add_row(name, desc)
console.print(table1)

# Quickstart templates table
console.print("\n[bold cyan]Quickstart Templates:[/bold cyan]")
console.print("[dim]Copies complete example projects[/dim]\n")
table2 = Table(show_header=True, header_style="cyan")
table2.add_column("Template", style="green")
table2.add_column("Description")
for name, desc in example_templates.items():
table2.add_row(name, desc)
console.print(table2)

console.print(table)
console.print("\n[dim]Use: mcp-agent init --template <name>[/dim]")
return

if ctx.invoked_subcommand:
return

# Validate template
if quickstart:
if quickstart not in example_templates:
console.print(f"[red]Unknown quickstart example: {quickstart}[/red]")
console.print(f"Available examples: {', '.join(example_templates.keys())}")
console.print("[dim]Use --list to see all available templates[/dim]")
raise typer.Exit(1)

example_map = {
"workflow": (EXAMPLE_ROOT / "workflows", "workflow"),
"researcher": (EXAMPLE_ROOT / "usecases" / "mcp_researcher", "researcher"),
"data-analysis": (EXAMPLE_ROOT / "usecases" / "mcp_financial_analyzer", "data-analysis"),
"state-transfer": (EXAMPLE_ROOT / "workflows" / "workflow_router", "state-transfer"),
"basic-agent-server": (EXAMPLE_ROOT / "mcp_agent_server" / "asyncio", "basic_agent_server"),
"mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
"token-counter": (None, "token_counter", "basic/token_counter"),
"agent-factory": (None, "agent_factory", "basic/agent_factory"),
"reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
"elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
"sampling": (None, "sampling", "mcp_agent_server/sampling"),
"notifications": (None, "notifications", "mcp_agent_server/notifications"),
"hello-world": (EXAMPLE_ROOT / "cloud" / "hello_world", "hello_world"),
"mcp": (EXAMPLE_ROOT / "cloud" / "mcp", "mcp"),
"temporal": (EXAMPLE_ROOT / "cloud" / "temporal", "temporal"),
"chatgpt-app": (EXAMPLE_ROOT / "cloud" / "chatgpt_app", "chatgpt_app"),
}
Comment on lines +223 to +240
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Repo path reliance breaks in installed wheels; use packaged examples for all entries.

Several quickstarts only reference EXAMPLE_ROOT; those won’t exist when the package is installed. Prefer packaged resources first, then fall back to repo paths in dev.

Apply:

-        example_map = {
-            "workflow": (EXAMPLE_ROOT / "workflows", "workflow"),
-            "researcher": (EXAMPLE_ROOT / "usecases" / "mcp_researcher", "researcher"),
-            "data-analysis": (EXAMPLE_ROOT / "usecases" / "mcp_financial_analyzer", "data-analysis"),
-            "state-transfer": (EXAMPLE_ROOT / "workflows" / "workflow_router", "state-transfer"),
-            "basic-agent-server": (EXAMPLE_ROOT / "mcp_agent_server" / "asyncio", "basic_agent_server"),
-            "mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
-            "token-counter": (None, "token_counter", "basic/token_counter"),
-            "agent-factory": (None, "agent_factory", "basic/agent_factory"),
-            "reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
-            "elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
-            "sampling": (None, "sampling", "mcp_agent_server/sampling"),
-            "notifications": (None, "notifications", "mcp_agent_server/notifications"),
-            "hello-world": (EXAMPLE_ROOT / "cloud" / "hello_world", "hello_world"),
-            "mcp": (EXAMPLE_ROOT / "cloud" / "mcp", "mcp"),
-            "temporal": (EXAMPLE_ROOT / "cloud" / "temporal", "temporal"),
-            "chatgpt-app": (EXAMPLE_ROOT / "cloud" / "chatgpt_app", "chatgpt_app"),
-        }
+        example_map = {
+            # Prefer packaged examples; repo fallback happens below if needed
+            "workflow": (None, "workflow", "workflows"),
+            "researcher": (None, "researcher", "usecases/mcp_researcher"),
+            "data-analysis": (None, "data-analysis", "usecases/mcp_financial_analyzer"),
+            "state-transfer": (None, "state-transfer", "workflows/workflow_router"),
+            "basic-agent-server": (None, "basic_agent_server", "mcp_agent_server/asyncio"),
+            "mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
+            "token-counter": (None, "token_counter", "basic/token_counter"),
+            "agent-factory": (None, "agent_factory", "basic/agent_factory"),
+            "reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
+            "elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
+            "sampling": (None, "sampling", "mcp_agent_server/sampling"),
+            "notifications": (None, "notifications", "mcp_agent_server/notifications"),
+            "hello-world": (None, "hello_world", "cloud/hello_world"),
+            "mcp": (None, "mcp", "cloud/mcp"),
+            "temporal": (None, "temporal", "cloud/temporal"),
+            "chatgpt-app": (None, "chatgpt_app", "cloud/chatgpt_app"),
+        }

Committable suggestion skipped: line range outside the PR's diff.


mapping = example_map.get(quickstart)
if not mapping:
console.print(f"[red]Quickstart example '{quickstart}' not found[/red]")
raise typer.Exit(1)

base_dir = dir.resolve()
base_dir.mkdir(parents=True, exist_ok=True)

if len(mapping) == 3:
_, dst_name, pkg_rel = mapping
dst = base_dir / dst_name
copied = _copy_pkg_tree(pkg_rel, dst, force)
if not copied:
src = EXAMPLE_ROOT / pkg_rel.replace("/", "_")
if src.exists():
copied = _copy_tree(src, dst, force)
else:
Comment on lines +250 to +258
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback to repo path uses an incorrect join.

pkg_rel.replace("/", "_") won’t match the repo layout; join the original segments under EXAMPLE_ROOT.

Apply:

-            if not copied:
-                src = EXAMPLE_ROOT / pkg_rel.replace("/", "_")
+            if not copied:
+                # Dev fallback: repo examples mirror the packaged relative path
+                src = EXAMPLE_ROOT / Path(pkg_rel)
                 if src.exists():
                     copied = _copy_tree(src, dst, force)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/init.py around lines 250 to 258, the fallback
constructs src with EXAMPLE_ROOT / pkg_rel.replace("/", "_") which produces the
wrong path; split pkg_rel on "/" and join the segments under EXAMPLE_ROOT (e.g.
EXAMPLE_ROOT.joinpath(*pkg_rel.split("/")) or EXAMPLE_ROOT /
Path(*pkg_rel.split("/"))) so the repo layout is preserved, then check
src.exists() and use _copy_tree(src, dst, force) as before.

src, dst_name = mapping
dst = base_dir / dst_name
copied = _copy_tree(src, dst, force)

if copied:
console.print(f"Copied {copied} set(s) to {dst}")
else:
console.print(f"[yellow]Could not copy '{quickstart}' - destination may already exist[/yellow]")
console.print("Use --force to overwrite")

return

if template not in templates:
console.print(f"[red]Unknown template: {template}[/red]")
console.print(f"Available templates: {', '.join(templates.keys())}")
Expand Down Expand Up @@ -150,7 +303,61 @@ def init(
if gitignore_content and _write(gitignore_path, gitignore_content, force):
files_created.append(".gitignore")

# Create template-specific files
# Handle example templates (copy from repository or package)
if template in example_templates:
# Map template names to their source paths
# Format: "name": (repo_path, dest_name) for repo examples
# "name": (None, dest_name, pkg_rel) for packaged examples
example_map = {
"workflow": (EXAMPLE_ROOT / "workflows", "workflow"),
"researcher": (EXAMPLE_ROOT / "usecases" / "mcp_researcher", "researcher"),
"data-analysis": (EXAMPLE_ROOT / "usecases" / "mcp_financial_analyzer", "data-analysis"),
"state-transfer": (EXAMPLE_ROOT / "workflows" / "workflow_router", "state-transfer"),
"basic-agent-server": (EXAMPLE_ROOT / "mcp_agent_server" / "asyncio", "basic_agent_server"),
"mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
"token-counter": (None, "token_counter", "basic/token_counter"),
"agent-factory": (None, "agent_factory", "basic/agent_factory"),
"reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
"elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
"sampling": (None, "sampling", "mcp_agent_server/sampling"),
"notifications": (None, "notifications", "mcp_agent_server/notifications"),
"hello-world": (EXAMPLE_ROOT / "cloud" / "hello_world", "hello_world"),
"mcp": (EXAMPLE_ROOT / "cloud" / "mcp", "mcp"),
"temporal": (EXAMPLE_ROOT / "cloud" / "temporal", "temporal"),
"chatgpt-app": (EXAMPLE_ROOT / "cloud" / "chatgpt_app", "chatgpt_app"),
}
Comment on lines +306 to +328
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same packaging issue for --template flow; mirror the quickstart fix.

The template example_map also relies on EXAMPLE_ROOT for many entries. Use packaged paths with repo fallback.

Apply:

-        example_map = {
-            "workflow": (EXAMPLE_ROOT / "workflows", "workflow"),
-            "researcher": (EXAMPLE_ROOT / "usecases" / "mcp_researcher", "researcher"),
-            "data-analysis": (EXAMPLE_ROOT / "usecases" / "mcp_financial_analyzer", "data-analysis"),
-            "state-transfer": (EXAMPLE_ROOT / "workflows" / "workflow_router", "state-transfer"),
-            "basic-agent-server": (EXAMPLE_ROOT / "mcp_agent_server" / "asyncio", "basic_agent_server"),
-            "mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
-            "token-counter": (None, "token_counter", "basic/token_counter"),
-            "agent-factory": (None, "agent_factory", "basic/agent_factory"),
-            "reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
-            "elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
-            "sampling": (None, "sampling", "mcp_agent_server/sampling"),
-            "notifications": (None, "notifications", "mcp_agent_server/notifications"),
-            "hello-world": (EXAMPLE_ROOT / "cloud" / "hello_world", "hello_world"),
-            "mcp": (EXAMPLE_ROOT / "cloud" / "mcp", "mcp"),
-            "temporal": (EXAMPLE_ROOT / "cloud" / "temporal", "temporal"),
-            "chatgpt-app": (EXAMPLE_ROOT / "cloud" / "chatgpt_app", "chatgpt_app"),
-        }
+        example_map = {
+            "workflow": (None, "workflow", "workflows"),
+            "researcher": (None, "researcher", "usecases/mcp_researcher"),
+            "data-analysis": (None, "data-analysis", "usecases/mcp_financial_analyzer"),
+            "state-transfer": (None, "state-transfer", "workflows/workflow_router"),
+            "basic-agent-server": (None, "basic_agent_server", "mcp_agent_server/asyncio"),
+            "mcp-basic-agent": (None, "mcp_basic_agent", "basic/mcp_basic_agent"),
+            "token-counter": (None, "token_counter", "basic/token_counter"),
+            "agent-factory": (None, "agent_factory", "basic/agent_factory"),
+            "reference-agent-server": (None, "reference_agent_server", "mcp_agent_server/reference"),
+            "elicitation": (None, "elicitation", "mcp_agent_server/elicitation"),
+            "sampling": (None, "sampling", "mcp_agent_server/sampling"),
+            "notifications": (None, "notifications", "mcp_agent_server/notifications"),
+            "hello-world": (None, "hello_world", "cloud/hello_world"),
+            "mcp": (None, "mcp", "cloud/mcp"),
+            "temporal": (None, "temporal", "cloud/temporal"),
+            "chatgpt-app": (None, "chatgpt_app", "cloud/chatgpt_app"),
+        }
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/init.py around lines 306 to 328, the example_map
currently hardcodes many entries to EXAMPLE_ROOT rather than using packaged
resource paths with a repository fallback (same bug fixed for quickstart);
update the mapping so entries prefer packaged paths (i.e., use tuples of the
form (None, dest_name, pkg_rel) for packaged examples) and for repo-only
examples keep the EXAMPLE_ROOT tuples, then adjust the code that resolves
examples to first try loading from the package path and only fall back to
EXAMPLE_ROOT if the packaged resource is not present, ensuring parity with the
quickstart fix approach.


mapping = example_map.get(template)
if not mapping:
console.print(f"[red]Example template '{template}' not found[/red]")
raise typer.Exit(1)

if len(mapping) == 3:
_, dst_name, pkg_rel = mapping
dst = dir / dst_name
copied = _copy_pkg_tree(pkg_rel, dst, force)
if not copied:
src = EXAMPLE_ROOT / pkg_rel.replace("/", "_")
if src.exists():
copied = _copy_tree(src, dst, force)
else:
Comment on lines +340 to +343
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same incorrect fallback join as quickstart.

Use EXAMPLE_ROOT / Path(pkg_rel), not underscore replacement.

Apply:

-            if not copied:
-                src = EXAMPLE_ROOT / pkg_rel.replace("/", "_")
+            if not copied:
+                src = EXAMPLE_ROOT / Path(pkg_rel)
                 if src.exists():
                     copied = _copy_tree(src, dst, force)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/commands/init.py around lines 340 to 343, the code
incorrectly builds the example path using pkg_rel.replace("/", "_"); change it
to use a Path join such as EXAMPLE_ROOT / Path(pkg_rel) so the directory
structure is preserved. Replace the underscore-replacement expression with
Path(pkg_rel) (ensure pathlib.Path is imported or already available), keep the
existing exists() check and _copy_tree call, and remove the replace("/") usage.

src, dst_name = mapping
dst = dir / dst_name
copied = _copy_tree(src, dst, force)

if copied:
console.print(f"\n[green]✅ Successfully copied example '{template}'![/green]")
console.print(f"Created: [cyan]{dst}[/cyan]\n")
console.print("[bold]Next steps:[/bold]")
console.print(f"1. cd [cyan]{dst}[/cyan]")
console.print("2. Review the README for instructions")
console.print("3. Add your API keys to config/secrets files if needed")
else:
console.print(f"[yellow]Example '{template}' could not be copied[/yellow]")
console.print("The destination may already exist. Use --force to overwrite.")

return

if template == "basic":
# Determine entry script name and handle existing files
script_name = "main.py"
Expand Down Expand Up @@ -278,7 +485,7 @@ def init(
console.print("4. Run the factory: [cyan]uv run factory.py[/cyan]")
elif template == "minimal":
console.print("3. Create your agent script")
console.print(" See examples: [cyan]mcp-agent quickstart[/cyan]")
console.print(" See examples: [cyan]mcp-agent init --list[/cyan]")

console.print(
"\n[dim]Run [cyan]mcp-agent doctor[/cyan] to check your configuration[/dim]"
Expand Down Expand Up @@ -346,10 +553,12 @@ def interactive(
console.print(f"\n[bold]Creating project '{project_name}'...[/bold]")

# Use the main init function with selected options
ctx = typer.Context(init)
init(
ctx=typer.Context(),
ctx=ctx,
dir=dir,
template=template_name,
quickstart=None,
force=False,
no_gitignore=False,
list_templates=False,
Expand Down
Loading
Loading