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
5 changes: 3 additions & 2 deletions examples/tensorzero/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ services:

mcp-server:
build:
context: ../..
dockerfile: examples/tensorzero/mcp_server/Dockerfile
context: ./mcp_server
dockerfile: Dockerfile
working_dir: /app
volumes:
- ./mcp_server:/app
ports:
Expand Down
1 change: 1 addition & 0 deletions examples/tensorzero/fastagent.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ default_model: haiku

tensorzero:
base_url: http://localhost:3000
api_key: verysecret

logger:
level: "info"
Expand Down
1 change: 1 addition & 0 deletions examples/tensorzero/mcp_server/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
19 changes: 8 additions & 11 deletions examples/tensorzero/mcp_server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
FROM python:3.12-slim

WORKDIR /app

RUN apt-get update && apt-get install -y curl wget && \
Expand All @@ -9,20 +8,18 @@ RUN apt-get update && apt-get install -y curl wget && \

RUN pip install uv

COPY pyproject.toml /app/
COPY uv.lock /app/
COPY LICENSE /app/
COPY README.md /app/
# Copy dependency files only
COPY pyproject.toml /tmp/
#COPY uv.lock /tmp/

# Install dependencies
WORKDIR /tmp
RUN uv pip install --system .

COPY examples/tensorzero/mcp_server/mcp_server.py /app/
COPY examples/tensorzero/mcp_server/entrypoint.sh /app/entrypoint.sh

RUN chmod +x /app/entrypoint.sh
# Switch back to /app (this will be overridden by volume mount)
WORKDIR /app

# These files will come from the volume mount at runtime
EXPOSE 8000

ENTRYPOINT ["/app/entrypoint.sh"]

CMD ["uvicorn", "mcp_server:app", "--host", "0.0.0.0", "--port", "8000"]
11 changes: 11 additions & 0 deletions examples/tensorzero/mcp_server/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "mcp-server"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastmcp>=2.10.6",
"starlette>=0.46.2",
"uvicorn>=0.34.3",
]
1 change: 1 addition & 0 deletions hatch_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def initialize(self, version: str, build_data: Dict[str, Any]) -> None:
# examples/mcp/state-transfer -> src/mcp_agent/resources/examples/mcp/state-transfer
"examples/mcp/state-transfer": "src/mcp_agent/resources/examples/mcp/state-transfer",
"examples/mcp/elicitations": "src/mcp_agent/resources/examples/mcp/elicitations",
"examples/tensorzero": "src/mcp_agent/resources/examples/tensorzero",
}

print("Fast-agent build: Copying examples to resources...")
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,6 @@ ignore=["E501"]
# More lenient settings for existing code
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["ANN"] # Don't require type annotations in tests

[tool.uv.workspace]
members = ["examples/tensorzero/mcp_server"]
107 changes: 107 additions & 0 deletions src/mcp_agent/cli/commands/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,25 @@
],
"create_subdir": True,
},
"tensorzero": {
"description": "A complete example showcasing the TensorZero integration.\n"
"Includes the T0 Gateway, an MCP server, an interactive agent, and \n"
"multi-modal functionality.",
"files": [
".env.sample",
"Makefile",
"README.md",
"agent.py",
"docker-compose.yml",
"fastagent.config.yaml",
"image_demo.py",
"simple_agent.py",
"mcp_server/",
"demo_images/",
"tensorzero_config/"
],
"create_subdir": True,
},
}


Expand Down Expand Up @@ -225,6 +244,27 @@ def copy_example_files(example_type: str, target_dir: Path, force: bool = False)
return created


def copy_project_template(source_dir: Path, dest_dir: Path, console: Console, force: bool = False):
"""
Recursively copies a project template directory.
This is a helper to handle project-based quickstarts like TensorZero.
"""
if dest_dir.exists():
if force:
console.print(f"[yellow]--force specified. Removing existing directory: {dest_dir}[/yellow]")
shutil.rmtree(dest_dir)
else:
console.print(f"[bold yellow]Directory '{dest_dir.name}' already exists.[/bold yellow] Use --force to overwrite.")
return False

try:
shutil.copytree(source_dir, dest_dir)
return True
except Exception as e:
console.print(f"[red]Error copying project template: {e}[/red]")
return False


def show_overview() -> None:
"""Display an overview of available examples in a nicely formatted table."""
console.print("\n[bold cyan]fast-agent quickstarts[/bold cyan]")
Expand Down Expand Up @@ -397,6 +437,73 @@ def _show_completion_message(example_type: str, created: list[str]) -> None:
console.print("\n[yellow]No files were created.[/yellow]")


