fastmcp 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastmcp/cli/claude.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import json
4
4
  import sys
5
5
  from pathlib import Path
6
- from typing import Optional
6
+ from typing import Optional, Dict
7
7
 
8
8
  from ..utilities.logging import get_logger
9
9
 
@@ -30,16 +30,17 @@ def update_claude_config(
30
30
  *,
31
31
  with_editable: Optional[Path] = None,
32
32
  with_packages: Optional[list[str]] = None,
33
- force: bool = False,
33
+ env_vars: Optional[Dict[str, str]] = None,
34
34
  ) -> bool:
35
- """Add the MCP server to Claude's configuration.
35
+ """Add or update a FastMCP server in Claude's configuration.
36
36
 
37
37
  Args:
38
38
  file_spec: Path to the server file, optionally with :object suffix
39
39
  server_name: Name for the server in Claude's config
40
40
  with_editable: Optional directory to install in editable mode
41
41
  with_packages: Optional list of additional packages to install
42
- force: If True, replace existing server with same name
42
+ env_vars: Optional dictionary of environment variables. These are merged with
43
+ any existing variables, with new values taking precedence.
43
44
  """
44
45
  config_dir = get_claude_config_path()
45
46
  if not config_dir:
@@ -54,18 +55,17 @@ def update_claude_config(
54
55
  if "mcpServers" not in config:
55
56
  config["mcpServers"] = {}
56
57
 
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
- )
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
69
 
70
70
  # Build uv run command
71
71
  args = ["run", "--with", "fastmcp"]
@@ -89,11 +89,17 @@ def update_claude_config(
89
89
  # Add fastmcp run command
90
90
  args.extend(["fastmcp", "run", file_spec])
91
91
 
92
- config["mcpServers"][server_name] = {
92
+ server_config = {
93
93
  "command": "uv",
94
94
  "args": args,
95
95
  }
96
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
+
97
103
  config_file.write_text(json.dumps(config, indent=2))
98
104
  logger.info(
99
105
  f"Added server '{server_name}' to Claude config",
fastmcp/cli/cli.py CHANGED
@@ -5,10 +5,11 @@ import importlib.util
5
5
  import subprocess
6
6
  import sys
7
7
  from pathlib import Path
8
- from typing import Optional, Tuple
8
+ from typing import Optional, Tuple, Dict
9
9
 
10
10
  import typer
11
11
  from typing_extensions import Annotated
12
+ import dotenv
12
13
 
13
14
  from ..utilities.logging import get_logger
14
15
  from . import claude
@@ -23,6 +24,17 @@ app = typer.Typer(
23
24
  )
24
25
 
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
+
26
38
  def _build_uv_command(
27
39
  file_spec: str,
28
40
  with_editable: Optional[Path] = None,
@@ -304,16 +316,32 @@ def install(
304
316
  help="Additional packages to install",
305
317
  ),
306
318
  ] = [],
307
- force: Annotated[
308
- bool,
319
+ env_vars: Annotated[
320
+ list[str],
309
321
  typer.Option(
310
- "--force",
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",
311
331
  "-f",
312
- help="Replace existing server if one exists with the same name",
332
+ help="Load environment variables from a .env file",
333
+ exists=True,
334
+ file_okay=True,
335
+ dir_okay=False,
336
+ resolve_path=True,
313
337
  ),
314
- ] = False,
338
+ ] = None,
315
339
  ) -> None:
316
- """Install a FastMCP server in the Claude desktop app."""
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
+ """
317
345
  file, server_object = _parse_file_path(file_spec)
318
346
 
319
347
  logger.debug(
@@ -324,7 +352,6 @@ def install(
324
352
  "server_object": server_object,
325
353
  "with_editable": str(with_editable) if with_editable else None,
326
354
  "with_packages": with_packages,
327
- "force": force,
328
355
  },
329
356
  )
330
357
 
@@ -345,12 +372,29 @@ def install(
345
372
  )
346
373
  name = file.stem
347
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
+
348
392
  if claude.update_claude_config(
349
393
  file_spec,
350
394
  name,
351
395
  with_editable=with_editable,
352
396
  with_packages=with_packages,
353
- force=force,
397
+ env_vars=env_dict,
354
398
  ):
