Jeremiah Lowin commited on
Commit
8d034c6
·
1 Parent(s): 91686d5

Support env vars when installing

Browse files
Files changed (6) hide show
  1. README.md +76 -4
  2. pyproject.toml +1 -0
  3. src/fastmcp/cli/claude.py +23 -17
  4. src/fastmcp/cli/cli.py +53 -9
  5. tests/test_cli.py +215 -0
  6. uv.lock +3 -1
README.md CHANGED
@@ -51,9 +51,14 @@ That's it! FastMCP handles all the complex protocol details and server managemen
51
  - [Prompts](#prompts)
52
  - [Images](#images)
53
  - [Context](#context)
 
 
 
54
  - [Deployment](#deployment)
55
  - [Development](#development)
56
- - [Claude Desktop](#claude-desktop)
 
 
57
  - [Examples](#examples)
58
  - [Echo Server](#echo-server)
59
  - [SQLite Explorer](#sqlite-explorer)
@@ -267,6 +272,42 @@ The Context object provides:
267
  - Resource access through `read_resource()`
268
  - Request metadata via `request_id` and `client_id`
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  ## Deployment
271
 
272
  The FastMCP CLI helps you develop and deploy MCP servers.
@@ -296,6 +337,10 @@ fastmcp dev server.py --with pandas --with numpy
296
  fastmcp dev server.py --with-editable .
297
  ```
298
 
 
 
 
 
299
  ### Claude Desktop
300
 
301
  Install your server in Claude Desktop:
@@ -308,9 +353,6 @@ fastmcp install server.py --name "My Server"
308
 
309
  # With dependencies
310
  fastmcp install server.py --with pandas --with numpy
311
-
312
- # Replace an existing server
313
- fastmcp install server.py --force
314
  ```
315
 
316
  The server name in Claude will be:
@@ -318,8 +360,38 @@ The server name in Claude will be:
318
  2. The `name` from your FastMCP instance
319
  3. The filename if the server can't be imported
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  ## Examples
322
 
 
 
323
  ### Echo Server
324
  A simple server demonstrating resources, tools, and prompts:
325
 
 
51
  - [Prompts](#prompts)
52
  - [Images](#images)
53
  - [Context](#context)
54
+ - [Environment Variables](#environment-variables)
55
+ - [Claude Desktop](#claude-desktop)
56
+ - [Development Mode](#development-mode)
57
  - [Deployment](#deployment)
58
  - [Development](#development)
59
+ - [Environment Variables](#environment-variables-1)
60
+ - [Claude Desktop](#claude-desktop-1)
61
+ - [Environment Variables](#environment-variables-2)
62
  - [Examples](#examples)
63
  - [Echo Server](#echo-server)
64
  - [SQLite Explorer](#sqlite-explorer)
 
272
  - Resource access through `read_resource()`
273
  - Request metadata via `request_id` and `client_id`
274
 
275
+ ## Environment Variables
276
+
277
+ MCP servers run in isolated environments and do not inherit environment variables from your system. Here's how to handle environment variables in different contexts:
278
+
279
+ ### Claude Desktop
280
+
281
+ When installing a server in Claude Desktop, provide environment variables using the CLI:
282
+
283
+ ```bash
284
+ # Single env var
285
+ fastmcp install server.py -e API_KEY=abc123
286
+
287
+ # Multiple env vars
288
+ fastmcp install server.py -e API_KEY=abc123 -e OTHER_VAR=value
289
+
290
+ # Load from .env file
291
+ fastmcp install server.py -f .env
292
+ ```
293
+
294
+ Environment variables persist across reinstalls and are only updated when new values are provided:
295
+
296
+ ```bash
297
+ # First install
298
+ fastmcp install server.py -e FOO=bar -e BAZ=123
299
+
300
+ # Second install - FOO and BAZ are preserved
301
+ fastmcp install server.py -e NEW=value
302
+
303
+ # Third install - FOO gets new value, others preserved
304
+ fastmcp install server.py -e FOO=newvalue
305
+ ```
306
+
307
+ ### Development Mode
308
+
309
+ The MCP Inspector also runs servers in an isolated environment. Environment variables must be set through the Inspector UI and are not inherited from your system. The Inspector does not currently support setting environment variables via command line (see [Issue #94](https://github.com/modelcontextprotocol/inspector/issues/94)).
310
+
311
  ## Deployment
312
 
313
  The FastMCP CLI helps you develop and deploy MCP servers.
 
337
  fastmcp dev server.py --with-editable .
338
  ```
339
 
340
+ #### Environment Variables
341
+
342
+ The MCP Inspector runs servers in an isolated environment. Environment variables must be set through the Inspector UI and are not inherited from your system. The Inspector does not currently support setting environment variables via command line (see [Issue #94](https://github.com/modelcontextprotocol/inspector/issues/94)).
343
+
344
  ### Claude Desktop
345
 
346
  Install your server in Claude Desktop:
 
353
 
354
  # With dependencies
355
  fastmcp install server.py --with pandas --with numpy
 
 
 
356
  ```
357
 
358
  The server name in Claude will be:
 
360
  2. The `name` from your FastMCP instance
361
  3. The filename if the server can't be imported
362
 
363
+ #### Environment Variables
364
+
365
+ Claude Desktop runs servers in an isolated environment. Environment variables from your system are NOT automatically available to the server - you must explicitly provide them during installation:
366
+
367
+ ```bash
368
+ # Single env var
369
+ fastmcp install server.py -e API_KEY=abc123
370
+
371
+ # Multiple env vars
372
+ fastmcp install server.py -e API_KEY=abc123 -e OTHER_VAR=value
373
+
374
+ # Load from .env file
375
+ fastmcp install server.py -f .env
376
+ ```
377
+
378
+ Environment variables persist across reinstalls and are only updated when new values are provided:
379
+
380
+ ```bash
381
+ # First install
382
+ fastmcp install server.py -e FOO=bar -e BAZ=123
383
+
384
+ # Second install - FOO and BAZ are preserved
385
+ fastmcp install server.py -e NEW=value
386
+
387
+ # Third install - FOO gets new value, others preserved
388
+ fastmcp install server.py -e FOO=newvalue
389
+ ```
390
+
391
  ## Examples
392
 
393
+ Here are a few examples of FastMCP servers. For more, see the `examples/` directory.
394
+
395
  ### Echo Server
396
  A simple server demonstrating resources, tools, and prompts:
397
 
pyproject.toml CHANGED
@@ -9,6 +9,7 @@ dependencies = [
9
  "pydantic-settings>=2.6.1",
10
  "pydantic>=2.5.3,<3.0.0",
11
  "typer>=0.9.0",
 
12
  ]
13
  requires-python = ">=3.10"
14
  readme = "README.md"
 
9
  "pydantic-settings>=2.6.1",
10
  "pydantic>=2.5.3,<3.0.0",
11
  "typer>=0.9.0",
12
+ "python-dotenv>=1.0.1",
13
  ]
14
  requires-python = ">=3.10"
15
  readme = "README.md"
src/fastmcp/cli/claude.py CHANGED
@@ -3,7 +3,7 @@
3
  import json
4
  import sys
5
  from pathlib import Path
6
- from typing import Optional
7
 
8
  from ..utilities.logging import get_logger
9
 
@@ -30,16 +30,17 @@ def update_claude_config(
30
  *,
31
  with_editable: Optional[Path] = None,
32
  with_packages: Optional[list[str]] = None,
33
- force: bool = False,
34
  ) -> bool:
35
- """Add the MCP server to Claude's configuration.
36
 
37
  Args:
38
  file_spec: Path to the server file, optionally with :object suffix
39
  server_name: Name for the server in Claude's config
40
  with_editable: Optional directory to install in editable mode
41
  with_packages: Optional list of additional packages to install
42
- force: If True, replace existing server with same name
 
43
  """
44
  config_dir = get_claude_config_path()
45
  if not config_dir:
@@ -54,18 +55,17 @@ def update_claude_config(
54
  if "mcpServers" not in config:
55
  config["mcpServers"] = {}
56
 
57
- if server_name in config["mcpServers"]:
58
- if not force:
59
- logger.warning(
60
- f"Server '{server_name}' already exists in Claude config. "
61
- "Use `--force` to replace.",
62
- extra={"config_file": str(config_file)},
63
- )
64
- return False
65
- logger.info(
66
- f"Replacing existing server '{server_name}' in Claude config",
67
- extra={"config_file": str(config_file)},
68
- )
69
 
70
  # Build uv run command
71
  args = ["run", "--with", "fastmcp"]
@@ -89,11 +89,17 @@ def update_claude_config(
89
  # Add fastmcp run command
90
  args.extend(["fastmcp", "run", file_spec])
91
 
92
- config["mcpServers"][server_name] = {
93
  "command": "uv",
94
  "args": args,
95
  }
96
 
 
 
 
 
 
 
97
  config_file.write_text(json.dumps(config, indent=2))
98
  logger.info(
99
  f"Added server '{server_name}' to Claude config",
 
3
  import json
4
  import sys
5
  from pathlib import Path
6
+ from typing import Optional, Dict
7
 
8
  from ..utilities.logging import get_logger
9
 
 
30
  *,
31
  with_editable: Optional[Path] = None,
32
  with_packages: Optional[list[str]] = None,
33
+ env_vars: Optional[Dict[str, str]] = None,
34
  ) -> bool:
35
+ """Add or update a FastMCP server in Claude's configuration.
36
 
37
  Args:
38
  file_spec: Path to the server file, optionally with :object suffix
39
  server_name: Name for the server in Claude's config
40
  with_editable: Optional directory to install in editable mode
41
  with_packages: Optional list of additional packages to install
42
+ env_vars: Optional dictionary of environment variables. These are merged with
43
+ any existing variables, with new values taking precedence.
44
  """
45
  config_dir = get_claude_config_path()
46
  if not config_dir:
 
55
  if "mcpServers" not in config:
56
  config["mcpServers"] = {}
57
 
58
+ # Always preserve existing env vars and merge with new ones
59
+ if (
60
+ server_name in config["mcpServers"]
61
+ and "env" in config["mcpServers"][server_name]
62
+ ):
63
+ existing_env = config["mcpServers"][server_name]["env"]
64
+ if env_vars:
65
+ # New vars take precedence over existing ones
66
+ env_vars = {**existing_env, **env_vars}
67
+ else:
68
+ env_vars = existing_env
 
69
 
70
  # Build uv run command
71
  args = ["run", "--with", "fastmcp"]
 
89
  # Add fastmcp run command
90
  args.extend(["fastmcp", "run", file_spec])
91
 
92
+ server_config = {
93
  "command": "uv",
94
  "args": args,
95
  }
96
 
97
+ # Add environment variables if specified
98
+ if env_vars:
99
+ server_config["env"] = env_vars
100
+
101
+ config["mcpServers"][server_name] = server_config
102
+
103
  config_file.write_text(json.dumps(config, indent=2))
104
  logger.info(
105
  f"Added server '{server_name}' to Claude config",
src/fastmcp/cli/cli.py CHANGED
@@ -5,10 +5,11 @@ import importlib.util
5
  import subprocess
6
  import sys
7
  from pathlib import Path
8
- from typing import Optional, Tuple
9
 
10
  import typer
11
  from typing_extensions import Annotated
 
12
 
13
  from ..utilities.logging import get_logger
14
  from . import claude
@@ -23,6 +24,17 @@ app = typer.Typer(
23
  )
24
 
25
 
 
 
 
 
 
 
 
 
 
 
 
26
  def _build_uv_command(
27
  file_spec: str,
28
  with_editable: Optional[Path] = None,
@@ -304,16 +316,32 @@ def install(
304
  help="Additional packages to install",
305
  ),
306
  ] = [],
307
- force: Annotated[
308
- bool,
309
  typer.Option(
310
- "--force",
 
 
 
 
 
 
 
 
311
  "-f",
312
- help="Replace existing server if one exists with the same name",
 
 
 
 
313
  ),
314
- ] = False,
315
  ) -> None:
316
- """Install a FastMCP server in the Claude desktop app."""
 
 
 
 
317
  file, server_object = _parse_file_path(file_spec)
318
 
319
  logger.debug(
@@ -324,7 +352,6 @@ def install(
324
  "server_object": server_object,
325
  "with_editable": str(with_editable) if with_editable else None,
326
  "with_packages": with_packages,
327
- "force": force,
328
  },
329
  )
330
 
@@ -345,12 +372,29 @@ def install(
345
  )
346
  name = file.stem
347
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  if claude.update_claude_config(
349
  file_spec,
350
  name,
351
  with_editable=with_editable,
352
  with_packages=with_packages,
353
- force=force,
354
  ):
355
  logger.info(f"Successfully installed {name} in Claude app")
356
  else:
 
5
  import subprocess
6
  import sys
7
  from pathlib import Path
8
+ from typing import Optional, Tuple, Dict
9
 
10
  import typer
11
  from typing_extensions import Annotated
12
+ import dotenv
13
 
14
  from ..utilities.logging import get_logger
15
  from . import claude
 
24
  )
25
 
26
 
27
+ def _parse_env_var(env_var: str) -> Tuple[str, str]:
28
+ """Parse environment variable string in format KEY=VALUE."""
29
+ if "=" not in env_var:
30
+ logger.error(
31
+ f"Invalid environment variable format: {env_var}. Must be KEY=VALUE"
32
+ )
33
+ sys.exit(1)
34
+ key, value = env_var.split("=", 1)
35
+ return key.strip(), value.strip()
36
+
37
+
38
  def _build_uv_command(
39
  file_spec: str,
40
  with_editable: Optional[Path] = None,
 
316
  help="Additional packages to install",
317
  ),
318
  ] = [],
319
+ env_vars: Annotated[
320
+ list[str],
321
  typer.Option(
322
+ "--env-var",
323
+ "-e",
324
+ help="Environment variables in KEY=VALUE format",
325
+ ),
326
+ ] = [],
327
+ env_file: Annotated[
328
+ Optional[Path],
329
+ typer.Option(
330
+ "--env-file",
331
  "-f",
332
+ help="Load environment variables from a .env file",
333
+ exists=True,
334
+ file_okay=True,
335
+ dir_okay=False,
336
+ resolve_path=True,
337
  ),
338
+ ] = None,
339
  ) -> None:
340
+ """Install a FastMCP server in the Claude desktop app.
341
+
342
+ Environment variables are preserved once added and only updated if new values
343
+ are explicitly provided.
344
+ """
345
  file, server_object = _parse_file_path(file_spec)
346
 
347
  logger.debug(
 
352
  "server_object": server_object,
353
  "with_editable": str(with_editable) if with_editable else None,
354
  "with_packages": with_packages,
 
355
  },
356
  )
357
 
 
372
  )
373
  name = file.stem
374
 
375
+ # Process environment variables if provided
376
+ env_dict: Optional[Dict[str, str]] = None
377
+ if env_file or env_vars:
378
+ env_dict = {}
379
+ # Load from .env file if specified
380
+ if env_file:
381
+ try:
382
+ env_dict.update(dotenv.dotenv_values(env_file))
383
+ except Exception as e:
384
+ logger.error(f"Failed to load .env file: {e}")
385
+ sys.exit(1)
386
+
387
+ # Add command line environment variables
388
+ for env_var in env_vars:
389
+ key, value = _parse_env_var(env_var)
390
+ env_dict[key] = value
391
+
392
  if claude.update_claude_config(
393
  file_spec,
394
  name,
395
  with_editable=with_editable,
396
  with_packages=with_packages,
397
+ env_vars=env_dict,
398
  ):
399
  logger.info(f"Successfully installed {name} in Claude app")
400
  else:
tests/test_cli.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for the FastMCP CLI."""
2
+
3
+ import json
4
+ from unittest.mock import Mock, patch
5
+
6
+ import pytest
7
+ from typer.testing import CliRunner
8
+
9
+ from fastmcp.cli.cli import app, _parse_env_var
10
+
11
+
12
+ @pytest.fixture
13
+ def mock_config(tmp_path):
14
+ """Create a mock Claude config file."""
15
+ config = {"mcpServers": {}}
16
+ config_file = tmp_path / "claude_desktop_config.json"
17
+ config_file.write_text(json.dumps(config))
18
+ return config_file
19
+
20
+
21
+ @pytest.fixture
22
+ def mock_server_file(tmp_path):
23
+ """Create a mock server file."""
24
+ server_file = tmp_path / "server.py"
25
+ server_file.write_text(
26
+ "from fastmcp import Server\n" "server = Server(name='test')\n"
27
+ )
28
+ return server_file
29
+
30
+
31
+ @pytest.fixture
32
+ def mock_env_file(tmp_path):
33
+ """Create a mock .env file."""
34
+ env_file = tmp_path / ".env"
35
+ env_file.write_text("FOO=bar\nBAZ=123")
36
+ return env_file
37
+
38
+
39
+ def test_parse_env_var():
40
+ """Test parsing environment variables."""
41
+ assert _parse_env_var("FOO=bar") == ("FOO", "bar")
42
+ assert _parse_env_var("FOO=") == ("FOO", "")
43
+ assert _parse_env_var("FOO=bar baz") == ("FOO", "bar baz")
44
+ assert _parse_env_var("FOO = bar ") == ("FOO", "bar")
45
+
46
+ with pytest.raises(SystemExit):
47
+ _parse_env_var("invalid")
48
+
49
+
50
+ @pytest.mark.parametrize(
51
+ "args,expected_env",
52
+ [
53
+ # Basic env var
54
+ (
55
+ ["--env-var", "FOO=bar"],
56
+ {"FOO": "bar"},
57
+ ),
58
+ # Multiple env vars
59
+ (
60
+ ["--env-var", "FOO=bar", "--env-var", "BAZ=123"],
61
+ {"FOO": "bar", "BAZ": "123"},
62
+ ),
63
+ # Env var with spaces
64
+ (
65
+ ["--env-var", "FOO=bar baz"],
66
+ {"FOO": "bar baz"},
67
+ ),
68
+ ],
69
+ )
70
+ def test_install_with_env_vars(mock_config, mock_server_file, args, expected_env):
71
+ """Test installing with environment variables."""
72
+ runner = CliRunner()
73
+
74
+ with (
75
+ patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path,
76
+ patch("fastmcp.cli.cli._import_server") as mock_import,
77
+ ):
78
+ mock_config_path.return_value = mock_config.parent
79
+ mock_server = Mock()
80
+ mock_server.name = "test" # Set name as an attribute
81
+ mock_import.return_value = mock_server
82
+
83
+ result = runner.invoke(
84
+ app,
85
+ ["install", str(mock_server_file)] + args,
86
+ )
87
+
88
+ assert result.exit_code == 0
89
+
90
+ # Read the config file and check env vars
91
+ config = json.loads(mock_config.read_text())
92
+ assert "mcpServers" in config
93
+ assert len(config["mcpServers"]) == 1
94
+ server = next(iter(config["mcpServers"].values()))
95
+ assert server["env"] == expected_env
96
+
97
+
98
+ def test_install_with_env_file(mock_config, mock_server_file, mock_env_file):
99
+ """Test installing with environment variables from a file."""
100
+ runner = CliRunner()
101
+
102
+ with (
103
+ patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path,
104
+ patch("fastmcp.cli.cli._import_server") as mock_import,
105
+ ):
106
+ mock_config_path.return_value = mock_config.parent
107
+ mock_server = Mock()
108
+ mock_server.name = "test" # Set name as an attribute
109
+ mock_import.return_value = mock_server
110
+
111
+ result = runner.invoke(
112
+ app,
113
+ ["install", str(mock_server_file), "--env-file", str(mock_env_file)],
114
+ )
115
+
116
+ assert result.exit_code == 0
117
+
118
+ # Read the config file and check env vars
119
+ config = json.loads(mock_config.read_text())
120
+ assert "mcpServers" in config
121
+ assert len(config["mcpServers"]) == 1
122
+ server = next(iter(config["mcpServers"].values()))
123
+ assert server["env"] == {"FOO": "bar", "BAZ": "123"}
124
+
125
+
126
+ def test_install_preserves_existing_env_vars(mock_config, mock_server_file):
127
+ """Test that installing preserves existing environment variables."""
128
+ # Set up initial config with env vars
129
+ config = {
130
+ "mcpServers": {
131
+ "test": {
132
+ "command": "uv",
133
+ "args": [
134
+ "run",
135
+ "--with",
136
+ "fastmcp",
137
+ "fastmcp",
138
+ "run",
139
+ str(mock_server_file),
140
+ ],
141
+ "env": {"FOO": "bar", "BAZ": "123"},
142
+ }
143
+ }
144
+ }
145
+ mock_config.write_text(json.dumps(config))
146
+
147
+ runner = CliRunner()
148
+
149
+ with (
150
+ patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path,
151
+ patch("fastmcp.cli.cli._import_server") as mock_import,
152
+ ):
153
+ mock_config_path.return_value = mock_config.parent
154
+ mock_server = Mock()
155
+ mock_server.name = "test" # Set name as an attribute
156
+ mock_import.return_value = mock_server
157
+
158
+ # Install with a new env var
159
+ result = runner.invoke(
160
+ app,
161
+ ["install", str(mock_server_file), "--env-var", "NEW=value"],
162
+ )
163
+
164
+ assert result.exit_code == 0
165
+
166
+ # Read the config file and check env vars are preserved
167
+ config = json.loads(mock_config.read_text())
168
+ server = next(iter(config["mcpServers"].values()))
169
+ assert server["env"] == {"FOO": "bar", "BAZ": "123", "NEW": "value"}
170
+
171
+
172
+ def test_install_updates_existing_env_vars(mock_config, mock_server_file):
173
+ """Test that installing updates existing environment variables."""
174
+ # Set up initial config with env vars
175
+ config = {
176
+ "mcpServers": {
177
+ "test": {
178
+ "command": "uv",
179
+ "args": [
180
+ "run",
181
+ "--with",
182
+ "fastmcp",
183
+ "fastmcp",
184
+ "run",
185
+ str(mock_server_file),
186
+ ],
187
+ "env": {"FOO": "bar", "BAZ": "123"},
188
+ }
189
+ }
190
+ }
191
+ mock_config.write_text(json.dumps(config))
192
+
193
+ runner = CliRunner()
194
+
195
+ with (
196
+ patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path,
197
+ patch("fastmcp.cli.cli._import_server") as mock_import,
198
+ ):
199
+ mock_config_path.return_value = mock_config.parent
200
+ mock_server = Mock()
201
+ mock_server.name = "test" # Set name as an attribute
202
+ mock_import.return_value = mock_server
203
+
204
+ # Update an existing env var
205
+ result = runner.invoke(
206
+ app,
207
+ ["install", str(mock_server_file), "--env-var", "FOO=newvalue"],
208
+ )
209
+
210
+ assert result.exit_code == 0
211
+
212
+ # Read the config file and check env var was updated
213
+ config = json.loads(mock_config.read_text())
214
+ server = next(iter(config["mcpServers"].values()))
215
+ assert server["env"] == {"FOO": "newvalue", "BAZ": "123"}
uv.lock CHANGED
@@ -228,13 +228,14 @@ wheels = [
228
 
229
  [[package]]
230
  name = "fastmcp"
231
- version = "0.3.1.dev3+g35b0931.d20241201"
232
  source = { editable = "." }
233
  dependencies = [
234
  { name = "httpx" },
235
  { name = "mcp" },
236
  { name = "pydantic" },
237
  { name = "pydantic-settings" },
 
238
  { name = "typer" },
239
  ]
240
 
@@ -263,6 +264,7 @@ requires-dist = [
263
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" },
264
  { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.5" },
265
  { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.1" },
 
266
  { name = "ruff", marker = "extra == 'dev'" },
267
  { name = "typer", specifier = ">=0.9.0" },
268
  ]
 
228
 
229
  [[package]]
230
  name = "fastmcp"
231
+ version = "0.3.2.dev0+g5656200.d20241201"
232
  source = { editable = "." }
233
  dependencies = [
234
  { name = "httpx" },
235
  { name = "mcp" },
236
  { name = "pydantic" },
237
  { name = "pydantic-settings" },
238
+ { name = "python-dotenv" },
239
  { name = "typer" },
240
  ]
241
 
 
264
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" },
265
  { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.5" },
266
  { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.1" },
267
+ { name = "python-dotenv", specifier = ">=1.0.1" },
268
  { name = "ruff", marker = "extra == 'dev'" },
269
  { name = "typer", specifier = ">=0.9.0" },
270
  ]