@app.command(name="tensorzero", help="Create the TensorZero integration example project.")
def tensorzero(
directory: Path = typer.Argument(
Path("."),
help="Directory where the 'tensorzero' project folder will be created.",
),
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite if project directory exists"),
):
"""Create the TensorZero project example."""
console.print("[bold green]Setting up the TensorZero quickstart example...[/bold green]")

dest_project_dir = directory.resolve() / "tensorzero"

# --- Find Source Directory ---
from importlib.resources import files
try:
# This path MUST match the "to" path from hatch_build.py
source_dir = files("mcp_agent").joinpath("resources").joinpath("examples").joinpath("tensorzero")
if not source_dir.is_dir():
raise FileNotFoundError # Fallback to dev mode if resource isn't a dir
except (ImportError, ModuleNotFoundError, FileNotFoundError):
console.print("[yellow]Package resources not found. Falling back to development mode.[/yellow]")
# This path is relative to the project root in a development environment
source_dir = Path(__file__).parent.parent.parent.parent / "examples" / "tensorzero"

if not source_dir.exists() or not source_dir.is_dir():
console.print(f"[red]Error: Source project directory not found at '{source_dir}'[/red]")
raise typer.Exit(1)

console.print(f"Source directory: [dim]{source_dir}[/dim]")
console.print(f"Destination: [dim]{dest_project_dir}[/dim]")

# --- Copy Project and Show Message ---
if copy_project_template(source_dir, dest_project_dir, console, force):
console.print(
f"\n[bold green]✅ Success![/bold green] Your TensorZero project has been created in: [cyan]{dest_project_dir}[/cyan]"
)
console.print("\n[bold yellow]Next Steps:[/bold yellow]")
console.print("\n1. [bold]Navigate to your new project directory:[/bold]")
console.print(f" [cyan]cd {dest_project_dir.relative_to(Path.cwd())}[/cyan]")

console.print("\n2. [bold]Set up your API keys:[/bold]")
console.print(" [cyan]cp .env.sample .env[/cyan]")
console.print(
" [dim]Then, open the new '.env' file and add your OpenAI or Anthropic API key.[/dim]"
)

console.print("\n3. [bold]Start the required services (TensorZero Gateway & MCP Server):[/bold]")
console.print(" [cyan]docker compose up --build -d[/cyan]")
console.print(
" [dim](This builds and starts the necessary containers in the background)[/dim]"
)

console.print("\n4. [bold]Run the interactive agent:[/bold]")
console.print(" [cyan]make agent[/cyan] (or `uv run agent.py`)")
console.print("\nEnjoy exploring the TensorZero integration with fast-agent! ✨")


@app.command(name="t0", help="Alias for the TensorZero quickstart.", hidden=True)
def t0_alias(
directory: Path = typer.Argument(Path("."), help="Directory for the 'tensorzero' project folder."),
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite"),
):
"""Alias for the `tensorzero` command."""
tensorzero(directory, force)


@app.callback(invoke_without_command=True)
def main(ctx: typer.Context) -> None:
"""Quickstart applications for fast-agent."""
Expand Down
6 changes: 3 additions & 3 deletions src/mcp_agent/llm/model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from mcp_agent.llm.providers.augmented_llm_groq import GroqAugmentedLLM
from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
from mcp_agent.llm.providers.augmented_llm_openrouter import OpenRouterAugmentedLLM
from mcp_agent.llm.providers.augmented_llm_tensorzero import TensorZeroAugmentedLLM
from mcp_agent.llm.providers.augmented_llm_tensorzero_openai import TensorZeroOpenAIAugmentedLLM
from mcp_agent.llm.providers.augmented_llm_xai import XAIAugmentedLLM
from mcp_agent.mcp.interfaces import AugmentedLLMProtocol

Expand All @@ -39,7 +39,7 @@
Type[SlowLLM],
Type[DeepSeekAugmentedLLM],
Type[OpenRouterAugmentedLLM],
Type[TensorZeroAugmentedLLM],
Type[TensorZeroOpenAIAugmentedLLM],
Type[GoogleNativeAugmentedLLM],
Type[GenericAugmentedLLM],
Type[AzureOpenAIAugmentedLLM],
Expand Down Expand Up @@ -157,7 +157,7 @@ class ModelFactory:
Provider.GOOGLE: GoogleNativeAugmentedLLM,
Provider.XAI: XAIAugmentedLLM,
Provider.OPENROUTER: OpenRouterAugmentedLLM,
Provider.TENSORZERO: TensorZeroAugmentedLLM,
Provider.TENSORZERO: TensorZeroOpenAIAugmentedLLM,
Provider.AZURE: AzureOpenAIAugmentedLLM,
Provider.ALIYUN: AliyunAugmentedLLM,
Provider.BEDROCK: BedrockAugmentedLLM,
Expand Down
Loading
Loading