[package]
name = "mcp-gateway"
version = "3.0.1"
edition = "2024"
rust-version = "1.95"
authors = ["Mikko Parkkola <mikko.parkkola@iki.fi>"]
description = "Universal MCP Gateway - Single-port multiplexing with Meta-MCP for ~95% context token savings"
license = "MIT"
homepage = "https://github.com/MikkoParkkola/mcp-gateway"
repository = "https://github.com/MikkoParkkola/mcp-gateway"
documentation = "https://github.com/MikkoParkkola/mcp-gateway#readme"
readme = "README.md"
keywords = ["mcp", "model-context", "gateway", "proxy", "ai"]
categories = ["command-line-utilities", "network-programming", "web-programming"]
# Keep the published crate focused on build/runtime assets and the fixtures
# needed by package verification tests.
include = [
    "Cargo.toml",
    "Cargo.lock",
    "README.md",
    "LICENSE",
    "CHANGELOG.md",
    "CODE_OF_CONDUCT.md",
    "CONTRIBUTING.md",
    "SECURITY.md",
    "llms.txt",
    "demo.tape",
    "src/**",
    "tests/**",
    "benches/**",
    "benchmarks/**",
    "capabilities/**",
    "examples/**",
    "docs/ARCHITECTURE.md",
    "docs/BENCHMARKS.md",
    "docs/COMMUNITY_REGISTRY.md",
    "docs/QUICKSTART.md",
]

[workspace]
members = [
    "crates/gateway-core",
]
resolver = "2"


[dependencies]
# Async runtime
tokio = { version = "1.52", features = ["full", "tracing"] }

# HTTP server/client
axum = { version = "0.8", features = ["tracing", "http2"] }
reqwest = { version = "0.13", default-features = false, features = ["rustls", "json", "stream", "form", "query", "gzip", "deflate", "brotli"] }
tower = { version = "0.5", features = ["timeout", "limit", "load-shed", "retry"] }
tower-http = { version = "0.7", features = ["cors", "trace", "timeout", "compression-gzip", "request-id", "catch-panic"] }

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.9"

# Configuration
figment = { version = "0.10", features = ["yaml", "env"] }

# CLI
clap = { version = "4.6", features = ["derive", "env", "wrap_help"] }
clap_complete = "4.6"

# Logging & Telemetry
tracing = "0.1"
tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json", "fmt"] }

# Error handling
thiserror = "2.0"
anyhow = "1.0"

# Async utilities
futures = "0.3"
async-trait = "0.1"
tokio-util = { version = "0.7", features = ["codec", "io"] }
tokio-stream = { version = "0.1", features = ["sync"] }
async-stream = "0.3"

# Rate limiting & backoff
governor = "0.10"
backon = { version = "1.6", default-features = false, features = ["std", "tokio-sleep"] }

# Utilities
regex = "1.11"
url = "2.5"
uuid = { version = "1.23", features = ["v4", "fast-rng"] }
chrono = { version = "0.4.45", features = ["serde"] }
dashmap = "6.2"
parking_lot = "0.12"
bytes = "1.11.1"
pin-project-lite = "0.2"
shlex = "2.0"

# OAuth / Crypto
sha2 = "0.11"
base64 = "0.22"
rand = "0.10"
dirs = "6.0"
hmac = "0.13"
hex = "0.4"
ring = "0.17"
# Key Server: OIDC JWT verification
# aws_lc_rs backend avoids the rsa crate (RUSTSEC-2023-0071 Marvin Attack)
jsonwebtoken = { version = "10.4", features = ["use_pem", "aws_lc_rs"] }
# Constant-time comparison (prevent timing attacks in admin token check)
subtle = "2.6"
# OS advisory file locking for the control-plane store's cross-process
# read-modify-write (MIK-6685). Already a transitive dep (tempfile, clap →
# terminal_size); promoted to direct with the `fs` feature — no new crate is
# compiled, only a manifest entry (verified via `cargo tree`).
rustix = { version = "1.1", features = ["fs"] }

# mTLS / TLS
# PQC hardening (issue #116): tls12 feature removed — TLS 1.3 is the enforced minimum.
# aws_lc_rs provider is consistent with jsonwebtoken's aws_lc_rs backend and avoids
# the RUSTSEC-2023-0071 Marvin Attack present in the rsa crate used by the ring backend.
rustls = { version = "0.23.40", default-features = false, features = ["aws_lc_rs"] }
rcgen = { version = "0.14", features = ["pem", "x509-parser"] }
time = { version = "0.3.47", default-features = false }
x509-parser = "0.18"
axum-server = { version = "0.8", features = ["tls-rustls"] }
tokio-rustls = "0.26"

# XML parsing (for APIs that return XML, e.g. ECB exchange rates)
quick-xml = "0.41"

# Environment file loading
dotenvy = "0.15"

# File watching (hot-reload)
notify = "8.0"
walkdir = "2.5"

# Glob pattern matching for firewall rules (RFC-0071)
glob = "0.3"

# Metrics
telemetry_metrics = { package = "metrics", version = "0.24" }
metrics-exporter-prometheus = { version = "0.18", optional = true }
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-webpki-roots"] }

[dev-dependencies]
tokio-test = "0.4"
pretty_assertions = "1.4"
tempfile = "3.14"
serde_urlencoded = "0.7"
criterion = { version = "0.8", features = ["html_reports"] }

[features]
default = ["a2a", "webui", "config-export", "cost-governance", "firewall", "discovery", "semantic-search", "tool-profiles", "metrics"]
## A2A (Agent2Agent) transport — proxy A2A agents as MCP backends.
## No new dependencies: reqwest, serde, serde_json, tokio already present.
a2a = []
webui = []
config-export = []
cost-governance = []
firewall = []
discovery = []
semantic-search = []
tool-profiles = []
metrics = ["dep:metrics-exporter-prometheus"]
## Opt-in preview of draft MCP spec extensions (SEP-1821, SEP-1862).
## NOT included in default — operators must explicitly enable.
spec-preview = []

## Dual-substrate OCI runtime abstraction (MIK-5226, B4-PLATFORM).
## Gates the dormant descriptor->substrate compiler behind an explicit opt-in:
## enables the `runtime compile` CLI subcommand and the hardened provisioning
## call path (src/runtime/provision.rs). NOT in default — foundation code.
runtime-substrate = []

[[bin]]
name = "mcp-gateway"
path = "src/main.rs"

[[bench]]
name = "gateway_benchmarks"
harness = false

[profile.dev]
debug = 0               # No debug symbols — biggest size saver (~50%)
strip = true            # Strip remaining metadata

[profile.release]
lto = "thin"
codegen-units = 1
panic = "abort"
strip = true

[lints.rust]
unsafe_code = "deny"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] }

[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
module_name_repetitions = "allow"
must_use_candidate = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
unused_async = "allow"
# Stylistic only — Duration::from_secs(60) reads fine; new in Rust 1.95.
# Mechanically rewriting 50+ call sites risks subtle semantic confusion
# (e.g. 3600 read as "an hour" vs "60 minutes" vs "magic number").
duration_suboptimal_units = "allow"