355
399
  logger.info(f"Successfully installed {name} in Claude app")
356
400
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fastmcp
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A more ergonomic interface for MCP servers
5
5
  Author: Jeremiah Lowin
6
6
  License: Apache-2.0
@@ -9,6 +9,7 @@ Requires-Dist: httpx>=0.26.0
9
9
  Requires-Dist: mcp<2.0.0,>=1.0.0
10
10
  Requires-Dist: pydantic-settings>=2.6.1
11
11
  Requires-Dist: pydantic<3.0.0,>=2.5.3
12
+ Requires-Dist: python-dotenv>=1.0.1
12
13
  Requires-Dist: typer>=0.9.0
13
14
  Provides-Extra: dev
14
15
  Requires-Dist: copychat>=0.5.2; extra == 'dev'
@@ -30,24 +31,34 @@ Description-Content-Type: text/markdown
30
31
  [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
31
32
  [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
32
33
 
33
- A fast, Pythonic way to build [Model Context Protocol (MCP)](https://modelcontextprotocol.io) servers
34
+ The fast, Pythonic way to build MCP servers
34
35
 
35
36
  </div>
36
37
 
37
- FastMCP makes building MCP servers simple and intuitive. Create tools, expose resources, and define prompts with clean, Pythonic code:
38
+ [Model Context Protocol (MCP)](https://modelcontextprotocol.io) servers are a new, standardized way to provide context and tools to your LLMs, and FastMCP makes building MCP servers simple and intuitive. Create tools, expose resources, and define prompts with clean, Pythonic code:
38
39
 
39
40
  ```python
41
+ # demo.py
42
+
40
43
  from fastmcp import FastMCP
41
44
 
45
+
42
46
  mcp = FastMCP("Demo 🚀")
43
47
 
48
+
44
49
  @mcp.tool()
45
50
  def add(a: int, b: int) -> int:
46
51
  """Add two numbers"""
47
52
  return a + b
48
53
  ```
49
54
 
50
- That's it! FastMCP handles all the complex protocol details and server management, so you can focus on building great tools. It's designed to be high-level and Pythonic - in most cases, decorating a function is all you need.
55
+ That's it! Give Claude access to the server by running:
56
+
57
+ ```bash
58
+ fastmcp install demo.py
59
+ ```
60
+
61
+ FastMCP handles all the complex protocol details and server management, so you can focus on building great tools. It's designed to be high-level and Pythonic - in most cases, decorating a function is all you need.
51
62
 
52
63
 
53
64
  ### Key features:
@@ -76,7 +87,9 @@ That's it! FastMCP handles all the complex protocol details and server managemen
76
87
  - [Context](#context)
77
88
  - [Deployment](#deployment)
78
89
  - [Development](#development)
90
+ - [Environment Variables](#environment-variables)
79
91
  - [Claude Desktop](#claude-desktop)
92
+ - [Environment Variables](#environment-variables-1)
80
93
  - [Examples](#examples)
81
94
  - [Echo Server](#echo-server)
82
95
  - [SQLite Explorer](#sqlite-explorer)
@@ -319,6 +332,10 @@ fastmcp dev server.py --with pandas --with numpy
319
332
  fastmcp dev server.py --with-editable .
320
333
  ```
321
334
 
335
+ #### Environment Variables
336
+
337
+ 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)).
338
+
322
339
  ### Claude Desktop
323
340
 
324
341
  Install your server in Claude Desktop:
@@ -331,9 +348,6 @@ fastmcp install server.py --name "My Server"
331
348
 
332
349
  # With dependencies
333
350
  fastmcp install server.py --with pandas --with numpy
334
-
335
- # Replace an existing server
336
- fastmcp install server.py --force
337
351
  ```
338
352
 
339
353
  The server name in Claude will be:
@@ -341,8 +355,38 @@ The server name in Claude will be:
341
355
  2. The `name` from your FastMCP instance
342
356
  3. The filename if the server can't be imported
343
357
 
358
+ #### Environment Variables
359
+
360
+ 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:
361
+
362
+ ```bash
363
+ # Single env var
364
+ fastmcp install server.py -e API_KEY=abc123
365
+
366
+ # Multiple env vars
367
+ fastmcp install server.py -e API_KEY=abc123 -e OTHER_VAR=value
368
+
369
+ # Load from .env file
370
+ fastmcp install server.py -f .env
371
+ ```
372
+
373
+ Environment variables persist across reinstalls and are only updated when new values are provided:
374
+
375
+ ```bash
376
+ # First install
377
+ fastmcp install server.py -e FOO=bar -e BAZ=123
378
+
379
+ # Second install - FOO and BAZ are preserved
380
+ fastmcp install server.py -e NEW=value
381
+
382
+ # Third install - FOO gets new value, others preserved
383
+ fastmcp install server.py -e FOO=newvalue
384
+ ```
385
+
344
386
  ## Examples
345
387
 
388
+ Here are a few examples of FastMCP servers. For more, see the `examples/` directory.
389
+
346
390
  ### Echo Server
347
391
  A simple server demonstrating resources, tools, and prompts:
348
392
 
@@ -2,8 +2,8 @@ fastmcp/__init__.py,sha256=Y5dHGBwyQPgNP5gzOyNIItefvMZ3vJLdom1oV8A1u_k,248
2
2
  fastmcp/exceptions.py,sha256=K0rCgXsUVlws39hz98Tb4BBf_BzIql_zXFZgqbkNTiE,348
3
3
  fastmcp/server.py,sha256=JttRzt1bnJGBU8mL4Bo764WHFXQ09QqKc_CUT3390WM,21997
4
4
  fastmcp/cli/__init__.py,sha256=7hrwtCHX9nMd9qcz7R_JFSoqbL71fC35cBLXBS430mg,88
5
- fastmcp/cli/claude.py,sha256=hId0cTmAfCrav72Hg5LeO0SPPNyEVtIOcoKVAy8gD3k,3390
6
- fastmcp/cli/cli.py,sha256=0r9_HR_wayV5MZARS02ZO1RnRCT8roxdY1CGGYzPNqo,10044
5
+ fastmcp/cli/claude.py,sha256=mepURIVhlwQQYgkjgmuJCS841QAs9Mu-xRxdLpfCO0o,3628
6
+ fastmcp/cli/cli.py,sha256=gzpgUseCR5tkdyZumjjxyAIYJLMeAdyJ5zgRHC9EL_M,11407
7
7
  fastmcp/prompts/__init__.py,sha256=4BsMxoYolpoxg74xkkkzCFL8vvdkLVJ5cIPNs1ND1Jo,99
8
8
  fastmcp/prompts/base.py,sha256=WaSsfyFSsUPUbcApkGy3Pm-Ne-Gk-5ZwU3efqRYn1mQ,4996
9
9
  fastmcp/prompts/manager.py,sha256=EkexOB_N4QNtC-UlZmIcWcau91ceO2O1K4_kD75pA_A,1485
@@ -19,8 +19,8 @@ fastmcp/tools/tool_manager.py,sha256=PT6XHcQWzhdC6kfdsJaddRn7VLps4nAs5FMG8l1j8Zc
19
19
  fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
20
20
  fastmcp/utilities/logging.py,sha256=VLJdNc0tIYoQZmpobehLUnWrQz7NXnuwSqrDlFt2RF0,738
21
21
  fastmcp/utilities/types.py,sha256=jFlZMZsKrJg4NWc1vTBIILLoHpTVwSd-vxO7ycoRuig,1718
22
- fastmcp-0.3.1.dist-info/METADATA,sha256=PdOwJThIuqGDpSExh8dUsYyxUQBq0rw5MnOV3an2aTs,12108
23
- fastmcp-0.3.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
24
- fastmcp-0.3.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
25
- fastmcp-0.3.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- fastmcp-0.3.1.dist-info/RECORD,,
22
+ fastmcp-0.3.2.dist-info/METADATA,sha256=R64QFKVKLfIVWjOFi-vmEL3At_Dx8rwHTBdjBOTLC30,13617
23
+ fastmcp-0.3.2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
24
+ fastmcp-0.3.2.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
25
+ fastmcp-0.3.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
+ fastmcp-0.3.2.dist-info/RECORD,,