mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-24 06:24:18 +02:00
Merge pull request #43 from justjoehere/windows2
Additional Windows Fixes for Dev running and for importing modules in a server
This commit is contained in:
commit
7bf0e1e6d1
3 changed files with 131 additions and 18 deletions
|
|
@ -1,10 +1,14 @@
|
|||
# Getting your development environment set up properly
|
||||
To get your environment up and running properly, you'll need a slightly different set of commands that are windows specific:
|
||||
```bash
|
||||
uv venv
|
||||
.venv\Scripts\activate
|
||||
uv pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
This will install the package in editable mode, and install the development dependencies.
|
||||
|
||||
|
||||
# Fixing `AttributeError: module 'collections' has no attribute 'Callable'`
|
||||
- open `.venv\Lib\site-packages\pyreadline\py3k_compat.py`
|
||||
- change `return isinstance(x, collections.Callable)` to
|
||||
|
|
@ -13,3 +17,42 @@ from collections.abc import Callable
|
|||
return isinstance(x, Callable)
|
||||
```
|
||||
|
||||
# Helpful notes
|
||||
For developing FastMCP
|
||||
## Install local development version of FastMCP into a local FastMCP project server
|
||||
- ensure
|
||||
- change directories to your FastMCP Server location so you can install it in your .venv
|
||||
- run `.venv\Scripts\activate` to activate your virtual environment
|
||||
- Then run a series of commands to uninstall the old version and install the new
|
||||
```bash
|
||||
# First uninstall
|
||||
uv pip uninstall fastmcp
|
||||
|
||||
# Clean any build artifacts in your fastmcp directory
|
||||
cd C:\path\to\fastmcp
|
||||
del /s /q *.egg-info
|
||||
|
||||
# Then reinstall in your weather project
|
||||
cd C:\path\to\new\fastmcp_server
|
||||
uv pip install --no-cache-dir -e C:\Users\justj\PycharmProjects\fastmcp
|
||||
|
||||
# Check that it installed properly and has the correct git hash
|
||||
pip show fastmcp
|
||||
```
|
||||
|
||||
## Running the FastMCP server with Inspector
|
||||
MCP comes with a node.js application called Inspector that can be used to inspect the FastMCP server. To run the inspector, you'll need to install node.js and npm. Then you can run the following commands:
|
||||
```bash
|
||||
fastmcp dev server.py
|
||||
```
|
||||
This will launch a web app on http://localhost:5173/ that you can use to inspect the FastMCP server.
|
||||
|
||||
## If you start development before creating a fork - your get out of jail free card
|
||||
- Add your fork as a new remote to your local repository `git remote add fork git@github.com:YOUR-USERNAME/REPOSITORY-NAME.git`
|
||||
- This will add your repo, short named 'fork', as a remote to your local repository
|
||||
- Verify that it was added correctly by running `git remote -v`
|
||||
- Commit your changes
|
||||
- Push your changes to your fork `git push fork <branch>`
|
||||
- Create your pull request on GitHub
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import typer
|
|||
from typing_extensions import Annotated
|
||||
import dotenv
|
||||
|
||||
from ..utilities.logging import get_logger
|
||||
from . import claude
|
||||
from fastmcp.cli import claude
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
logger = get_logger("cli")
|
||||
|
||||
|
|
@ -24,6 +24,22 @@ app = typer.Typer(
|
|||
)
|
||||
|
||||
|
||||
def _get_npx_command():
|
||||
"""Get the correct npx command for the current platform."""
|
||||
if sys.platform == "win32":
|
||||
# Try both npx.cmd and npx.exe on Windows
|
||||
for cmd in ["npx.cmd", "npx.exe", "npx"]:
|
||||
try:
|
||||
subprocess.run(
|
||||
[cmd, "--version"], check=True, capture_output=True, shell=True
|
||||
)
|
||||
return cmd
|
||||
except subprocess.CalledProcessError:
|
||||
continue
|
||||
return None
|
||||
return "npx" # On Unix-like systems, just use npx
|
||||
|
||||
|
||||
def _parse_env_var(env_var: str) -> Tuple[str, str]:
|
||||
"""Parse environment variable string in format KEY=VALUE."""
|
||||
if "=" not in env_var:
|
||||
|
|
@ -99,6 +115,11 @@ def _import_server(file: Path, server_object: Optional[str] = None):
|
|||
Returns:
|
||||
The server object
|
||||
"""
|
||||
# Add parent directory to Python path so imports can be resolved
|
||||
file_dir = str(file.parent)
|
||||
if file_dir not in sys.path:
|
||||
sys.path.insert(0, file_dir)
|
||||
|
||||
# Import the module
|
||||
spec = importlib.util.spec_from_file_location("server_module", file)
|
||||
if not spec or not spec.loader:
|
||||
|
|
@ -205,10 +226,22 @@ def dev(
|
|||
with_packages = list(set(with_packages + server.dependencies))
|
||||
|
||||
uv_cmd = _build_uv_command(file_spec, with_editable, with_packages)
|
||||
# Run the MCP Inspector command
|
||||
|
||||
# Get the correct npx command
|
||||
npx_cmd = _get_npx_command()
|
||||
if not npx_cmd:
|
||||
logger.error(
|
||||
"npx not found. Please ensure Node.js and npm are properly installed "
|
||||
"and added to your system PATH."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Run the MCP Inspector command with shell=True on Windows
|
||||
shell = sys.platform == "win32"
|
||||
process = subprocess.run(
|
||||
["npx", "@modelcontextprotocol/inspector"] + uv_cmd,
|
||||
[npx_cmd, "@modelcontextprotocol/inspector"] + uv_cmd,
|
||||
check=True,
|
||||
shell=shell,
|
||||
)
|
||||
sys.exit(process.returncode)
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
|
@ -223,7 +256,9 @@ def dev(
|
|||
sys.exit(e.returncode)
|
||||
except FileNotFoundError:
|
||||
logger.error(
|
||||
"npx not found. Please install Node.js and npm.",
|
||||
"npx not found. Please ensure Node.js and npm are properly installed "
|
||||
"and added to your system PATH. You may need to restart your terminal "
|
||||
"after installation.",
|
||||
extra={"file": str(file)},
|
||||
)
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Tests for the FastMCP CLI."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch, call
|
||||
|
||||
|
|
@ -276,7 +277,6 @@ def test_server_dependencies_empty(mock_config, server_file):
|
|||
|
||||
def test_dev_with_dependencies(mock_config, server_file):
|
||||
"""Test that dev command handles dependencies correctly."""
|
||||
# Create a server file with dependencies
|
||||
server_file = server_file.parent / "server_with_deps.py"
|
||||
server_file.write_text(
|
||||
"""from fastmcp import FastMCP
|
||||
|
|
@ -287,21 +287,56 @@ mcp = FastMCP("test", dependencies=["pandas", "numpy"])
|
|||
runner = CliRunner()
|
||||
|
||||
with patch("subprocess.run") as mock_run:
|
||||
mock_run.return_value.returncode = 0 # Set successful return code
|
||||
mock_run.return_value.returncode = 0
|
||||
result = runner.invoke(app, ["dev", str(server_file)])
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Check that dependencies were passed to subprocess.run
|
||||
mock_run.assert_called_once()
|
||||
args = mock_run.call_args[0][0]
|
||||
assert "npx" in args
|
||||
assert "@modelcontextprotocol/inspector" in args
|
||||
assert "uv" in args
|
||||
assert "run" in args
|
||||
assert "--with" in args
|
||||
assert "pandas" in args
|
||||
assert "numpy" in args
|
||||
assert "fastmcp" in args
|
||||
if sys.platform == "win32":
|
||||
# On Windows, expect two calls
|
||||
assert mock_run.call_count == 2
|
||||
assert mock_run.call_args_list[0] == call(
|
||||
["npx.cmd", "--version"], check=True, capture_output=True, shell=True
|
||||
)
|
||||
assert mock_run.call_args_list[1] == call(
|
||||
[
|
||||
"npx.cmd",
|
||||
"@modelcontextprotocol/inspector",
|
||||
"uv",
|
||||
"run",
|
||||
"--with",
|
||||
"fastmcp",
|
||||
"--with",
|
||||
"numpy",
|
||||
"--with",
|
||||
"pandas",
|
||||
"fastmcp",
|
||||
"run",
|
||||
str(server_file),
|
||||
],
|
||||
check=True,
|
||||
shell=True,
|
||||
)
|
||||
else:
|
||||
# On Unix, expect one call
|
||||
mock_run.assert_called_once_with(
|
||||
[
|
||||
"npx",
|
||||
"@modelcontextprotocol/inspector",
|
||||
"uv",
|
||||
"run",
|
||||
"--with",
|
||||
"fastmcp",
|
||||
"--with",
|
||||
"numpy",
|
||||
"--with",
|
||||
"pandas",
|
||||
"fastmcp",
|
||||
"run",
|
||||
str(server_file),
|
||||
],
|
||||
check=True,
|
||||
shell=False, # Note: shell=False on Unix
|
||||
)
|
||||
|
||||
|
||||
def test_run_with_dependencies(mock_config, server_file):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue