[package]
name = "pierre_mcp_server"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Pierre Fitness API - Multi-protocol fitness data API for LLMs (MCP + A2A)"
repository = "https://github.com/Async-IO/pierre_mcp_server"
keywords = ["mcp", "fitness", "strava", "api", "claude"]
categories = ["api-bindings", "web-programming"]
[lib]
name = "pierre_mcp_server"
path = "src/lib.rs"
# Disable built-in bench harness for lib (we use Criterion benches in benches/ instead)
bench = false
[[bin]]
name = "pierre-mcp-server"
path = "src/bin/pierre-mcp-server.rs"
bench = false
# [[bin]]
# name = "auth-setup"
# path = "src/bin/auth_setup.rs"
[[bin]]
name = "pierre-cli"
path = "src/bin/pierre_cli/main.rs"
bench = false
[[bin]]
name = "seed-demo-data"
path = "src/bin/seed_demo_data.rs"
bench = false
[[bin]]
name = "seed-coaches"
path = "src/bin/seed_coaches.rs"
bench = false
[[bin]]
name = "seed-mobility"
path = "src/bin/seed_mobility.rs"
bench = false
[[bin]]
name = "seed-social"
path = "src/bin/seed_social.rs"
bench = false
[[bin]]
name = "seed-insight-samples"
path = "src/bin/seed_insight_samples.rs"
bench = false
[[bin]]
name = "seed-synthetic-activities"
path = "src/bin/seed_synthetic_activities.rs"
bench = false
# [[bin]]
# name = "serve-docs"
# path = "src/bin/serve_docs.rs"
# NOTE: Temporarily disabled during Warp-to-Axum migration
[features]
default = ["sqlite", "server-full", "sqlx/migrate"]
sqlite = []
postgresql = ["sqlx/postgres"]
testing = []
telemetry = []
# Provider feature flags - enable/disable individual fitness data providers
provider-strava = []
provider-garmin = []
provider-terra = []
provider-fitbit = []
provider-whoop = []
provider-coros = []
provider-synthetic = []
all-providers = ["provider-strava", "provider-garmin", "provider-terra", "provider-fitbit", "provider-whoop", "provider-coros", "provider-synthetic"]
# Tool feature flags - enable/disable MCP tool categories
# All tools (default)
tools-all = [
"tools-connection",
"tools-data",
"tools-analytics",
"tools-goals",
"tools-config",
"tools-nutrition",
"tools-sleep",
"tools-recipes",
"tools-coaches",
"tools-admin",
"tools-mobility",
]
# Individual tool categories
tools-connection = [] # connect_provider, get_connection_status, disconnect
tools-data = [] # get_activities, get_athlete, get_stats
tools-analytics = [] # analyze_activity, calculate_metrics, trends
tools-goals = [] # set_goal, track_progress, suggest_goals
tools-config = [] # fitness config tools, user configuration
tools-nutrition = [] # daily_nutrition, nutrient_timing, food search
tools-sleep = [] # sleep_quality, recovery_score, rest_day
tools-recipes = [] # validate_recipe, save_recipe, list_recipes
tools-coaches = [] # coach CRUD, activate, favorites
tools-admin = ["tools-coaches"] # admin tools depend on coach tools
tools-mobility = [] # stretching exercises, yoga poses, mobility recommendations
tools-store = [] # store MCP tools (browse, search, install coaches)
# Convenience bundles for common deployments
tools-fitness-core = ["tools-connection", "tools-data", "tools-analytics"]
tools-wellness = ["tools-sleep", "tools-nutrition", "tools-recipes", "tools-mobility"]
# ═══════════════════════════════════════════════════════════════
# PROTOCOL FEATURES
# ═══════════════════════════════════════════════════════════════
protocol-rest = []
protocol-mcp = ["transport-http"] # Requires at least HTTP transport
protocol-a2a = ["transport-http"]
protocol-all = ["protocol-rest", "protocol-mcp", "protocol-a2a"]
# ═══════════════════════════════════════════════════════════════
# TRANSPORT FEATURES
# ═══════════════════════════════════════════════════════════════
transport-http = []
transport-websocket = []
transport-sse = []
transport-stdio = []
transport-all = ["transport-http", "transport-websocket", "transport-sse", "transport-stdio"]
transport-web = ["transport-http", "transport-websocket", "transport-sse"]
# ═══════════════════════════════════════════════════════════════
# GRANULAR CLIENT-WEB FEATURES
# ═══════════════════════════════════════════════════════════════
client-dashboard = ["protocol-rest"] # dashboard.rs
client-settings = ["protocol-rest"] # configuration.rs, fitness.rs
client-chat = ["protocol-rest"] # chat.rs
client-coaches = ["protocol-rest", "tools-coaches"] # coaches.rs
client-oauth-apps = ["protocol-rest", "oauth"] # user_oauth_apps.rs
client-store = ["protocol-rest", "tools-store"] # Store REST API routes + MCP tools
client-social = ["protocol-rest"] # social.rs - coach-mediated social features
client-web = ["client-dashboard", "client-settings", "client-chat", "client-coaches", "client-oauth-apps", "client-store", "client-social"]
# ═══════════════════════════════════════════════════════════════
# GRANULAR CLIENT-ADMIN FEATURES
# ═══════════════════════════════════════════════════════════════
client-admin-api = ["protocol-rest"] # admin.rs
client-admin-ui = ["protocol-rest"] # web_admin.rs
client-api-keys = ["protocol-rest"] # api_keys.rs
client-tenants = ["protocol-rest"] # tenants.rs
client-impersonation = ["protocol-rest"] # impersonation.rs
client-llm-settings = ["protocol-rest"] # llm_settings.rs
client-tool-selection = ["protocol-mcp"] # tool_selection.rs
client-admin = ["client-admin-api", "client-admin-ui", "client-api-keys", "client-tenants", "client-impersonation", "client-llm-settings", "client-tool-selection"]
# ═══════════════════════════════════════════════════════════════
# OTHER CLIENT FEATURES
# ═══════════════════════════════════════════════════════════════
client-mobile = ["protocol-rest"] # Mobile-specific routes (subset)
client-mcp-tokens = ["protocol-mcp"] # user_mcp_tokens.rs
client-all = ["client-web", "client-admin", "client-mobile", "client-mcp-tokens"]
# ═══════════════════════════════════════════════════════════════
# OAUTH (shared infrastructure)
# ═══════════════════════════════════════════════════════════════
oauth = []
# ═══════════════════════════════════════════════════════════════
# SERVER PROFILES
# ═══════════════════════════════════════════════════════════════
server-full = ["protocol-all", "transport-all", "client-all", "oauth", "all-providers", "tools-all"]
server-mcp-stdio = ["protocol-mcp", "transport-stdio", "oauth", "all-providers", "tools-all"]
server-mcp-bridge = ["protocol-mcp", "protocol-a2a", "transport-web", "oauth", "all-providers", "tools-all"]
server-mobile-backend = ["protocol-rest", "protocol-mcp", "client-mobile", "client-settings", "client-store", "oauth", "all-providers", "tools-all"]
server-saas-full = ["protocol-rest", "protocol-mcp", "transport-web", "client-web", "client-admin", "oauth", "all-providers", "tools-all"]
# OpenAPI documentation (SwaggerUI) - excluded from production by default
# Enable with: cargo build --features openapi
openapi = ["dep:utoipa", "dep:utoipa-axum", "dep:utoipa-swagger-ui"]
[profile.dev]
debug = 1
opt-level = 0
overflow-checks = true
[profile.release]
lto = "thin"
codegen-units = 1
panic = "abort"
strip = true
[profile.release-lto]
inherits = "release"
lto = "fat"
[dependencies]
tokio = { version = "1.45", features = ["rt-multi-thread", "macros", "net", "time", "fs", "signal", "io-std", "io-util", "sync"] }
tokio-stream = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.9"
reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"], default-features = false }
reqwest-middleware = "0.3"
anyhow = "1.0"
async-stream = "0.3"
thiserror = "2.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "std", "registry", "json"], default-features = false }
# OpenTelemetry for distributed tracing and request correlation
tracing-opentelemetry = "0.23"
opentelemetry = { version = "0.22", features = ["trace"] }
opentelemetry_sdk = { version = "0.22", features = ["trace", "rt-tokio"] }
# HTTP tracing middleware
tower = { version = "0.5", features = ["util"] }
tower-http = { version = "0.6", features = ["trace", "request-id", "cors", "timeout", "limit", "set-header"] }
async-trait = "0.1"
chrono = { version = "0.4", features = ["serde"] }
base64 = "0.22"
bytes = "1.11"
url = "2.5"
clap = { version = "4.5", features = ["derive", "std"], default-features = false }
dotenvy = "0.15"
# toml = "0.8" # Removed: Environment-only configuration approach
dirs = "5.0"
uuid = { version = "1.11", features = ["v4", "serde"] }
dashmap = "5.5"
urlencoding = "2.1"
sha2 = "0.10"
rand = "0.8"
aes-gcm = "0.10"
# Encryption and database support for multi-tenant
ring = "0.17"
ed25519-dalek = "2.1"
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "postgres", "chrono", "uuid", "derive", "migrate", "macros"], default-features = false }
bcrypt = "0.16"
argon2 = "0.5"
jsonwebtoken = { version = "10.3", features = ["aws_lc_rs"] }
subtle = "2.6"
zeroize = { version = "1.8", features = ["alloc"] }
hex = "0.4"
# HTTP framework: Axum (migrated from Warp)
axum = { version = "0.7", features = ["ws", "json", "query", "macros", "tokio", "http2", "form"], default-features = false }
axum-extra = { version = "0.9", features = ["typed-header"] }
http = "1.0"
futures-util = "0.3"
eventsource-stream = "0.2"
base64ct = "1.6"
# For documentation server
env_logger = "0.11"
# LRU cache for bounded session storage (DoS prevention)
lru = "0.16"
# Glob pattern matching for cache invalidation
glob = "0.3"
# Regex for PII redaction and pattern matching
regex = "1.11"
# Bitflags for PII redaction feature configuration
bitflags = { version = "2.6", features = ["serde"] }
# Cross-platform system information without unsafe FFI
sysinfo = "0.31"
# Redis client with connection pooling and async support
redis = { version = "0.28", features = ["tokio-comp", "connection-manager", "aio"] }
num-traits = "0.2"
# Rayon for CPU-bound parallel data processing (zone analysis, fitness calculations)
rayon = "1.10"
rsa = "0.9"
x509-parser = "0.18.0"
# TOON format for token-efficient LLM serialization (~40% token reduction vs JSON)
toon-format = "0.2"
rand_chacha = "0.3"
# OpenAPI documentation generation with utoipa
# OpenAPI documentation - optional (enable with --features openapi)
utoipa = { version = "5.3", features = ["axum_extras", "chrono", "uuid"], optional = true }
utoipa-axum = { version = "0.2", optional = true }
utoipa-swagger-ui = { version = "8.1", features = ["axum"], optional = true }
html-escape = "0.2.13"
[dev-dependencies]
tempfile = "3.20"
serial_test = "3.1"
tokio-tungstenite = "0.24"
criterion = { version = "0.5", features = ["async_tokio", "html_reports"] }
serde_urlencoded = "0.7"
# ============================================================================
# Benchmark Configuration
# ============================================================================
# Each benchmark suite is defined as a separate [[bench]] entry.
# Run all benchmarks: cargo bench
# Run specific suite: cargo bench --bench intelligence_bench
# Run specific benchmark: cargo bench --bench intelligence_bench -- "training_load"
[[bench]]
name = "intelligence_bench"
harness = false
[[bench]]
name = "cache_bench"
harness = false
[[bench]]
name = "database_bench"
harness = false
[[bench]]
name = "serialization_bench"
harness = false
# ============================================================================
# Linting Configuration (Replaces bash script clippy invocation)
# ============================================================================
# This configuration eliminates the need for custom clippy flags in scripts.
# CI/CD can now simply run `cargo clippy` and these settings are applied.
#
# ZERO TOLERANCE ENFORCEMENT:
# Old: cargo clippy --all-targets --all-features -- \
# -W clippy::all -W clippy::pedantic -W clippy::nursery -D warnings
# New: cargo clippy (reads this config, applies level = "deny")
#
# All clippy warnings are now ERRORS via level = "deny" configuration below.
[lints.rust]
# STRICT UNSAFE CODE POLICY: Zero tolerance with whitelisted exceptions
# - "deny" level: Unsafe code is an ERROR by default everywhere
# - scripts/architectural-validation.sh: Enforces ONLY src/health.rs can use unsafe (Windows FFI)
# - Any unsafe usage outside approved locations FAILS build immediately
# - Approved: Windows health monitoring FFI (GlobalMemoryStatusEx, GetDiskFreeSpaceExW)
unsafe_code = "deny"
# Warn on missing documentation for public APIs
missing_docs = "warn"
[lints.clippy]
# Base configuration: Enable all clippy lint groups at DENY level (zero tolerance)
# Priority -1 ensures these are applied first, then specific overrides below
# All clippy warnings are treated as ERRORS via level = "deny"
all = { level = "deny", priority = -1 }
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
# ============================================================================
# Critical Denials - Error Handling Anti-Patterns (CLAUDE.md enforcement)
# ============================================================================
# These replace the bash script's error handling pattern detection
# DENY: .unwrap() in production code (except test/bin with "// Safe" comments)
unwrap_used = "deny"
# DENY: .expect() in production code (except test/bin with "// Safe" comments)
expect_used = "deny"
# DENY: panic!() in production code (only acceptable in tests/bins)
panic = "deny"
# ============================================================================
# Allowed Exceptions (Per CLAUDE.md Policy)
# ============================================================================
# Type conversion casts - allowed when properly validated
cast_possible_truncation = "allow"
cast_sign_loss = "allow"
cast_precision_loss = "allow"
# Const fn suggestions - clippy suggests const for methods that can't actually be const
# (e.g., methods using .to_lowercase(), .map_or(), Utc::now(), etc.)
missing_const_for_fn = "allow"
# Structural patterns - validated separately
struct_excessive_bools = "allow"
# Long functions - allowed with mandatory documentation comment
# Script validates: "// Long function:" or "// Safe:" comment required
too_many_lines = "allow"
# Significant drop tightening - false positives with async lock guards
# The suggestions given are often syntactically invalid
significant_drop_tightening = "allow"
# Cognitive complexity - warn on overly complex functions
# Enforces CLAUDE.md guideline of small, focused functions (max 50 lines)
cognitive_complexity = "deny"
# ============================================================================
# Additional Code Quality Lints
# ============================================================================
# Catch common performance issues
clone_on_copy = "warn"
redundant_clone = "warn"
# Catch async anti-patterns
await_holding_lock = "warn"
# Catch string allocation inefficiencies
str_to_string = "deny"
# Module naming - allow repetition for clarity (e.g., error::ErrorKind)
module_name_repetitions = "allow"
# ============================================================================
# Rust Idiom Enforcement (Prevent AI-generated regressions)
# ============================================================================
# Enforce modern format string syntax: format!("{foo}") not format!("{}", foo)
uninlined_format_args = "warn"
# Warn on .map().unwrap_or() - should use map_or() or is_some_and() instead
map_unwrap_or = "warn"
# Enforce `use` imports over inline qualified paths (crate::foo::bar)
# Idiomatic Rust prefers imports at top of file for readability
absolute_paths = "deny"
# Disallow anyhow::Context methods - use structured ProviderError types instead
# Configuration in clippy.toml specifies which methods are banned
disallowed_methods = "deny"