Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:24:49 +08:00
commit 99a553f8ab
25 changed files with 6408 additions and 0 deletions

102
scripts/check-versions.sh Executable file
View 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
View 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
View 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