fastmcp 0.3.2__tar.gz → 0.3.3__tar.gz
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-0.3.2 → fastmcp-0.3.3}/.github/workflows/run-tests.yml +11 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.gitignore +1 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/PKG-INFO +173 -62
- {fastmcp-0.3.2 → fastmcp-0.3.3}/README.md +172 -61
- {fastmcp-0.3.2 → fastmcp-0.3.3}/examples/screenshot.py +8 -8
- fastmcp-0.3.3/examples/text_me.py +71 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/cli/claude.py +10 -6
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/cli/cli.py +15 -12
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/server.py +7 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/test_cli.py +118 -42
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.github/ai-labeler.yml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.github/release.yml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.github/workflows/ai-labeler.yml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.github/workflows/lint.yml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.github/workflows/publish.yml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.pre-commit-config.yaml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/.python-version +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/LICENSE +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/docs/assets/demo-inspector.png +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/examples/desktop.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/examples/echo.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/examples/readme-quickstart.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/examples/simple_echo.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/pyproject.toml +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/cli/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/exceptions.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/prompts/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/prompts/base.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/prompts/manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/prompts/prompt_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/resources/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/resources/base.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/resources/resource_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/resources/templates.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/resources/types.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/tools/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/tools/base.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/tools/tool_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/utilities/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/utilities/logging.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/src/fastmcp/utilities/types.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/prompts/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/prompts/test_base.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/prompts/test_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/test_file_resources.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/test_function_resources.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/test_resource_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/test_resource_template.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/resources/test_resources.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/servers/__init__.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/servers/test_file_server.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/test_server.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/tests/test_tool_manager.py +0 -0
- {fastmcp-0.3.2 → fastmcp-0.3.3}/uv.lock +0 -0
|
@@ -7,7 +7,18 @@ env:
|
|
|
7
7
|
on:
|
|
8
8
|
push:
|
|
9
9
|
branches: ["main"]
|
|
10
|
+
paths:
|
|
11
|
+
- "src/**"
|
|
12
|
+
- "tests/**"
|
|
13
|
+
- "uv.lock"
|
|
14
|
+
- "pyproject.toml"
|
|
10
15
|
pull_request:
|
|
16
|
+
paths:
|
|
17
|
+
- "src/**"
|
|
18
|
+
- "tests/**"
|
|
19
|
+
- "uv.lock"
|
|
20
|
+
- "pyproject.toml"
|
|
21
|
+
|
|
11
22
|
workflow_dispatch:
|
|
12
23
|
|
|
13
24
|
permissions:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fastmcp
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A more ergonomic interface for MCP servers
|
|
5
5
|
Author: Jeremiah Lowin
|
|
6
6
|
License: Apache-2.0
|
|
@@ -27,11 +27,12 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
<div align="center">
|
|
29
29
|
|
|
30
|
+
<strong>The fast, Pythonic way to build MCP servers.</strong>
|
|
31
|
+
|
|
30
32
|
[](https://pypi.org/project/fastmcp)
|
|
31
33
|
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
|
32
34
|
[](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
|
|
33
35
|
|
|
34
|
-
The fast, Pythonic way to build MCP servers
|
|
35
36
|
|
|
36
37
|
</div>
|
|
37
38
|
|
|
@@ -85,24 +86,33 @@ FastMCP handles all the complex protocol details and server management, so you c
|
|
|
85
86
|
- [Prompts](#prompts)
|
|
86
87
|
- [Images](#images)
|
|
87
88
|
- [Context](#context)
|
|
88
|
-
- [
|
|
89
|
-
- [Development](#development)
|
|
90
|
-
|
|
91
|
-
- [
|
|
92
|
-
|
|
89
|
+
- [Running Your Server](#running-your-server)
|
|
90
|
+
- [Development Mode (Recommended for Building \& Testing)](#development-mode-recommended-for-building--testing)
|
|
91
|
+
- [Claude Desktop Integration (For Regular Use)](#claude-desktop-integration-for-regular-use)
|
|
92
|
+
- [Direct Execution (For Advanced Use Cases)](#direct-execution-for-advanced-use-cases)
|
|
93
|
+
- [Server Object Names](#server-object-names)
|
|
93
94
|
- [Examples](#examples)
|
|
94
95
|
- [Echo Server](#echo-server)
|
|
95
96
|
- [SQLite Explorer](#sqlite-explorer)
|
|
97
|
+
- [Contributing](#contributing)
|
|
98
|
+
- [Prerequisites](#prerequisites)
|
|
99
|
+
- [Installation](#installation-1)
|
|
100
|
+
- [Testing](#testing)
|
|
101
|
+
- [Formatting](#formatting)
|
|
102
|
+
- [Opening a Pull Request](#opening-a-pull-request)
|
|
96
103
|
|
|
97
104
|
## Installation
|
|
98
105
|
|
|
106
|
+
We strongly recommend installing FastMCP with [uv](https://docs.astral.sh/uv/), as it is required for deploying servers:
|
|
107
|
+
|
|
99
108
|
```bash
|
|
100
|
-
# We strongly recommend installing with uv
|
|
101
|
-
brew install uv # on macOS
|
|
102
109
|
uv pip install fastmcp
|
|
103
110
|
```
|
|
104
111
|
|
|
105
|
-
|
|
112
|
+
Note: on macOS, uv may need to be installed with Homebrew (`brew install uv`) in order to make it available to the Claude Desktop app.
|
|
113
|
+
|
|
114
|
+
Alternatively, to use the SDK without deploying, you may use pip:
|
|
115
|
+
|
|
106
116
|
```bash
|
|
107
117
|
pip install fastmcp
|
|
108
118
|
```
|
|
@@ -173,8 +183,10 @@ mcp = FastMCP("My App")
|
|
|
173
183
|
|
|
174
184
|
# Configure host/port for HTTP transport (optional)
|
|
175
185
|
mcp = FastMCP("My App", host="localhost", port=8000)
|
|
186
|
+
|
|
187
|
+
# Specify dependencies for deployment and development
|
|
188
|
+
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
|
|
176
189
|
```
|
|
177
|
-
*Note: All of the following code examples assume you've created a FastMCP server instance called `mcp`, as shown above.*
|
|
178
190
|
|
|
179
191
|
### Resources
|
|
180
192
|
|
|
@@ -303,84 +315,101 @@ The Context object provides:
|
|
|
303
315
|
- Resource access through `read_resource()`
|
|
304
316
|
- Request metadata via `request_id` and `client_id`
|
|
305
317
|
|
|
306
|
-
##
|
|
318
|
+
## Running Your Server
|
|
307
319
|
|
|
308
|
-
|
|
320
|
+
There are three main ways to use your FastMCP server, each suited for different stages of development:
|
|
309
321
|
|
|
310
|
-
|
|
322
|
+
### Development Mode (Recommended for Building & Testing)
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
The fastest way to test and debug your server is with the MCP Inspector:
|
|
313
325
|
|
|
314
|
-
### Development
|
|
315
|
-
|
|
316
|
-
Test and debug your server with the MCP Inspector:
|
|
317
326
|
```bash
|
|
318
|
-
# Provide the fully qualified path to your server
|
|
319
|
-
fastmcp dev server.py:my_mcp_server
|
|
320
|
-
|
|
321
|
-
# Or just the file if your server is named 'mcp', 'server', or 'app'
|
|
322
327
|
fastmcp dev server.py
|
|
323
328
|
```
|
|
324
329
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
# Using your project's dependencies and up-to-date code
|
|
332
|
-
fastmcp dev server.py --with-editable .
|
|
333
|
-
```
|
|
330
|
+
This launches a web interface where you can:
|
|
331
|
+
- Test your tools and resources interactively
|
|
332
|
+
- See detailed logs and error messages
|
|
333
|
+
- Monitor server performance
|
|
334
|
+
- Set environment variables for testing
|
|
334
335
|
|
|
335
|
-
|
|
336
|
+
During development, you can:
|
|
337
|
+
- Add dependencies with `--with`:
|
|
338
|
+
```bash
|
|
339
|
+
fastmcp dev server.py --with pandas --with numpy
|
|
340
|
+
```
|
|
341
|
+
- Mount your local code for live updates:
|
|
342
|
+
```bash
|
|
343
|
+
fastmcp dev server.py --with-editable .
|
|
344
|
+
```
|
|
336
345
|
|
|
337
|
-
|
|
346
|
+
### Claude Desktop Integration (For Regular Use)
|
|
338
347
|
|
|
339
|
-
|
|
348
|
+
Once your server is ready, install it in Claude Desktop to use it with Claude:
|
|
340
349
|
|
|
341
|
-
Install your server in Claude Desktop:
|
|
342
350
|
```bash
|
|
343
|
-
# Basic usage (name is taken from your FastMCP instance)
|
|
344
351
|
fastmcp install server.py
|
|
352
|
+
```
|
|
345
353
|
|
|
346
|
-
|
|
347
|
-
|
|
354
|
+
Your server will run in an isolated environment with:
|
|
355
|
+
- Automatic installation of dependencies specified in your FastMCP instance:
|
|
356
|
+
```python
|
|
357
|
+
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
|
|
358
|
+
```
|
|
359
|
+
- Custom naming via `--name`:
|
|
360
|
+
```bash
|
|
361
|
+
fastmcp install server.py --name "My Analytics Server"
|
|
362
|
+
```
|
|
363
|
+
- Environment variable management:
|
|
364
|
+
```bash
|
|
365
|
+
# Set variables individually
|
|
366
|
+
fastmcp install server.py -e API_KEY=abc123 -e DB_URL=postgres://...
|
|
367
|
+
|
|
368
|
+
# Or load from a .env file
|
|
369
|
+
fastmcp install server.py -f .env
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Direct Execution (For Advanced Use Cases)
|
|
373
|
+
|
|
374
|
+
For advanced scenarios like custom deployments or running without Claude, you can execute your server directly:
|
|
348
375
|
|
|
349
|
-
|
|
350
|
-
fastmcp
|
|
376
|
+
```python
|
|
377
|
+
from fastmcp import FastMCP
|
|
378
|
+
|
|
379
|
+
mcp = FastMCP("My App")
|
|
380
|
+
|
|
381
|
+
if __name__ == "__main__":
|
|
382
|
+
mcp.run()
|
|
351
383
|
```
|
|
352
384
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
385
|
+
Run it with:
|
|
386
|
+
```bash
|
|
387
|
+
# Using the FastMCP CLI
|
|
388
|
+
fastmcp run server.py
|
|
357
389
|
|
|
358
|
-
|
|
390
|
+
# Or with Python/uv directly
|
|
391
|
+
python server.py
|
|
392
|
+
uv run python server.py
|
|
393
|
+
```
|
|
359
394
|
|
|
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
395
|
|
|
362
|
-
|
|
363
|
-
# Single env var
|
|
364
|
-
fastmcp install server.py -e API_KEY=abc123
|
|
396
|
+
Note: When running directly, you are responsible for ensuring all dependencies are available in your environment. Any dependencies specified on the FastMCP instance are ignored.
|
|
365
397
|
|
|
366
|
-
|
|
367
|
-
|
|
398
|
+
Choose this method when you need:
|
|
399
|
+
- Custom deployment configurations
|
|
400
|
+
- Integration with other services
|
|
401
|
+
- Direct control over the server lifecycle
|
|
368
402
|
|
|
369
|
-
|
|
370
|
-
fastmcp install server.py -f .env
|
|
371
|
-
```
|
|
403
|
+
### Server Object Names
|
|
372
404
|
|
|
373
|
-
|
|
405
|
+
All FastMCP commands will look for a server object called `mcp`, `app`, or `server` in your file. If you have a different object name or multiple servers in one file, use the syntax `server.py:my_server`:
|
|
374
406
|
|
|
375
407
|
```bash
|
|
376
|
-
#
|
|
377
|
-
fastmcp
|
|
408
|
+
# Using a standard name
|
|
409
|
+
fastmcp run server.py
|
|
378
410
|
|
|
379
|
-
#
|
|
380
|
-
fastmcp
|
|
381
|
-
|
|
382
|
-
# Third install - FOO gets new value, others preserved
|
|
383
|
-
fastmcp install server.py -e FOO=newvalue
|
|
411
|
+
# Using a custom name
|
|
412
|
+
fastmcp run server.py:my_custom_server
|
|
384
413
|
```
|
|
385
414
|
|
|
386
415
|
## Examples
|
|
@@ -449,3 +478,85 @@ Schema:
|
|
|
449
478
|
|
|
450
479
|
What insights can you provide about the structure and relationships?"""
|
|
451
480
|
```
|
|
481
|
+
|
|
482
|
+
## Contributing
|
|
483
|
+
|
|
484
|
+
<details>
|
|
485
|
+
|
|
486
|
+
<summary><h3>Open Developer Guide</h3></summary>
|
|
487
|
+
|
|
488
|
+
### Prerequisites
|
|
489
|
+
|
|
490
|
+
FastMCP requires Python 3.10+ and [uv](https://docs.astral.sh/uv/).
|
|
491
|
+
|
|
492
|
+
### Installation
|
|
493
|
+
|
|
494
|
+
Create a fork of this repository, then clone it:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
git clone https://github.com/YouFancyUserYou/fastmcp.git
|
|
498
|
+
cd fastmcp
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Next, create a virtual environment and install FastMCP:
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
uv venv
|
|
505
|
+
source .venv/bin/activate
|
|
506
|
+
uv sync --frozen --all-extras --dev
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
### Testing
|
|
512
|
+
|
|
513
|
+
Please make sure to test any new functionality. Your tests should be simple and atomic and anticipate change rather than cement complex patterns.
|
|
514
|
+
|
|
515
|
+
Run tests from the root directory:
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
pytest -vv
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Formatting
|
|
523
|
+
|
|
524
|
+
FastMCP enforces a variety of required formats, which you can automatically enforce with pre-commit.
|
|
525
|
+
|
|
526
|
+
Install the pre-commit hooks:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
pre-commit install
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
The hooks will now run on every commit (as well as on every PR). To run them manually:
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
pre-commit run --all-files
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Opening a Pull Request
|
|
539
|
+
|
|
540
|
+
Fork the repository and create a new branch:
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
git checkout -b my-branch
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Make your changes and commit them:
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
git add . && git commit -m "My changes"
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Push your changes to your fork:
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
git push origin my-branch
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Feel free to reach out in a GitHub issue or discussion if you have any questions!
|
|
561
|
+
|
|
562
|
+
</details>
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
<div align="center">
|
|
5
5
|
|
|
6
|
+
<strong>The fast, Pythonic way to build MCP servers.</strong>
|
|
7
|
+
|
|
6
8
|
[](https://pypi.org/project/fastmcp)
|
|
7
9
|
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
|
8
10
|
[](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
|
|
9
11
|
|
|
10
|
-
The fast, Pythonic way to build MCP servers
|
|
11
12
|
|
|
12
13
|
</div>
|
|
13
14
|
|
|
@@ -61,24 +62,33 @@ FastMCP handles all the complex protocol details and server management, so you c
|
|
|
61
62
|
- [Prompts](#prompts)
|
|
62
63
|
- [Images](#images)
|
|
63
64
|
- [Context](#context)
|
|
64
|
-
- [
|
|
65
|
-
- [Development](#development)
|
|
66
|
-
|
|
67
|
-
- [
|
|
68
|
-
|
|
65
|
+
- [Running Your Server](#running-your-server)
|
|
66
|
+
- [Development Mode (Recommended for Building \& Testing)](#development-mode-recommended-for-building--testing)
|
|
67
|
+
- [Claude Desktop Integration (For Regular Use)](#claude-desktop-integration-for-regular-use)
|
|
68
|
+
- [Direct Execution (For Advanced Use Cases)](#direct-execution-for-advanced-use-cases)
|
|
69
|
+
- [Server Object Names](#server-object-names)
|
|
69
70
|
- [Examples](#examples)
|
|
70
71
|
- [Echo Server](#echo-server)
|
|
71
72
|
- [SQLite Explorer](#sqlite-explorer)
|
|
73
|
+
- [Contributing](#contributing)
|
|
74
|
+
- [Prerequisites](#prerequisites)
|
|
75
|
+
- [Installation](#installation-1)
|
|
76
|
+
- [Testing](#testing)
|
|
77
|
+
- [Formatting](#formatting)
|
|
78
|
+
- [Opening a Pull Request](#opening-a-pull-request)
|
|
72
79
|
|
|
73
80
|
## Installation
|
|
74
81
|
|
|
82
|
+
We strongly recommend installing FastMCP with [uv](https://docs.astral.sh/uv/), as it is required for deploying servers:
|
|
83
|
+
|
|
75
84
|
```bash
|
|
76
|
-
# We strongly recommend installing with uv
|
|
77
|
-
brew install uv # on macOS
|
|
78
85
|
uv pip install fastmcp
|
|
79
86
|
```
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
Note: on macOS, uv may need to be installed with Homebrew (`brew install uv`) in order to make it available to the Claude Desktop app.
|
|
89
|
+
|
|
90
|
+
Alternatively, to use the SDK without deploying, you may use pip:
|
|
91
|
+
|
|
82
92
|
```bash
|
|
83
93
|
pip install fastmcp
|
|
84
94
|
```
|
|
@@ -149,8 +159,10 @@ mcp = FastMCP("My App")
|
|
|
149
159
|
|
|
150
160
|
# Configure host/port for HTTP transport (optional)
|
|
151
161
|
mcp = FastMCP("My App", host="localhost", port=8000)
|
|
162
|
+
|
|
163
|
+
# Specify dependencies for deployment and development
|
|
164
|
+
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
|
|
152
165
|
```
|
|
153
|
-
*Note: All of the following code examples assume you've created a FastMCP server instance called `mcp`, as shown above.*
|
|
154
166
|
|
|
155
167
|
### Resources
|
|
156
168
|
|
|
@@ -279,84 +291,101 @@ The Context object provides:
|
|
|
279
291
|
- Resource access through `read_resource()`
|
|
280
292
|
- Request metadata via `request_id` and `client_id`
|
|
281
293
|
|
|
282
|
-
##
|
|
294
|
+
## Running Your Server
|
|
283
295
|
|
|
284
|
-
|
|
296
|
+
There are three main ways to use your FastMCP server, each suited for different stages of development:
|
|
285
297
|
|
|
286
|
-
|
|
298
|
+
### Development Mode (Recommended for Building & Testing)
|
|
287
299
|
|
|
288
|
-
|
|
300
|
+
The fastest way to test and debug your server is with the MCP Inspector:
|
|
289
301
|
|
|
290
|
-
### Development
|
|
291
|
-
|
|
292
|
-
Test and debug your server with the MCP Inspector:
|
|
293
302
|
```bash
|
|
294
|
-
# Provide the fully qualified path to your server
|
|
295
|
-
fastmcp dev server.py:my_mcp_server
|
|
296
|
-
|
|
297
|
-
# Or just the file if your server is named 'mcp', 'server', or 'app'
|
|
298
303
|
fastmcp dev server.py
|
|
299
304
|
```
|
|
300
305
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
# Using your project's dependencies and up-to-date code
|
|
308
|
-
fastmcp dev server.py --with-editable .
|
|
309
|
-
```
|
|
306
|
+
This launches a web interface where you can:
|
|
307
|
+
- Test your tools and resources interactively
|
|
308
|
+
- See detailed logs and error messages
|
|
309
|
+
- Monitor server performance
|
|
310
|
+
- Set environment variables for testing
|
|
310
311
|
|
|
311
|
-
|
|
312
|
+
During development, you can:
|
|
313
|
+
- Add dependencies with `--with`:
|
|
314
|
+
```bash
|
|
315
|
+
fastmcp dev server.py --with pandas --with numpy
|
|
316
|
+
```
|
|
317
|
+
- Mount your local code for live updates:
|
|
318
|
+
```bash
|
|
319
|
+
fastmcp dev server.py --with-editable .
|
|
320
|
+
```
|
|
312
321
|
|
|
313
|
-
|
|
322
|
+
### Claude Desktop Integration (For Regular Use)
|
|
314
323
|
|
|
315
|
-
|
|
324
|
+
Once your server is ready, install it in Claude Desktop to use it with Claude:
|
|
316
325
|
|
|
317
|
-
Install your server in Claude Desktop:
|
|
318
326
|
```bash
|
|
319
|
-
# Basic usage (name is taken from your FastMCP instance)
|
|
320
327
|
fastmcp install server.py
|
|
328
|
+
```
|
|
321
329
|
|
|
322
|
-
|
|
323
|
-
|
|
330
|
+
Your server will run in an isolated environment with:
|
|
331
|
+
- Automatic installation of dependencies specified in your FastMCP instance:
|
|
332
|
+
```python
|
|
333
|
+
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
|
|
334
|
+
```
|
|
335
|
+
- Custom naming via `--name`:
|
|
336
|
+
```bash
|
|
337
|
+
fastmcp install server.py --name "My Analytics Server"
|
|
338
|
+
```
|
|
339
|
+
- Environment variable management:
|
|
340
|
+
```bash
|
|
341
|
+
# Set variables individually
|
|
342
|
+
fastmcp install server.py -e API_KEY=abc123 -e DB_URL=postgres://...
|
|
343
|
+
|
|
344
|
+
# Or load from a .env file
|
|
345
|
+
fastmcp install server.py -f .env
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Direct Execution (For Advanced Use Cases)
|
|
349
|
+
|
|
350
|
+
For advanced scenarios like custom deployments or running without Claude, you can execute your server directly:
|
|
324
351
|
|
|
325
|
-
|
|
326
|
-
fastmcp
|
|
352
|
+
```python
|
|
353
|
+
from fastmcp import FastMCP
|
|
354
|
+
|
|
355
|
+
mcp = FastMCP("My App")
|
|
356
|
+
|
|
357
|
+
if __name__ == "__main__":
|
|
358
|
+
mcp.run()
|
|
327
359
|
```
|
|
328
360
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
361
|
+
Run it with:
|
|
362
|
+
```bash
|
|
363
|
+
# Using the FastMCP CLI
|
|
364
|
+
fastmcp run server.py
|
|
333
365
|
|
|
334
|
-
|
|
366
|
+
# Or with Python/uv directly
|
|
367
|
+
python server.py
|
|
368
|
+
uv run python server.py
|
|
369
|
+
```
|
|
335
370
|
|
|
336
|
-
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:
|
|
337
371
|
|
|
338
|
-
|
|
339
|
-
# Single env var
|
|
340
|
-
fastmcp install server.py -e API_KEY=abc123
|
|
372
|
+
Note: When running directly, you are responsible for ensuring all dependencies are available in your environment. Any dependencies specified on the FastMCP instance are ignored.
|
|
341
373
|
|
|
342
|
-
|
|
343
|
-
|
|
374
|
+
Choose this method when you need:
|
|
375
|
+
- Custom deployment configurations
|
|
376
|
+
- Integration with other services
|
|
377
|
+
- Direct control over the server lifecycle
|
|
344
378
|
|
|
345
|
-
|
|
346
|
-
fastmcp install server.py -f .env
|
|
347
|
-
```
|
|
379
|
+
### Server Object Names
|
|
348
380
|
|
|
349
|
-
|
|
381
|
+
All FastMCP commands will look for a server object called `mcp`, `app`, or `server` in your file. If you have a different object name or multiple servers in one file, use the syntax `server.py:my_server`:
|
|
350
382
|
|
|
351
383
|
```bash
|
|
352
|
-
#
|
|
353
|
-
fastmcp
|
|
384
|
+
# Using a standard name
|
|
385
|
+
fastmcp run server.py
|
|
354
386
|
|
|
355
|
-
#
|
|
356
|
-
fastmcp
|
|
357
|
-
|
|
358
|
-
# Third install - FOO gets new value, others preserved
|
|
359
|
-
fastmcp install server.py -e FOO=newvalue
|
|
387
|
+
# Using a custom name
|
|
388
|
+
fastmcp run server.py:my_custom_server
|
|
360
389
|
```
|
|
361
390
|
|
|
362
391
|
## Examples
|
|
@@ -425,3 +454,85 @@ Schema:
|
|
|
425
454
|
|
|
426
455
|
What insights can you provide about the structure and relationships?"""
|
|
427
456
|
```
|
|
457
|
+
|
|
458
|
+
## Contributing
|
|
459
|
+
|
|
460
|
+
<details>
|
|
461
|
+
|
|
462
|
+
<summary><h3>Open Developer Guide</h3></summary>
|
|
463
|
+
|
|
464
|
+
### Prerequisites
|
|
465
|
+
|
|
466
|
+
FastMCP requires Python 3.10+ and [uv](https://docs.astral.sh/uv/).
|
|
467
|
+
|
|
468
|
+
### Installation
|
|
469
|
+
|
|
470
|
+
Create a fork of this repository, then clone it:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
git clone https://github.com/YouFancyUserYou/fastmcp.git
|
|
474
|
+
cd fastmcp
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Next, create a virtual environment and install FastMCP:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
uv venv
|
|
481
|
+
source .venv/bin/activate
|
|
482
|
+
uv sync --frozen --all-extras --dev
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
### Testing
|
|
488
|
+
|
|
489
|
+
Please make sure to test any new functionality. Your tests should be simple and atomic and anticipate change rather than cement complex patterns.
|
|
490
|
+
|
|
491
|
+
Run tests from the root directory:
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
pytest -vv
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Formatting
|
|
499
|
+
|
|
500
|
+
FastMCP enforces a variety of required formats, which you can automatically enforce with pre-commit.
|
|
501
|
+
|
|
502
|
+
Install the pre-commit hooks:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
pre-commit install
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
The hooks will now run on every commit (as well as on every PR). To run them manually:
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
pre-commit run --all-files
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Opening a Pull Request
|
|
515
|
+
|
|
516
|
+
Fork the repository and create a new branch:
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
git checkout -b my-branch
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Make your changes and commit them:
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
git add . && git commit -m "My changes"
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Push your changes to your fork:
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
git push origin my-branch
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Feel free to reach out in a GitHub issue or discussion if you have any questions!
|
|
537
|
+
|
|
538
|
+
</details>
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# dependencies = ["fastmcp", "pyautogui", "Pillow"]
|
|
3
|
-
# ///
|
|
4
|
-
|
|
5
1
|
"""
|
|
6
2
|
FastMCP Screenshot Example
|
|
7
3
|
|
|
@@ -9,20 +5,24 @@ Give Claude a tool to capture and view screenshots.
|
|
|
9
5
|
"""
|
|
10
6
|
|
|
11
7
|
import io
|
|
12
|
-
|
|
13
8
|
from fastmcp import FastMCP, Image
|
|
14
9
|
|
|
10
|
+
|
|
15
11
|
# Create server
|
|
16
|
-
mcp = FastMCP("Screenshot Demo")
|
|
12
|
+
mcp = FastMCP("Screenshot Demo", dependencies=["pyautogui", "Pillow"])
|
|
17
13
|
|
|
18
14
|
|
|
19
15
|
@mcp.tool()
|
|
20
16
|
def take_screenshot() -> Image:
|
|
21
|
-
"""
|
|
17
|
+
"""
|
|
18
|
+
Take a screenshot of the user's screen and return it as an image. Use
|
|
19
|
+
this tool anytime the user wants you to look at something they're doing.
|
|
20
|
+
"""
|
|
22
21
|
import pyautogui
|
|
23
22
|
|
|
24
|
-
screenshot = pyautogui.screenshot()
|
|
25
23
|
buffer = io.BytesIO()
|
|
24
|
+
|
|
26
25
|
# if the file exceeds ~1MB, it will be rejected by Claude
|
|
26
|
+
screenshot = pyautogui.screenshot()
|
|
27
27
|
screenshot.convert("RGB").save(buffer, format="JPEG", quality=60, optimize=True)
|
|
28
28
|
return Image(data=buffer.getvalue(), format="jpeg")
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# dependencies = ["fastmcp"]
|
|
3
|
+
# ///
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
FastMCP Text Me Server
|
|
7
|
+
--------------------------------
|
|
8
|
+
This defines a simple FastMCP server that sends a text message to a phone number via https://surgemsg.com/.
|
|
9
|
+
|
|
10
|
+
To run this example, create a `.env` file with the following values:
|
|
11
|
+
|
|
12
|
+
SURGE_API_KEY=...
|
|
13
|
+
SURGE_ACCOUNT_ID=...
|
|
14
|
+
SURGE_MY_PHONE_NUMBER=...
|
|
15
|
+
SURGE_MY_FIRST_NAME=...
|
|
16
|
+
SURGE_MY_LAST_NAME=...
|
|
17
|
+
|
|
18
|
+
Visit https://surgemsg.com/ and click "Get Started" to obtain these values.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Annotated
|
|
22
|
+
import httpx
|
|
23
|
+
from pydantic import BeforeValidator
|
|
24
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
25
|
+
|
|
26
|
+
from fastmcp import FastMCP
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SurgeSettings(BaseSettings):
|
|
30
|
+
model_config: SettingsConfigDict = SettingsConfigDict(
|
|
31
|
+
env_prefix="SURGE_", env_file=".env"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
api_key: str
|
|
35
|
+
account_id: str
|
|
36
|
+
my_phone_number: Annotated[
|
|
37
|
+
str, BeforeValidator(lambda v: "+" + v if not v.startswith("+") else v)
|
|
38
|
+
]
|
|
39
|
+
my_first_name: str
|
|
40
|
+
my_last_name: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Create server
|
|
44
|
+
mcp = FastMCP("Text me")
|
|
45
|
+
surge_settings = SurgeSettings() # type: ignore
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@mcp.tool(name="textme", description="Send a text message to me")
|
|
49
|
+
def text_me(text_content: str) -> str:
|
|
50
|
+
"""Send a text message to a phone number via https://surgemsg.com/"""
|
|
51
|
+
with httpx.Client() as client:
|
|
52
|
+
response = client.post(
|
|
53
|
+
"https://api.surgemsg.com/messages",
|
|
54
|
+
headers={
|
|
55
|
+
"Authorization": f"Bearer {surge_settings.api_key}",
|
|
56
|
+
"Surge-Account": surge_settings.account_id,
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
},
|
|
59
|
+
json={
|
|
60
|
+
"body": text_content,
|
|
61
|
+
"conversation": {
|
|
62
|
+
"contact": {
|
|
63
|
+
"first_name": surge_settings.my_first_name,
|
|
64
|
+
"last_name": surge_settings.my_last_name,
|
|
65
|
+
"phone_number": surge_settings.my_phone_number,
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
response.raise_for_status()
|
|
71
|
+
return f"Message sent: {text_content}"
|
|
@@ -68,16 +68,20 @@ def update_claude_config(
|
|
|
68
68
|
env_vars = existing_env
|
|
69
69
|
|
|
70
70
|
# Build uv run command
|
|
71
|
-
args = ["run"
|
|
71
|
+
args = ["run"]
|
|
72
|
+
|
|
73
|
+
# Collect all packages in a set to deduplicate
|
|
74
|
+
packages = {"fastmcp"}
|
|
75
|
+
if with_packages:
|
|
76
|
+
packages.update(pkg for pkg in with_packages if pkg)
|
|
77
|
+
|
|
78
|
+
# Add all packages with --with
|
|
79
|
+
for pkg in sorted(packages):
|
|
80
|
+
args.extend(["--with", pkg])
|
|
72
81
|
|
|
73
82
|
if with_editable:
|
|
74
83
|
args.extend(["--with-editable", str(with_editable)])
|
|
75
84
|
|
|
76
|
-
if with_packages:
|
|
77
|
-
for pkg in with_packages:
|
|
78
|
-
if pkg:
|
|
79
|
-
args.extend(["--with", pkg])
|
|
80
|
-
|
|
81
85
|
# Convert file path to absolute before adding to command
|
|
82
86
|
# Split off any :object suffix first
|
|
83
87
|
if ":" in file_spec:
|
|
@@ -193,6 +193,11 @@ def dev(
|
|
|
193
193
|
)
|
|
194
194
|
|
|
195
195
|
try:
|
|
196
|
+
# Import server to get dependencies
|
|
197
|
+
server = _import_server(file, server_object)
|
|
198
|
+
if hasattr(server, "dependencies"):
|
|
199
|
+
with_packages = list(set(with_packages + server.dependencies))
|
|
200
|
+
|
|
196
201
|
uv_cmd = _build_uv_command(file_spec, with_editable, with_packages)
|
|
197
202
|
# Run the MCP Inspector command
|
|
198
203
|
process = subprocess.run(
|
|
@@ -232,23 +237,16 @@ def run(
|
|
|
232
237
|
help="Transport protocol to use (stdio or sse)",
|
|
233
238
|
),
|
|
234
239
|
] = None,
|
|
235
|
-
with_editable: Annotated[
|
|
236
|
-
Optional[Path],
|
|
237
|
-
typer.Option(
|
|
238
|
-
"--with-editable",
|
|
239
|
-
"-e",
|
|
240
|
-
help="Directory containing pyproject.toml to install in editable mode",
|
|
241
|
-
exists=True,
|
|
242
|
-
file_okay=False,
|
|
243
|
-
resolve_path=True,
|
|
244
|
-
),
|
|
245
|
-
] = None,
|
|
246
240
|
) -> None:
|
|
247
241
|
"""Run a FastMCP server.
|
|
248
242
|
|
|
249
243
|
The server can be specified in two ways:
|
|
250
244
|
1. Module approach: server.py - runs the module directly, expecting a server.run() call
|
|
251
245
|
2. Import approach: server.py:app - imports and runs the specified server object
|
|
246
|
+
|
|
247
|
+
Note: This command runs the server directly. You are responsible for ensuring
|
|
248
|
+
all dependencies are available. For dependency management, use fastmcp install
|
|
249
|
+
or fastmcp dev instead.
|
|
252
250
|
"""
|
|
253
251
|
file, server_object = _parse_file_path(file_spec)
|
|
254
252
|
|
|
@@ -258,7 +256,6 @@ def run(
|
|
|
258
256
|
"file": str(file),
|
|
259
257
|
"server_object": server_object,
|
|
260
258
|
"transport": transport,
|
|
261
|
-
"with_editable": str(with_editable) if with_editable else None,
|
|
262
259
|
},
|
|
263
260
|
)
|
|
264
261
|
|
|
@@ -361,6 +358,7 @@ def install(
|
|
|
361
358
|
|
|
362
359
|
# Try to import server to get its name, but fall back to file name if dependencies missing
|
|
363
360
|
name = server_name
|
|
361
|
+
server = None
|
|
364
362
|
if not name:
|
|
365
363
|
try:
|
|
366
364
|
server = _import_server(file, server_object)
|
|
@@ -372,6 +370,11 @@ def install(
|
|
|
372
370
|
)
|
|
373
371
|
name = file.stem
|
|
374
372
|
|
|
373
|
+
# Get server dependencies if available
|
|
374
|
+
server_dependencies = getattr(server, "dependencies", []) if server else []
|
|
375
|
+
if server_dependencies:
|
|
376
|
+
with_packages = list(set(with_packages + server_dependencies))
|
|
377
|
+
|
|
375
378
|
# Process environment variables if provided
|
|
376
379
|
env_dict: Optional[Dict[str, str]] = None
|
|
377
380
|
if env_file or env_vars:
|
|
@@ -9,6 +9,7 @@ from itertools import chain
|
|
|
9
9
|
from typing import Any, Callable, Dict, Literal, Sequence
|
|
10
10
|
|
|
11
11
|
import pydantic_core
|
|
12
|
+
from pydantic import Field
|
|
12
13
|
import uvicorn
|
|
13
14
|
from mcp.server import Server as MCPServer
|
|
14
15
|
from mcp.server.sse import SseServerTransport
|
|
@@ -76,6 +77,11 @@ class Settings(BaseSettings):
|
|
|
76
77
|
# prompt settings
|
|
77
78
|
warn_on_duplicate_prompts: bool = True
|
|
78
79
|
|
|
80
|
+
dependencies: list[str] = Field(
|
|
81
|
+
default_factory=list,
|
|
82
|
+
description="List of dependencies to install in the server environment",
|
|
83
|
+
)
|
|
84
|
+
|
|
79
85
|
|
|
80
86
|
class FastMCP:
|
|
81
87
|
def __init__(self, name: str | None = None, **settings: Any):
|
|
@@ -90,6 +96,7 @@ class FastMCP:
|
|
|
90
96
|
self._prompt_manager = PromptManager(
|
|
91
97
|
warn_on_duplicate_prompts=self.settings.warn_on_duplicate_prompts
|
|
92
98
|
)
|
|
99
|
+
self.dependencies = self.settings.dependencies
|
|
93
100
|
|
|
94
101
|
# Set up MCP protocol handlers
|
|
95
102
|
self._setup_handlers()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Tests for the FastMCP CLI."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from unittest.mock import
|
|
4
|
+
from unittest.mock import patch
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
from typer.testing import CliRunner
|
|
@@ -19,11 +19,13 @@ def mock_config(tmp_path):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
@pytest.fixture
|
|
22
|
-
def
|
|
23
|
-
"""Create a
|
|
22
|
+
def server_file(tmp_path):
|
|
23
|
+
"""Create a server file."""
|
|
24
24
|
server_file = tmp_path / "server.py"
|
|
25
25
|
server_file.write_text(
|
|
26
|
-
"from fastmcp import
|
|
26
|
+
"""from fastmcp import FastMCP
|
|
27
|
+
mcp = FastMCP("test")
|
|
28
|
+
"""
|
|
27
29
|
)
|
|
28
30
|
return server_file
|
|
29
31
|
|
|
@@ -67,22 +69,16 @@ def test_parse_env_var():
|
|
|
67
69
|
),
|
|
68
70
|
],
|
|
69
71
|
)
|
|
70
|
-
def test_install_with_env_vars(mock_config,
|
|
72
|
+
def test_install_with_env_vars(mock_config, server_file, args, expected_env):
|
|
71
73
|
"""Test installing with environment variables."""
|
|
72
74
|
runner = CliRunner()
|
|
73
75
|
|
|
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
|
-
):
|
|
76
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
78
77
|
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
78
|
|
|
83
79
|
result = runner.invoke(
|
|
84
80
|
app,
|
|
85
|
-
["install", str(
|
|
81
|
+
["install", str(server_file)] + args,
|
|
86
82
|
)
|
|
87
83
|
|
|
88
84
|
assert result.exit_code == 0
|
|
@@ -95,22 +91,16 @@ def test_install_with_env_vars(mock_config, mock_server_file, args, expected_env
|
|
|
95
91
|
assert server["env"] == expected_env
|
|
96
92
|
|
|
97
93
|
|
|
98
|
-
def test_install_with_env_file(mock_config,
|
|
94
|
+
def test_install_with_env_file(mock_config, server_file, mock_env_file):
|
|
99
95
|
"""Test installing with environment variables from a file."""
|
|
100
96
|
runner = CliRunner()
|
|
101
97
|
|
|
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
|
-
):
|
|
98
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
106
99
|
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
100
|
|
|
111
101
|
result = runner.invoke(
|
|
112
102
|
app,
|
|
113
|
-
["install", str(
|
|
103
|
+
["install", str(server_file), "--env-file", str(mock_env_file)],
|
|
114
104
|
)
|
|
115
105
|
|
|
116
106
|
assert result.exit_code == 0
|
|
@@ -123,7 +113,7 @@ def test_install_with_env_file(mock_config, mock_server_file, mock_env_file):
|
|
|
123
113
|
assert server["env"] == {"FOO": "bar", "BAZ": "123"}
|
|
124
114
|
|
|
125
115
|
|
|
126
|
-
def test_install_preserves_existing_env_vars(mock_config,
|
|
116
|
+
def test_install_preserves_existing_env_vars(mock_config, server_file):
|
|
127
117
|
"""Test that installing preserves existing environment variables."""
|
|
128
118
|
# Set up initial config with env vars
|
|
129
119
|
config = {
|
|
@@ -136,7 +126,7 @@ def test_install_preserves_existing_env_vars(mock_config, mock_server_file):
|
|
|
136
126
|
"fastmcp",
|
|
137
127
|
"fastmcp",
|
|
138
128
|
"run",
|
|
139
|
-
str(
|
|
129
|
+
str(server_file),
|
|
140
130
|
],
|
|
141
131
|
"env": {"FOO": "bar", "BAZ": "123"},
|
|
142
132
|
}
|
|
@@ -146,19 +136,13 @@ def test_install_preserves_existing_env_vars(mock_config, mock_server_file):
|
|
|
146
136
|
|
|
147
137
|
runner = CliRunner()
|
|
148
138
|
|
|
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
|
-
):
|
|
139
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
153
140
|
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
141
|
|
|
158
142
|
# Install with a new env var
|
|
159
143
|
result = runner.invoke(
|
|
160
144
|
app,
|
|
161
|
-
["install", str(
|
|
145
|
+
["install", str(server_file), "--env-var", "NEW=value"],
|
|
162
146
|
)
|
|
163
147
|
|
|
164
148
|
assert result.exit_code == 0
|
|
@@ -169,7 +153,7 @@ def test_install_preserves_existing_env_vars(mock_config, mock_server_file):
|
|
|
169
153
|
assert server["env"] == {"FOO": "bar", "BAZ": "123", "NEW": "value"}
|
|
170
154
|
|
|
171
155
|
|
|
172
|
-
def test_install_updates_existing_env_vars(mock_config,
|
|
156
|
+
def test_install_updates_existing_env_vars(mock_config, server_file):
|
|
173
157
|
"""Test that installing updates existing environment variables."""
|
|
174
158
|
# Set up initial config with env vars
|
|
175
159
|
config = {
|
|
@@ -182,7 +166,7 @@ def test_install_updates_existing_env_vars(mock_config, mock_server_file):
|
|
|
182
166
|
"fastmcp",
|
|
183
167
|
"fastmcp",
|
|
184
168
|
"run",
|
|
185
|
-
str(
|
|
169
|
+
str(server_file),
|
|
186
170
|
],
|
|
187
171
|
"env": {"FOO": "bar", "BAZ": "123"},
|
|
188
172
|
}
|
|
@@ -192,19 +176,13 @@ def test_install_updates_existing_env_vars(mock_config, mock_server_file):
|
|
|
192
176
|
|
|
193
177
|
runner = CliRunner()
|
|
194
178
|
|
|
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
|
-
):
|
|
179
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
199
180
|
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
181
|
|
|
204
182
|
# Update an existing env var
|
|
205
183
|
result = runner.invoke(
|
|
206
184
|
app,
|
|
207
|
-
["install", str(
|
|
185
|
+
["install", str(server_file), "--env-var", "FOO=newvalue"],
|
|
208
186
|
)
|
|
209
187
|
|
|
210
188
|
assert result.exit_code == 0
|
|
@@ -213,3 +191,101 @@ def test_install_updates_existing_env_vars(mock_config, mock_server_file):
|
|
|
213
191
|
config = json.loads(mock_config.read_text())
|
|
214
192
|
server = next(iter(config["mcpServers"].values()))
|
|
215
193
|
assert server["env"] == {"FOO": "newvalue", "BAZ": "123"}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_server_dependencies(mock_config, server_file):
|
|
197
|
+
"""Test that server dependencies are correctly handled."""
|
|
198
|
+
# Create a server file with dependencies
|
|
199
|
+
server_file = server_file.parent / "server_with_deps.py"
|
|
200
|
+
server_file.write_text(
|
|
201
|
+
"""from fastmcp import FastMCP
|
|
202
|
+
mcp = FastMCP("test", dependencies=["pandas", "numpy"])
|
|
203
|
+
"""
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
runner = CliRunner()
|
|
207
|
+
|
|
208
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
209
|
+
mock_config_path.return_value = mock_config.parent
|
|
210
|
+
|
|
211
|
+
result = runner.invoke(app, ["install", str(server_file)])
|
|
212
|
+
|
|
213
|
+
assert result.exit_code == 0
|
|
214
|
+
|
|
215
|
+
# Read the config file and check dependencies were added as --with args
|
|
216
|
+
config = json.loads(mock_config.read_text())
|
|
217
|
+
server = next(iter(config["mcpServers"].values()))
|
|
218
|
+
assert "--with" in server["args"]
|
|
219
|
+
assert "pandas" in server["args"]
|
|
220
|
+
assert "numpy" in server["args"]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_server_dependencies_empty(mock_config, server_file):
|
|
224
|
+
"""Test that server with no dependencies works correctly."""
|
|
225
|
+
runner = CliRunner()
|
|
226
|
+
|
|
227
|
+
with patch("fastmcp.cli.claude.get_claude_config_path") as mock_config_path:
|
|
228
|
+
mock_config_path.return_value = mock_config.parent
|
|
229
|
+
|
|
230
|
+
result = runner.invoke(app, ["install", str(server_file)])
|
|
231
|
+
|
|
232
|
+
assert result.exit_code == 0
|
|
233
|
+
|
|
234
|
+
# Read the config file and check only fastmcp is in --with args
|
|
235
|
+
config = json.loads(mock_config.read_text())
|
|
236
|
+
server = next(iter(config["mcpServers"].values()))
|
|
237
|
+
assert server["args"].count("--with") == 1
|
|
238
|
+
assert "fastmcp" in server["args"]
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_dev_with_dependencies(mock_config, server_file):
|
|
242
|
+
"""Test that dev command handles dependencies correctly."""
|
|
243
|
+
# Create a server file with dependencies
|
|
244
|
+
server_file = server_file.parent / "server_with_deps.py"
|
|
245
|
+
server_file.write_text(
|
|
246
|
+
"""from fastmcp import FastMCP
|
|
247
|
+
mcp = FastMCP("test", dependencies=["pandas", "numpy"])
|
|
248
|
+
"""
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
runner = CliRunner()
|
|
252
|
+
|
|
253
|
+
with patch("subprocess.run") as mock_run:
|
|
254
|
+
mock_run.return_value.returncode = 0 # Set successful return code
|
|
255
|
+
result = runner.invoke(app, ["dev", str(server_file)])
|
|
256
|
+
assert result.exit_code == 0
|
|
257
|
+
|
|
258
|
+
# Check that dependencies were passed to subprocess.run
|
|
259
|
+
mock_run.assert_called_once()
|
|
260
|
+
args = mock_run.call_args[0][0]
|
|
261
|
+
assert "npx" in args
|
|
262
|
+
assert "@modelcontextprotocol/inspector" in args
|
|
263
|
+
assert "uv" in args
|
|
264
|
+
assert "run" in args
|
|
265
|
+
assert "--with" in args
|
|
266
|
+
assert "pandas" in args
|
|
267
|
+
assert "numpy" in args
|
|
268
|
+
assert "fastmcp" in args
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def test_run_with_dependencies(mock_config, server_file):
|
|
272
|
+
"""Test that run command does not handle dependencies."""
|
|
273
|
+
# Create a server file with dependencies
|
|
274
|
+
server_file = server_file.parent / "server_with_deps.py"
|
|
275
|
+
server_file.write_text(
|
|
276
|
+
"""from fastmcp import FastMCP
|
|
277
|
+
mcp = FastMCP("test", dependencies=["pandas", "numpy"])
|
|
278
|
+
|
|
279
|
+
if __name__ == "__main__":
|
|
280
|
+
mcp.run()
|
|
281
|
+
"""
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
runner = CliRunner()
|
|
285
|
+
|
|
286
|
+
with patch("subprocess.run") as mock_run:
|
|
287
|
+
result = runner.invoke(app, ["run", str(server_file)])
|
|
288
|
+
assert result.exit_code == 0
|
|
289
|
+
|
|
290
|
+
# Run command should not call subprocess.run
|
|
291
|
+
mock_run.assert_not_called()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|