diff --git a/docs/en/concepts/cli.mdx b/docs/en/concepts/cli.mdx
index 2935b52a4..c6724fc06 100644
--- a/docs/en/concepts/cli.mdx
+++ b/docs/en/concepts/cli.mdx
@@ -404,6 +404,10 @@ crewai config reset
After resetting configuration, re-run `crewai login` to authenticate again.
+
+CrewAI CLI handles authentication to the Tool Repository automatically when adding packages to your project. Just append `crewai` before any `uv` command to use it. E.g. `crewai uv add requests`. For more information, see [Tool Repository](https://docs.crewai.com/enterprise/features/tool-repository) docs.
+
+
Configuration settings are stored in `~/.config/crewai/settings.json`. Some settings like organization name and UUID are read-only and managed through authentication and organization commands. Tool repository related settings are hidden and cannot be set directly by users.
diff --git a/docs/en/enterprise/features/tool-repository.mdx b/docs/en/enterprise/features/tool-repository.mdx
index 9a578e117..7ec7ff346 100644
--- a/docs/en/enterprise/features/tool-repository.mdx
+++ b/docs/en/enterprise/features/tool-repository.mdx
@@ -52,6 +52,36 @@ researcher = Agent(
)
```
+## Adding other packages after installing a tool
+
+After installing a tool from the CrewAI Enterprise Tool Repository, you need to use the `crewai uv` command to add other packages to your project.
+Using pure `uv` commands will fail due to authentication to tool repository being handled by the CLI. By using the `crewai uv` command, you can add other packages to your project without having to worry about authentication.
+Any `uv` command can be used with the `crewai uv` command, making it a powerful tool for managing your project's dependencies without the hassle of managing authentication through environment variables or other methods.
+
+Say that you have installed a custom tool from the CrewAI Enterprise Tool Repository called "my-tool":
+
+```bash
+crewai tool install my-tool
+```
+
+And now you want to add another package to your project, you can use the following command:
+
+```bash
+crewai uv add requests
+```
+
+Other commands like `uv sync` or `uv remove` can also be used with the `crewai uv` command:
+
+```bash
+crewai uv sync
+```
+
+```bash
+crewai uv remove requests
+```
+
+This will add the package to your project and update `pyproject.toml` accordingly.
+
## Creating and Publishing Tools
To create a new tool project:
diff --git a/src/crewai/cli/cli.py b/src/crewai/cli/cli.py
index b9bf7147b..991082de0 100644
--- a/src/crewai/cli/cli.py
+++ b/src/crewai/cli/cli.py
@@ -1,3 +1,5 @@
+import os
+import subprocess
from importlib.metadata import version as get_version
import click
@@ -8,6 +10,7 @@ from crewai.cli.create_crew import create_crew
from crewai.cli.create_flow import create_flow
from crewai.cli.crew_chat import run_chat
from crewai.cli.settings.main import SettingsCommand
+from crewai.cli.utils import build_env_with_tool_repository_credentials, read_toml
from crewai.memory.storage.kickoff_task_outputs_storage import (
KickoffTaskOutputsSQLiteStorage,
)
@@ -34,6 +37,46 @@ def crewai():
"""Top-level command group for crewai."""
+@crewai.command(
+ name="uv",
+ context_settings=dict(
+ ignore_unknown_options=True,
+ ),
+)
+@click.argument("uv_args", nargs=-1, type=click.UNPROCESSED)
+def uv(uv_args):
+ """A wrapper around uv commands that adds custom tool authentication through env vars."""
+ env = os.environ.copy()
+ try:
+ pyproject_data = read_toml()
+ sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
+
+ for source_config in sources.values():
+ if isinstance(source_config, dict):
+ index = source_config.get("index")
+ if index:
+ index_env = build_env_with_tool_repository_credentials(index)
+ env.update(index_env)
+ except (FileNotFoundError, KeyError) as e:
+ raise SystemExit(
+ "Error. A valid pyproject.toml file is required. Check that a valid pyproject.toml file exists in the current directory."
+ ) from e
+ except Exception as e:
+ raise SystemExit(f"Error: {e}") from e
+
+ try:
+ subprocess.run( # noqa: S603
+ ["uv", *uv_args], # noqa: S607
+ capture_output=False,
+ env=env,
+ text=True,
+ check=True,
+ )
+ except subprocess.CalledProcessError as e:
+ click.secho(f"uv command failed with exit code {e.returncode}", fg="red")
+ raise SystemExit(e.returncode) from e
+
+
@crewai.command()
@click.argument("type", type=click.Choice(["crew", "flow"]))
@click.argument("name")
@@ -239,11 +282,6 @@ def deploy():
"""Deploy the Crew CLI group."""
-@crewai.group()
-def tool():
- """Tool Repository related commands."""
-
-
@deploy.command(name="create")
@click.option("-y", "--yes", is_flag=True, help="Skip the confirmation prompt")
def deploy_create(yes: bool):
@@ -291,6 +329,11 @@ def deploy_remove(uuid: str | None):
deploy_cmd.remove_crew(uuid=uuid)
+@crewai.group()
+def tool():
+ """Tool Repository related commands."""
+
+
@tool.command(name="create")
@click.argument("handle")
def tool_create(handle: str):
diff --git a/src/crewai/cli/tools/main.py b/src/crewai/cli/tools/main.py
index 25cf89ee8..a7fc718c7 100644
--- a/src/crewai/cli/tools/main.py
+++ b/src/crewai/cli/tools/main.py
@@ -12,6 +12,7 @@ from crewai.cli import git
from crewai.cli.command import BaseCommand, PlusAPIMixin
from crewai.cli.config import Settings
from crewai.cli.utils import (
+ build_env_with_tool_repository_credentials,
extract_available_exports,
get_project_description,
get_project_name,
@@ -42,8 +43,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
if project_root.exists():
click.secho(f"Folder {folder_name} already exists.", fg="red")
raise SystemExit
- else:
- os.makedirs(project_root)
+ os.makedirs(project_root)
click.secho(f"Creating custom tool {folder_name}...", fg="green", bold=True)
@@ -56,7 +56,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
os.chdir(project_root)
try:
self.login()
- subprocess.run(["git", "init"], check=True)
+ subprocess.run(["git", "init"], check=True) # noqa: S607
console.print(
f"[green]Created custom tool [bold]{folder_name}[/bold]. Run [bold]cd {project_root}[/bold] to start working.[/green]"
)
@@ -76,10 +76,10 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
raise SystemExit()
project_name = get_project_name(require=True)
- assert isinstance(project_name, str)
+ assert isinstance(project_name, str) # noqa: S101
project_version = get_project_version(require=True)
- assert isinstance(project_version, str)
+ assert isinstance(project_version, str) # noqa: S101
project_description = get_project_description(require=False)
encoded_tarball = None
@@ -94,8 +94,8 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
self._print_current_organization()
with tempfile.TemporaryDirectory() as temp_build_dir:
- subprocess.run(
- ["uv", "build", "--sdist", "--out-dir", temp_build_dir],
+ subprocess.run( # noqa: S603
+ ["uv", "build", "--sdist", "--out-dir", temp_build_dir], # noqa: S607
check=True,
capture_output=False,
)
@@ -146,7 +146,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
style="bold red",
)
raise SystemExit
- elif get_response.status_code != 200:
+ if get_response.status_code != 200:
console.print(
"Failed to get tool details. Please try again later.", style="bold red"
)
@@ -196,10 +196,10 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
else:
add_package_command.extend(["--index", index, tool_handle])
- add_package_result = subprocess.run(
+ add_package_result = subprocess.run( # noqa: S603
add_package_command,
capture_output=False,
- env=self._build_env_with_credentials(repository_handle),
+ env=build_env_with_tool_repository_credentials(repository_handle),
text=True,
check=True,
)
@@ -221,20 +221,6 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
)
raise SystemExit
- def _build_env_with_credentials(self, repository_handle: str):
- repository_handle = repository_handle.upper().replace("-", "_")
- settings = Settings()
-
- env = os.environ.copy()
- env[f"UV_INDEX_{repository_handle}_USERNAME"] = str(
- settings.tool_repository_username or ""
- )
- env[f"UV_INDEX_{repository_handle}_PASSWORD"] = str(
- settings.tool_repository_password or ""
- )
-
- return env
-
def _print_current_organization(self) -> None:
settings = Settings()
if settings.org_uuid:
diff --git a/src/crewai/cli/utils.py b/src/crewai/cli/utils.py
index 764af9d2f..fc0ad7ab3 100644
--- a/src/crewai/cli/utils.py
+++ b/src/crewai/cli/utils.py
@@ -11,6 +11,7 @@ import click
import tomli
from rich.console import Console
+from crewai.cli.config import Settings
from crewai.cli.constants import ENV_VARS
from crewai.crew import Crew
from crewai.flow import Flow
@@ -417,6 +418,21 @@ def extract_available_exports(dir_path: str = "src"):
raise SystemExit(1) from e
+def build_env_with_tool_repository_credentials(repository_handle: str):
+ repository_handle = repository_handle.upper().replace("-", "_")
+ settings = Settings()
+
+ env = os.environ.copy()
+ env[f"UV_INDEX_{repository_handle}_USERNAME"] = str(
+ settings.tool_repository_username or ""
+ )
+ env[f"UV_INDEX_{repository_handle}_PASSWORD"] = str(
+ settings.tool_repository_password or ""
+ )
+
+ return env
+
+
def _load_tools_from_init(init_file: Path) -> list[dict[str, Any]]:
"""
Load and validate tools from a given __init__.py file.