Initial commit
This commit is contained in:
102
scripts/check-versions.sh
Executable file
102
scripts/check-versions.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
# FastMCP Version Checker
|
||||
# Verifies that FastMCP and dependencies are up to date
|
||||
|
||||
set -e
|
||||
|
||||
echo "======================================"
|
||||
echo "FastMCP Version Checker"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check if Python is installed
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo -e "${RED}✗${NC} Python 3 is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Python $(python3 --version)"
|
||||
echo ""
|
||||
|
||||
# Check Python version
|
||||
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||
REQUIRED_VERSION="3.10"
|
||||
|
||||
if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$PYTHON_VERSION" | sort -V | head -n1)" != "$REQUIRED_VERSION" ]; then
|
||||
echo -e "${RED}✗${NC} Python $PYTHON_VERSION is too old. FastMCP requires Python $REQUIRED_VERSION or later"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Python version $PYTHON_VERSION meets requirements"
|
||||
echo ""
|
||||
|
||||
# Check if pip is installed
|
||||
if ! command -v pip3 &> /dev/null; then
|
||||
echo -e "${RED}✗${NC} pip3 is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking package versions..."
|
||||
echo ""
|
||||
|
||||
# Function to check package version
|
||||
check_package() {
|
||||
local package=$1
|
||||
local min_version=$2
|
||||
|
||||
if pip3 show "$package" &> /dev/null; then
|
||||
local installed_version=$(pip3 show "$package" | grep "Version:" | awk '{print $2}')
|
||||
echo -e "${GREEN}✓${NC} $package: $installed_version (required: >=$min_version)"
|
||||
|
||||
# Note: This is a simple check. For production, use more robust version comparison
|
||||
if [ "$installed_version" != "$min_version" ]; then
|
||||
if [ "$(printf '%s\n' "$min_version" "$installed_version" | sort -V | head -n1)" != "$min_version" ]; then
|
||||
echo -e " ${YELLOW}⚠${NC} Installed version is older than minimum required"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗${NC} $package: Not installed (required: >=$min_version)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check core packages
|
||||
check_package "fastmcp" "2.12.0"
|
||||
check_package "httpx" "0.27.0"
|
||||
check_package "python-dotenv" "1.0.0"
|
||||
check_package "pydantic" "2.0.0"
|
||||
|
||||
echo ""
|
||||
echo "Checking optional packages..."
|
||||
echo ""
|
||||
|
||||
# Check optional packages
|
||||
if pip3 show "psutil" &> /dev/null; then
|
||||
check_package "psutil" "5.9.0"
|
||||
else
|
||||
echo -e "${YELLOW}○${NC} psutil: Not installed (optional, for health checks)"
|
||||
fi
|
||||
|
||||
if pip3 show "pytest" &> /dev/null; then
|
||||
check_package "pytest" "8.0.0"
|
||||
else
|
||||
echo -e "${YELLOW}○${NC} pytest: Not installed (optional, for testing)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo "Version check complete!"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Suggestions
|
||||
echo "Suggestions:"
|
||||
echo " - To update FastMCP: pip install --upgrade fastmcp"
|
||||
echo " - To update all dependencies: pip install --upgrade -r requirements.txt"
|
||||
echo " - To see outdated packages: pip list --outdated"
|
||||
echo ""
|
||||
231
scripts/deploy-cloud.sh
Executable file
231
scripts/deploy-cloud.sh
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/bin/bash
|
||||
# FastMCP Cloud Deployment Checker
|
||||
# Validates server is ready for FastMCP Cloud deployment
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "======================================"
|
||||
echo "FastMCP Cloud Deployment Checker"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Check arguments
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <server.py>"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 server.py"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_PATH=$1
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Function to check requirement
|
||||
check_required() {
|
||||
local description=$1
|
||||
local command=$2
|
||||
|
||||
if eval "$command" &> /dev/null; then
|
||||
echo -e "${GREEN}✓${NC} $description"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $description"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check warning
|
||||
check_warning() {
|
||||
local description=$1
|
||||
local command=$2
|
||||
|
||||
if eval "$command" &> /dev/null; then
|
||||
echo -e "${GREEN}✓${NC} $description"
|
||||
return 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} $description"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Check server file exists
|
||||
echo "Checking server file..."
|
||||
check_required "Server file exists: $SERVER_PATH" "test -f '$SERVER_PATH'"
|
||||
echo ""
|
||||
|
||||
# 2. Check Python syntax
|
||||
echo "Checking Python syntax..."
|
||||
check_required "Python syntax is valid" "python3 -m py_compile '$SERVER_PATH'"
|
||||
echo ""
|
||||
|
||||
# 3. Check for module-level server object
|
||||
echo "Checking module-level server object..."
|
||||
if grep -q "^mcp = FastMCP\|^server = FastMCP\|^app = FastMCP" "$SERVER_PATH"; then
|
||||
echo -e "${GREEN}✓${NC} Found module-level server object (mcp/server/app)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} No module-level server object found"
|
||||
echo " Expected: mcp = FastMCP(...) at module level"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 4. Check requirements.txt
|
||||
echo "Checking requirements.txt..."
|
||||
if [ -f "requirements.txt" ]; then
|
||||
echo -e "${GREEN}✓${NC} requirements.txt exists"
|
||||
|
||||
# Check for non-PyPI dependencies
|
||||
if grep -q "^git+\|^-e \|\.whl$\|\.tar.gz$" requirements.txt; then
|
||||
echo -e "${RED}✗${NC} requirements.txt contains non-PyPI dependencies"
|
||||
echo " FastMCP Cloud requires PyPI packages only"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} All dependencies are PyPI packages"
|
||||
fi
|
||||
|
||||
# Check for fastmcp
|
||||
if grep -q "^fastmcp" requirements.txt; then
|
||||
echo -e "${GREEN}✓${NC} FastMCP is in requirements.txt"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} FastMCP not found in requirements.txt"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗${NC} requirements.txt not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 5. Check for hardcoded secrets
|
||||
echo "Checking for hardcoded secrets..."
|
||||
if grep -i "api_key\s*=\s*[\"']" "$SERVER_PATH" | grep -v "os.getenv\|os.environ" > /dev/null; then
|
||||
echo -e "${RED}✗${NC} Found hardcoded API keys (possible security issue)"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} No hardcoded API keys found"
|
||||
fi
|
||||
|
||||
if grep -i "password\s*=\s*[\"']\|secret\s*=\s*[\"']" "$SERVER_PATH" | grep -v "os.getenv\|os.environ" > /dev/null; then
|
||||
echo -e "${YELLOW}⚠${NC} Found possible hardcoded passwords/secrets"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 6. Check .gitignore
|
||||
echo "Checking .gitignore..."
|
||||
if [ -f ".gitignore" ]; then
|
||||
echo -e "${GREEN}✓${NC} .gitignore exists"
|
||||
|
||||
if grep -q "\.env$" .gitignore; then
|
||||
echo -e "${GREEN}✓${NC} .env is in .gitignore"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} .env not in .gitignore"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} .gitignore not found"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 7. Check for circular imports
|
||||
echo "Checking for potential circular imports..."
|
||||
if grep -r "from __init__ import\|from . import.*get_" . --include="*.py" 2>/dev/null | grep -v ".git" > /dev/null; then
|
||||
echo -e "${YELLOW}⚠${NC} Possible circular import pattern detected (factory functions)"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} No obvious circular import patterns"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 8. Check git repository
|
||||
echo "Checking git repository..."
|
||||
if [ -d ".git" ]; then
|
||||
echo -e "${GREEN}✓${NC} Git repository initialized"
|
||||
|
||||
# Check if there are uncommitted changes
|
||||
if [ -z "$(git status --porcelain)" ]; then
|
||||
echo -e "${GREEN}✓${NC} No uncommitted changes"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} There are uncommitted changes"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
|
||||
# Check if remote is set
|
||||
if git remote -v | grep -q "origin"; then
|
||||
echo -e "${GREEN}✓${NC} Git remote (origin) configured"
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
echo " Remote: $REMOTE_URL"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} No git remote configured"
|
||||
echo " Run: gh repo create <name> --public"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Not a git repository"
|
||||
echo " Run: git init"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 9. Test server can run
|
||||
echo "Testing server execution..."
|
||||
if timeout 5 python3 "$SERVER_PATH" --help &> /dev/null || timeout 5 fastmcp inspect "$SERVER_PATH" &> /dev/null; then
|
||||
echo -e "${GREEN}✓${NC} Server can be loaded"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Could not verify server loads correctly"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "======================================"
|
||||
echo "Deployment Check Summary"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Ready for deployment!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Commit changes: git add . && git commit -m 'Ready for deployment'"
|
||||
echo " 2. Push to GitHub: git push -u origin main"
|
||||
echo " 3. Visit https://fastmcp.cloud"
|
||||
echo " 4. Connect your repository"
|
||||
echo " 5. Add environment variables"
|
||||
echo " 6. Deploy!"
|
||||
exit 0
|
||||
elif [ $ERRORS -eq 0 ]; then
|
||||
echo -e "${YELLOW}⚠ Ready with warnings (${WARNINGS} warnings)${NC}"
|
||||
echo ""
|
||||
echo "Review warnings above before deploying."
|
||||
echo ""
|
||||
echo "To deploy anyway:"
|
||||
echo " 1. git add . && git commit -m 'Ready for deployment'"
|
||||
echo " 2. git push -u origin main"
|
||||
echo " 3. Visit https://fastmcp.cloud"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Not ready for deployment (${ERRORS} errors, ${WARNINGS} warnings)${NC}"
|
||||
echo ""
|
||||
echo "Fix the errors above before deploying."
|
||||
echo ""
|
||||
echo "Common fixes:"
|
||||
echo " - Export server at module level: mcp = FastMCP('name')"
|
||||
echo " - Use only PyPI packages in requirements.txt"
|
||||
echo " - Use os.getenv() for secrets, not hardcoded values"
|
||||
echo " - Initialize git: git init"
|
||||
echo " - Create .gitignore with .env"
|
||||
exit 1
|
||||
fi
|
||||
187
scripts/test-server.sh
Executable file
187
scripts/test-server.sh
Executable file
@@ -0,0 +1,187 @@
|
||||
#!/bin/bash
|
||||
# FastMCP Server Tester
|
||||
# Tests a FastMCP server using the FastMCP Client
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "======================================"
|
||||
echo "FastMCP Server Tester"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Check arguments
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <server.py> [--http] [--port 8000]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 server.py # Test stdio server"
|
||||
echo " $0 server.py --http --port 8000 # Test HTTP server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_PATH=$1
|
||||
TRANSPORT="stdio"
|
||||
PORT="8000"
|
||||
|
||||
# Parse arguments
|
||||
shift
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--http)
|
||||
TRANSPORT="http"
|
||||
shift
|
||||
;;
|
||||
--port)
|
||||
PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if server file exists
|
||||
if [ ! -f "$SERVER_PATH" ]; then
|
||||
echo -e "${RED}✗${NC} Server file not found: $SERVER_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} Found server: $SERVER_PATH"
|
||||
echo -e "${GREEN}✓${NC} Transport: $TRANSPORT"
|
||||
if [ "$TRANSPORT" = "http" ]; then
|
||||
echo -e "${GREEN}✓${NC} Port: $PORT"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Create test script
|
||||
TEST_SCRIPT=$(mktemp)
|
||||
cat > "$TEST_SCRIPT" << 'EOF'
|
||||
import asyncio
|
||||
import sys
|
||||
from fastmcp import Client
|
||||
|
||||
async def test_server(server_path, transport, port):
|
||||
"""Test MCP server."""
|
||||
print("Starting server test...\n")
|
||||
|
||||
try:
|
||||
if transport == "http":
|
||||
server_url = f"http://localhost:{port}/mcp"
|
||||
print(f"Connecting to HTTP server at {server_url}...")
|
||||
client_context = Client(server_url)
|
||||
else:
|
||||
print(f"Connecting to stdio server: {server_path}...")
|
||||
client_context = Client(server_path)
|
||||
|
||||
async with client_context as client:
|
||||
print("✓ Connected to server\n")
|
||||
|
||||
# Test: List tools
|
||||
print("Testing: List tools")
|
||||
tools = await client.list_tools()
|
||||
print(f"✓ Found {len(tools)} tools")
|
||||
for tool in tools:
|
||||
print(f" - {tool.name}: {tool.description[:60]}...")
|
||||
print()
|
||||
|
||||
# Test: List resources
|
||||
print("Testing: List resources")
|
||||
resources = await client.list_resources()
|
||||
print(f"✓ Found {len(resources)} resources")
|
||||
for resource in resources:
|
||||
print(f" - {resource.uri}: {resource.description[:60] if resource.description else 'No description'}...")
|
||||
print()
|
||||
|
||||
# Test: List prompts
|
||||
print("Testing: List prompts")
|
||||
prompts = await client.list_prompts()
|
||||
print(f"✓ Found {len(prompts)} prompts")
|
||||
for prompt in prompts:
|
||||
print(f" - {prompt.name}: {prompt.description[:60] if prompt.description else 'No description'}...")
|
||||
print()
|
||||
|
||||
# Test: Call first tool (if any)
|
||||
if tools:
|
||||
print(f"Testing: Call tool '{tools[0].name}'")
|
||||
try:
|
||||
# Try calling with empty args (may fail if required params)
|
||||
result = await client.call_tool(tools[0].name, {})
|
||||
print(f"✓ Tool executed successfully")
|
||||
print(f" Result: {str(result.data)[:100]}...")
|
||||
except Exception as e:
|
||||
print(f"⚠ Tool call failed (may require parameters): {e}")
|
||||
print()
|
||||
|
||||
# Test: Read first resource (if any)
|
||||
if resources:
|
||||
print(f"Testing: Read resource '{resources[0].uri}'")
|
||||
try:
|
||||
data = await client.read_resource(resources[0].uri)
|
||||
print(f"✓ Resource read successfully")
|
||||
print(f" Data: {str(data)[:100]}...")
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to read resource: {e}")
|
||||
print()
|
||||
|
||||
print("=" * 50)
|
||||
print("✓ Server test completed successfully!")
|
||||
print("=" * 50)
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Server test failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
server_path = sys.argv[1]
|
||||
transport = sys.argv[2] if len(sys.argv) > 2 else "stdio"
|
||||
port = sys.argv[3] if len(sys.argv) > 3 else "8000"
|
||||
|
||||
exit_code = asyncio.run(test_server(server_path, transport, port))
|
||||
sys.exit(exit_code)
|
||||
EOF
|
||||
|
||||
# Run test
|
||||
echo "Running tests..."
|
||||
echo ""
|
||||
|
||||
if [ "$TRANSPORT" = "http" ]; then
|
||||
# For HTTP, start server in background
|
||||
echo "Starting HTTP server in background..."
|
||||
python3 "$SERVER_PATH" --transport http --port "$PORT" &
|
||||
SERVER_PID=$!
|
||||
|
||||
# Wait for server to start
|
||||
sleep 2
|
||||
|
||||
# Run test
|
||||
python3 "$TEST_SCRIPT" "$SERVER_PATH" "$TRANSPORT" "$PORT"
|
||||
TEST_EXIT=$?
|
||||
|
||||
# Kill server
|
||||
kill $SERVER_PID 2>/dev/null || true
|
||||
|
||||
# Cleanup
|
||||
rm "$TEST_SCRIPT"
|
||||
|
||||
exit $TEST_EXIT
|
||||
else
|
||||
# For stdio, run test directly
|
||||
python3 "$TEST_SCRIPT" "$SERVER_PATH" "$TRANSPORT"
|
||||
TEST_EXIT=$?
|
||||
|
||||
# Cleanup
|
||||
rm "$TEST_SCRIPT"
|
||||
|
||||
exit $TEST_EXIT
|
||||
fi
|
||||
Reference in New Issue
Block a user