Improve .env discovery (#737)
Resolves TOO-201 Documentation PR for this is here: https://github.com/ArcadeAI/docs/pull/626 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes how environment variables/secrets are discovered and loaded, which can subtly alter runtime behavior depending on directory structure and existing env vars; bounded traversal and added tests reduce but don’t eliminate this risk. > > **Overview** > **Improves `.env` discovery across the MCP server and CLI.** Adds `find_env_file()` (bounded by the nearest `pyproject.toml` by default) and switches settings loading, `arcade deploy`, `arcade configure` stdio env injection, and provider API-key resolution to use it. > > Updates dev reload to also watch the discovered `.env` even when it lives outside the current working directory, adjusts `deploy --secrets all` to only run when a `.env` was found, and moves the minimal scaffold’s `.env.example` to the project root with updated tests/integration checks. Version bumps align examples and top-level deps with `arcade-mcp-server` `1.17.4` and `arcade-mcp` `1.11.2`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 40cff1738c14674ce01f09fd325ece9c874cd072. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c50699d5e6
commit
4a737b9710
20 changed files with 292 additions and 44 deletions
|
|
@ -4,13 +4,13 @@ version = "0.1.0"
|
|||
description = "MCP Server created with Arcade.dev"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"arcade-mcp-server>=1.12.0,<2.0.0",
|
||||
"arcade-mcp-server>=1.17.4,<2.0.0",
|
||||
"httpx>=0.28.0,<1.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"arcade-mcp[all]>=1.5.2,<2.0.0",
|
||||
"arcade-mcp[all]>=1.11.2,<2.0.0",
|
||||
"pytest>=7.0.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"mypy>=1.0.0",
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ version = "0.1.0"
|
|||
description = "MCP Server created with Arcade.dev"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"arcade-mcp-server>=1.5.0,<2.0.0",
|
||||
"arcade-mcp-server>=1.17.4,<2.0.0",
|
||||
"httpx>=0.28.0,<1.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"arcade-mcp[all]>=1.4.0,<2.0.0",
|
||||
"arcade-mcp[all]>=1.11.2,<2.0.0",
|
||||
"pytest>=7.0.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"mypy>=1.0.0",
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ version = "0.1.0"
|
|||
description = "MCP Server created with Arcade.dev"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"arcade-mcp-server>=1.5.0,<2.0.0",
|
||||
"arcade-mcp-server>=1.17.4,<2.0.0",
|
||||
"httpx>=0.28.0,<1.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"arcade-mcp[all]>=1.4.0,<2.0.0",
|
||||
"arcade-mcp[all]>=1.11.2,<2.0.0",
|
||||
"pytest>=7.0.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"mypy>=1.0.0",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import subprocess
|
|||
from pathlib import Path
|
||||
|
||||
import typer
|
||||
from arcade_mcp_server.settings import find_env_file
|
||||
from dotenv import dotenv_values
|
||||
|
||||
from arcade_cli.console import console
|
||||
|
|
@ -204,10 +205,16 @@ def is_uv_installed() -> bool:
|
|||
|
||||
|
||||
def get_tool_secrets() -> dict:
|
||||
"""Only useful for stdio servers, because HTTP servers load in envvars at runtime"""
|
||||
# TODO: Allow for a custom .env file to be used
|
||||
env_path = Path.cwd() / ".env"
|
||||
if env_path.exists():
|
||||
"""Get tool secrets from .env file for stdio servers.
|
||||
|
||||
Discovers .env file by traversing upward from the current directory
|
||||
through parent directories until a .env file is found.
|
||||
|
||||
Returns:
|
||||
Dictionary of environment variables from the .env file, or empty dict if not found.
|
||||
"""
|
||||
env_path = find_env_file()
|
||||
if env_path is not None:
|
||||
return dotenv_values(env_path)
|
||||
return {}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from arcade_core.subprocess_utils import (
|
|||
get_windows_no_window_creationflags,
|
||||
graceful_terminate_process,
|
||||
)
|
||||
from arcade_mcp_server.settings import find_env_file
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import BaseModel, Field
|
||||
from rich.columns import Columns
|
||||
|
|
@ -817,14 +818,14 @@ def deploy_server_logic(
|
|||
)
|
||||
console.print(f"✓ Entrypoint file found at {entrypoint_path}", style="green")
|
||||
|
||||
# Step 3: Load .env file from current directory if it exists
|
||||
console.print("\nLoading .env file from current directory if it exists...", style="dim")
|
||||
env_path = current_dir / ".env"
|
||||
if env_path.exists():
|
||||
# Step 3: Load .env file if it exists (searches upward through parent directories)
|
||||
console.print("\nSearching for .env file...", style="dim")
|
||||
env_path = find_env_file()
|
||||
if env_path is not None:
|
||||
load_dotenv(env_path, override=False)
|
||||
console.print(f"✓ Loaded environment from {env_path}", style="green")
|
||||
else:
|
||||
console.print(f"[!] No .env file found at {env_path}", style="yellow")
|
||||
console.print("[!] No .env file found in current or parent directories", style="yellow")
|
||||
|
||||
# Step 4: Verify server and extract metadata (or skip if --skip-validate)
|
||||
required_secrets_from_validation: set[str] = set()
|
||||
|
|
@ -860,7 +861,7 @@ def deploy_server_logic(
|
|||
|
||||
if secrets == "skip":
|
||||
console.print("\n[!] Skipping secret upload (--secrets skip)", style="yellow")
|
||||
elif secrets == "all":
|
||||
elif secrets == "all" and env_path is not None:
|
||||
console.print("\nUploading ALL secrets from .env file...", style="dim")
|
||||
secrets_to_upsert = set(load_env_file(str(env_path)).keys())
|
||||
if secrets_to_upsert:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# Environment variables for {{ toolkit_name }} MCP server
|
||||
#
|
||||
# Copy this file to .env and fill in your values:
|
||||
# cp .env.example .env
|
||||
#
|
||||
# The .env file will be automatically discovered when running your server,
|
||||
# even from subdirectories like src/{{ toolkit_name }}/.
|
||||
#
|
||||
# IMPORTANT: Never commit your .env file to version control!
|
||||
|
||||
# Example secret used by the whisper_secret tool
|
||||
# Replace with your actual secret value
|
||||
MY_SECRET_KEY="Your tools can have secrets injected at runtime!"
|
||||
|
|
@ -1 +0,0 @@
|
|||
MY_SECRET_KEY="Your tools can have secrets injected at runtime!"
|
||||
|
|
@ -26,6 +26,7 @@ from arcade_core.discovery import (
|
|||
from arcade_core.errors import ToolkitLoadError
|
||||
from arcade_core.network.org_transport import build_org_scoped_http_client
|
||||
from arcade_core.schema import ToolDefinition
|
||||
from arcade_mcp_server.settings import find_env_file
|
||||
from arcadepy import (
|
||||
NOT_GIVEN,
|
||||
APIConnectionError,
|
||||
|
|
@ -1123,9 +1124,9 @@ def resolve_provider_api_key(provider: Provider, provider_api_key: str | None =
|
|||
if api_key:
|
||||
return api_key
|
||||
|
||||
# Then check .env file in current working directory
|
||||
env_file_path = Path.cwd() / ".env"
|
||||
if env_file_path.exists():
|
||||
# Then check .env file by traversing upward through parent directories
|
||||
env_file_path = find_env_file()
|
||||
if env_file_path is not None:
|
||||
load_dotenv(env_file_path, override=False)
|
||||
api_key = os.getenv(env_var_name)
|
||||
if api_key:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from arcade_mcp_server.exceptions import ServerError
|
|||
from arcade_mcp_server.logging_utils import intercept_standard_logging
|
||||
from arcade_mcp_server.resource_server.base import ResourceServerValidator
|
||||
from arcade_mcp_server.server import MCPServer
|
||||
from arcade_mcp_server.settings import MCPSettings, ServerSettings
|
||||
from arcade_mcp_server.settings import MCPSettings, ServerSettings, find_env_file
|
||||
from arcade_mcp_server.types import Prompt, PromptMessage, Resource
|
||||
from arcade_mcp_server.usage import ServerTracker
|
||||
from arcade_mcp_server.worker import create_arcade_mcp, serve_with_force_quit
|
||||
|
|
@ -367,7 +367,7 @@ class MCPApp:
|
|||
This method runs as the parent process that watches for file changes
|
||||
and spawns/restarts child processes to run the actual server.
|
||||
"""
|
||||
env_file_path = Path.cwd() / ".env"
|
||||
env_file_path = find_env_file()
|
||||
|
||||
def start_server_process() -> subprocess.Popen:
|
||||
"""Start a child process running the server."""
|
||||
|
|
@ -414,9 +414,17 @@ class MCPApp:
|
|||
try:
|
||||
|
||||
def watch_filter(change: Any, path: str) -> bool:
|
||||
return path.endswith(".py") or (Path(path) == env_file_path)
|
||||
# Watch Python files and the .env file (if one was found)
|
||||
return path.endswith(".py") or (
|
||||
env_file_path is not None and Path(path) == env_file_path
|
||||
)
|
||||
|
||||
for changes in watch(".", watch_filter=watch_filter):
|
||||
# Watch current directory, plus the .env file if it's outside cwd
|
||||
paths_to_watch: list[str] = ["."]
|
||||
if env_file_path is not None:
|
||||
paths_to_watch.append(str(env_file_path))
|
||||
|
||||
for changes in watch(*paths_to_watch, watch_filter=watch_filter):
|
||||
logger.info(f"Detected changes in {len(changes)} file(s), restarting server...")
|
||||
shutdown_server_process(process, reason="reload")
|
||||
process = start_server_process()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,86 @@ from pydantic import Field, field_validator
|
|||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
def _find_project_root(start_dir: Path) -> Path | None:
|
||||
"""Find the nearest ancestor directory containing pyproject.toml.
|
||||
|
||||
This is used as a default boundary for upward directory traversal
|
||||
to prevent accidentally loading files from unrelated parent directories.
|
||||
|
||||
Args:
|
||||
start_dir: Directory to start searching from (must be resolved).
|
||||
|
||||
Returns:
|
||||
Path to the project root directory, or None if no pyproject.toml is found.
|
||||
"""
|
||||
current = start_dir
|
||||
while True:
|
||||
if (current / "pyproject.toml").is_file():
|
||||
return current
|
||||
parent = current.parent
|
||||
if parent == current:
|
||||
return None
|
||||
current = parent
|
||||
|
||||
|
||||
def find_env_file(
|
||||
start_dir: Path | None = None,
|
||||
stop_at: Path | None = None,
|
||||
filename: str = ".env",
|
||||
) -> Path | None:
|
||||
"""Find a .env file by traversing upward through parent directories.
|
||||
|
||||
Starts at the specified directory (or current working directory) and
|
||||
traverses upward through parent directories until a .env file is found
|
||||
or a boundary is reached.
|
||||
|
||||
By default, traversal stops at the nearest ancestor directory containing
|
||||
``pyproject.toml`` (the project root). This prevents accidentally loading
|
||||
an unrelated ``.env`` file from ``~/`` or other parent directories.
|
||||
Pass an explicit ``stop_at`` to override this behavior.
|
||||
|
||||
Args:
|
||||
start_dir: Directory to start searching from. Defaults to current working directory.
|
||||
stop_at: Directory to stop traversal at (inclusive). If specified, the search
|
||||
will not continue past this directory. The stop_at directory itself
|
||||
is still checked for the .env file. When not specified, the nearest
|
||||
ancestor containing ``pyproject.toml`` is used as the boundary.
|
||||
filename: Name of the env file to find. Defaults to ".env".
|
||||
|
||||
Returns:
|
||||
Path to the .env file if found, None otherwise.
|
||||
|
||||
Example:
|
||||
# Find .env starting from current directory (bounded by pyproject.toml)
|
||||
env_path = find_env_file()
|
||||
|
||||
# Find .env starting from a specific directory
|
||||
env_path = find_env_file(start_dir=Path("/path/to/project/src"))
|
||||
|
||||
# Find .env but don't search above a specific directory
|
||||
env_path = find_env_file(stop_at=Path("/path/to/project"))
|
||||
"""
|
||||
current = start_dir or Path.cwd()
|
||||
current = current.resolve()
|
||||
|
||||
stop_at = stop_at.resolve() if stop_at is not None else _find_project_root(current)
|
||||
|
||||
while True:
|
||||
env_path = current / filename
|
||||
if env_path.is_file():
|
||||
return env_path
|
||||
|
||||
if stop_at is not None and current == stop_at:
|
||||
return None
|
||||
|
||||
parent = current.parent
|
||||
if parent == current:
|
||||
# We've reached the filesystem root
|
||||
return None
|
||||
|
||||
current = parent
|
||||
|
||||
|
||||
class NotificationSettings(BaseSettings):
|
||||
"""Notification-related settings."""
|
||||
|
||||
|
|
@ -308,16 +388,17 @@ class MCPSettings(BaseSettings):
|
|||
def from_env(cls) -> "MCPSettings":
|
||||
"""Create settings from environment variables.
|
||||
|
||||
Automatically loads .env file from current directory if it exists,
|
||||
then creates settings from the combined environment.
|
||||
Automatically discovers and loads .env file by traversing upward from
|
||||
the current directory through parent directories until a .env file is
|
||||
found or the filesystem root is reached.
|
||||
|
||||
The .env file is loaded with override=False, meaning existing
|
||||
environment variables take precedence. Multiple calls are safe
|
||||
environment variables take precedence. Multiple calls are safe.
|
||||
"""
|
||||
from dotenv import load_dotenv
|
||||
|
||||
env_path = Path.cwd() / ".env"
|
||||
if env_path.exists():
|
||||
env_path = find_env_file()
|
||||
if env_path is not None:
|
||||
load_dotenv(env_path, override=False)
|
||||
|
||||
return cls()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "arcade-mcp-server"
|
||||
version = "1.17.3"
|
||||
version = "1.17.4"
|
||||
description = "Model Context Protocol (MCP) server framework for Arcade.dev"
|
||||
readme = "README.md"
|
||||
authors = [{ name = "Arcade.dev" }]
|
||||
|
|
|
|||
111
libs/tests/arcade_mcp_server/test_env_discovery.py
Normal file
111
libs/tests/arcade_mcp_server/test_env_discovery.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
"""Tests for find_env_file() upward directory traversal."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from arcade_mcp_server.settings import find_env_file
|
||||
|
||||
|
||||
class TestFindEnvFile:
|
||||
"""Test the find_env_file() utility function."""
|
||||
|
||||
def test_finds_env_in_current_directory(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should find .env file in cwd."""
|
||||
env_file = tmp_path / ".env"
|
||||
env_file.write_text("TEST_VAR=value")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
assert find_env_file() == env_file
|
||||
|
||||
def test_finds_env_in_parent_directory(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should traverse upward to find .env in parent (no pyproject.toml boundary)."""
|
||||
subdir = tmp_path / "a" / "b" / "c"
|
||||
subdir.mkdir(parents=True)
|
||||
env_file = tmp_path / ".env"
|
||||
env_file.write_text("TEST_VAR=value")
|
||||
monkeypatch.chdir(subdir)
|
||||
|
||||
assert find_env_file() == env_file
|
||||
|
||||
def test_prefers_closest_env_file(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should find closest .env when multiple exist."""
|
||||
subdir = tmp_path / "subdir"
|
||||
subdir.mkdir()
|
||||
(tmp_path / ".env").write_text("ROOT=1")
|
||||
closer_env = subdir / ".env"
|
||||
closer_env.write_text("CLOSER=1")
|
||||
monkeypatch.chdir(subdir)
|
||||
|
||||
assert find_env_file() == closer_env
|
||||
|
||||
def test_returns_none_when_not_found(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should return None when no .env exists."""
|
||||
subdir = tmp_path / "subdir"
|
||||
subdir.mkdir()
|
||||
monkeypatch.chdir(subdir)
|
||||
|
||||
assert find_env_file() is None
|
||||
|
||||
def test_stop_at_limits_traversal(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Explicit stop_at should prevent traversing past specified directory."""
|
||||
project = tmp_path / "project" / "src"
|
||||
project.mkdir(parents=True)
|
||||
(tmp_path / ".env").write_text("OUTSIDE=1")
|
||||
monkeypatch.chdir(project)
|
||||
|
||||
assert find_env_file(stop_at=tmp_path / "project") is None
|
||||
|
||||
def test_stops_at_pyproject_toml_boundary(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should not traverse past directory containing pyproject.toml."""
|
||||
project_root = tmp_path / "project"
|
||||
project_root.mkdir()
|
||||
(project_root / "pyproject.toml").write_text("[project]\nname = 'test'")
|
||||
src = project_root / "src"
|
||||
src.mkdir()
|
||||
# .env is above the project root — should NOT be found
|
||||
(tmp_path / ".env").write_text("OUTSIDE=1")
|
||||
monkeypatch.chdir(src)
|
||||
|
||||
assert find_env_file() is None
|
||||
|
||||
def test_finds_env_at_pyproject_toml_level(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should find .env at the same level as pyproject.toml."""
|
||||
project_root = tmp_path / "project"
|
||||
project_root.mkdir()
|
||||
(project_root / "pyproject.toml").write_text("[project]\nname = 'test'")
|
||||
env_file = project_root / ".env"
|
||||
env_file.write_text("SECRET=value")
|
||||
src = project_root / "src"
|
||||
src.mkdir()
|
||||
monkeypatch.chdir(src)
|
||||
|
||||
assert find_env_file() == env_file
|
||||
|
||||
def test_finds_env_below_pyproject_toml(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should find .env in a subdirectory within the project."""
|
||||
project_root = tmp_path / "project"
|
||||
project_root.mkdir()
|
||||
(project_root / "pyproject.toml").write_text("[project]\nname = 'test'")
|
||||
src = project_root / "src"
|
||||
src.mkdir()
|
||||
env_file = src / ".env"
|
||||
env_file.write_text("SECRET=value")
|
||||
monkeypatch.chdir(src)
|
||||
|
||||
assert find_env_file() == env_file
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
"""Tests for get_tool_secrets() in arcade configure."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import types
|
||||
|
|
@ -10,6 +12,7 @@ from arcade_cli.configure import (
|
|||
_resolve_windows_appdata,
|
||||
_warn_overwrite,
|
||||
configure_client,
|
||||
get_tool_secrets,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -30,6 +33,28 @@ def _assert_stdio_entry(entry: dict) -> None:
|
|||
assert "env" in entry
|
||||
|
||||
|
||||
def test_get_tool_secrets_loads_from_env_file(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should load secrets from .env file."""
|
||||
env_file = tmp_path / ".env"
|
||||
env_file.write_text("SECRET_ONE=value1\nSECRET_TWO=value2")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
secrets = get_tool_secrets()
|
||||
assert secrets.get("SECRET_ONE") == "value1"
|
||||
assert secrets.get("SECRET_TWO") == "value2"
|
||||
|
||||
|
||||
def test_get_tool_secrets_returns_empty_when_no_env(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Should return empty dict when no .env exists."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
assert get_tool_secrets() == {}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _format_path_for_display()
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ def test_create_new_toolkit_minimal_with_spaces(tmp_path: Path) -> None:
|
|||
server_root = output_dir / "my_server"
|
||||
assert (server_root / "pyproject.toml").is_file()
|
||||
assert (server_root / "src" / "my_server" / "server.py").is_file()
|
||||
assert (server_root / "src" / "my_server" / ".env.example").is_file()
|
||||
assert (server_root / ".env.example").is_file()
|
||||
|
||||
|
||||
def test_create_new_toolkit_minimal_prints_next_steps(tmp_path: Path) -> None:
|
||||
|
|
|
|||
|
|
@ -41,21 +41,23 @@ def pytest_collection_modifyitems(config, items):
|
|||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def disable_usage_tracking():
|
||||
"""Disable CLI usage tracking for all tests.
|
||||
def isolate_environment():
|
||||
"""Isolate environment variables for each test.
|
||||
|
||||
This prevents test runs from sending analytics events to PostHog.
|
||||
The fixture is autouse=True so it applies automatically to every test.
|
||||
This fixture captures the entire environment before a test and restores it
|
||||
after. This ensures that environment variables set by load_dotenv() or any
|
||||
other mechanism during tests don't leak into subsequent tests.
|
||||
|
||||
This also disables CLI usage tracking to prevent test runs from sending
|
||||
analytics events to PostHog.
|
||||
"""
|
||||
original_value = os.environ.get("ARCADE_USAGE_TRACKING")
|
||||
original_env = os.environ.copy()
|
||||
|
||||
# Disable tracking
|
||||
os.environ["ARCADE_USAGE_TRACKING"] = "0"
|
||||
|
||||
yield
|
||||
|
||||
# Restore original value after test
|
||||
if original_value is None:
|
||||
os.environ.pop("ARCADE_USAGE_TRACKING", None)
|
||||
else:
|
||||
os.environ["ARCADE_USAGE_TRACKING"] = original_value
|
||||
# Restore the original environment
|
||||
os.environ.clear()
|
||||
os.environ.update(original_env)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "arcade-mcp"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
description = "Arcade.dev - Tool Calling platform for Agents"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
|
|
@ -19,7 +19,7 @@ requires-python = ">=3.10"
|
|||
|
||||
dependencies = [
|
||||
# CLI dependencies
|
||||
"arcade-mcp-server>=1.17.2,<2.0.0",
|
||||
"arcade-mcp-server>=1.17.4,<2.0.0",
|
||||
"arcade-core>=4.4.2,<5.0.0",
|
||||
"typer==0.10.0",
|
||||
"rich>=14.0.0,<15.0.0",
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ def _run_scaffold_and_protocol_smoke(repo_root: Path) -> None:
|
|||
generated_root = scaffold_dir / "my_server"
|
||||
_ensure_exists(generated_root / "pyproject.toml")
|
||||
_ensure_exists(generated_root / "src" / "my_server" / "server.py")
|
||||
_ensure_exists(generated_root / "src" / "my_server" / ".env.example")
|
||||
_ensure_exists(generated_root / ".env.example")
|
||||
|
||||
generated_pyproject = generated_root / "pyproject.toml"
|
||||
_add_local_uv_sources(generated_pyproject, repo_root)
|
||||
|
|
|
|||
Loading…
Reference in a new issue