Jeremiah Lowin commited on
Commit
ee8fcb5
·
unverified ·
2 Parent(s): d0be1ec8d034c6

Merge pull request #27 from jlowin/env

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
@@ -61,9 +61,14 @@ FastMCP handles all the complex protocol details and server management, so you c
61
  - [Prompts](#prompts)
62
  - [Images](#images)
63
  - [Context](#context)
 
 
 
64
  - [Deployment](#deployment)
65
  - [Development](#development)
66
- - [Claude Desktop](#claude-desktop)
 
 
67
  - [Examples](#examples)
68
  - [Echo Server](#echo-server)
69
  - [SQLite Explorer](#sqlite-explorer)
@@ -277,6 +282,42 @@ The Context object provides:
277
  - Resource access through `read_resource()`
278
  - Request metadata via `request_id` and `client_id`
279
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  ## Deployment
281
 
282
  The FastMCP CLI helps you develop and deploy MCP servers.
@@ -306,6 +347,10 @@ fastmcp dev server.py --with pandas --with numpy
306
  fastmcp dev server.py --with-editable .
307
  ```
308
 
 
 
 
 
309
  ### Claude Desktop
310
 
311
  Install your server in Claude Desktop:
@@ -318,9 +363,6 @@ fastmcp install server.py --name "My Server"
318
 
319
  # With dependencies
320
  fastmcp install server.py --with pandas --with numpy
321
-
322
- # Replace an existing server
323
- fastmcp install server.py --force
324
  ```
325
 
326
  The server name in Claude will be:
@@ -328,8 +370,38 @@ The server name in Claude will be:
328
  2. The `name` from your FastMCP instance
329
  3. The filename if the server can't be imported
330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  ## Examples
332
 
 
 
333
  ### Echo Server
334
  A simple server demonstrating resources, tools, and prompts:
335
 
 
61
  - [Prompts](#prompts)
62
  - [Images](#images)
63
  - [Context](#context)
64
+ - [Environment Variables](#environment-variables)
65
+ - [Claude Desktop](#claude-desktop)
66
+ - [Development Mode](#development-mode)
67
  - [Deployment](#deployment)
68
  - [Development](#development)
69
+ - [Environment Variables](#environment-variables-1)
70
+ - [Claude Desktop](#claude-desktop-1)
71
+ - [Environment Variables](#environment-variables-2)
72
  - [Examples](#examples)
73
  - [Echo Server](#echo-server)
74
  - [SQLite Explorer](#sqlite-explorer)
 
282
  - Resource access through `read_resource()`
283
  - Request metadata via `request_id` and `client_id`
284
 
285
+ ## Environment Variables
286
+
287
+ 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:
288
+
289
+ ### Claude Desktop
290
+
291
+ When installing a server in Claude Desktop, provide environment variables using the CLI:
292
+
293
+ ```bash
294
+ # Single env var
295
+ fastmcp install server.py -e API_KEY=abc123
296
+
297
+ # Multiple env vars
298
+ fastmcp install server.py -e API_KEY=abc123 -e OTHER_VAR=value
299
+
300
+ # Load from .env file
301
+ fastmcp install server.py -f .env
302
+ ```
303
+
304
+ Environment variables persist across reinstalls and are only updated when new values are provided:
305
+
306
+ ```bash
307
+ # First install
308
+ fastmcp install server.py -e FOO=bar -e BAZ=123
309
+
310
+ # Second install - FOO and BAZ are preserved
311
+ fastmcp install server.py -e NEW=value
312
+
313
+ # Third install - FOO gets new value, others preserved
314
+ fastmcp install server.py -e FOO=newvalue
315
+ ```
316
+
317
+ ### Development Mode
318
+
319
+ 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)).
320
+
321
  ## Deployment
322
 
323
  The FastMCP CLI helps you develop and deploy MCP servers.
 
347
  fastmcp dev server.py --with-editable .
348
  ```
349
 
350
+ #### Environment Variables
351
+
352
+ 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)).
353
+
354
  ### Claude Desktop
355
 
356
  Install your server in Claude Desktop:
 
363
 
364
  # With dependencies
365
  fastmcp install server.py --with pandas --with numpy
 
 
 
366
  ```
367
 
368
  The server name in Claude will be:
 
370
  2. The `name` from your FastMCP instance
371
  3. The filename if the server can't be imported
372
 
373
+ #### Environment Variables
374
+
375
+ 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:
376
+
377
+ ```bash
378
+ # Single env var
379
+ fastmcp install server.py -e API_KEY=abc123
380
+
381
+ # Multiple env vars
382
+ fastmcp install server.py -e API_KEY=abc123 -e OTHER_VAR=value
383
+
384
+ # Load from .env file
385
+ fastmcp install server.py -f .env
386
+ ```
387
+
388
+ Environment variables persist across reinstalls and are only updated when new values are provided:
389
+
390
+ ```bash
391
+ # First install
392
+ fastmcp install server.py -e FOO=bar -e BAZ=123
393
+
394
+ # Second install - FOO and BAZ are preserved
395
+ fastmcp install server.py -e NEW=value
396
+
397
+ # Third install - FOO gets new value, others preserved
398
+ fastmcp install server.py -e FOO=newvalue
399
+ ```
400
+
401
  ## Examples
402
 
403
+ Here are a few examples of FastMCP servers. For more, see the `examples/` directory.
404
+
405
  ### Echo Server
406
  A simple server demonstrating resources, tools, and prompts:
407
 
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
  ]