We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/marioser/dolibarr-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Configuration management for Dolibarr MCP Server."""
import os
import sys
from pydantic import AliasChoices, Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
class Config(BaseSettings):
"""Configuration for Dolibarr MCP Server."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
validate_assignment=True,
extra="ignore",
)
dolibarr_url: str = Field(
description="Dolibarr API URL",
default="",
)
dolibarr_api_key: str = Field(
description="Dolibarr API key",
default="",
validation_alias=AliasChoices("dolibarr_api_key", "api_key"),
)
log_level: str = Field(
description="Logging level",
default="INFO",
)
mcp_transport: str = Field(
description="Transport for MCP server (stdio or http)",
default="stdio",
)
mcp_http_host: str = Field(
description="HTTP host/interface for MCP server",
default="0.0.0.0",
)
mcp_http_port: int = Field(
description="HTTP port for MCP server",
default=8080,
)
allow_ref_autogen: bool = Field(
description="Allow automatic generation of reference fields when missing",
default=False,
)
ref_autogen_prefix: str = Field(
description="Prefix to use when auto-generating references",
default="AUTO",
)
debug_mode: bool = Field(
description="Enable verbose debug logging without exposing secrets",
default=False,
)
max_retries: int = Field(
description="Maximum retries for transient HTTP errors (502, 503, 504)",
default=3,
)
retry_backoff_seconds: float = Field(
description="Base backoff (seconds) for retry strategy with exponential backoff",
default=1.0,
)
request_timeout: int = Field(
description="Request timeout in seconds",
default=60,
)
@field_validator("dolibarr_url")
@classmethod
def validate_dolibarr_url(cls, v: str) -> str:
"""Validate Dolibarr URL."""
if not v:
v = (
os.getenv("DOLIBARR_URL")
or os.getenv("DOLIBARR_BASE_URL")
or os.getenv("DOLIBARR_SHOP_URL")
or ""
)
if not v:
# Print warning but don't fail
print(
"⚠️ DOLIBARR_URL/DOLIBARR_BASE_URL not configured - API calls will fail",
file=sys.stderr,
)
return "https://your-dolibarr-instance.com/api/index.php"
if not v.startswith(("http://", "https://")):
raise ValueError("DOLIBARR_URL must start with http:// or https://")
# Remove trailing slash if present
v = v.rstrip("/")
# Ensure it ends with the proper API path
if not v.endswith("/api/index.php"):
if "/api" in v:
if not v.endswith("/index.php"):
if v.endswith("/index.php/"):
v = v[:-1]
elif not v.endswith("/index.php"):
v = v + "/index.php"
else:
v = v + "/api/index.php"
return v
@field_validator("dolibarr_api_key")
@classmethod
def validate_api_key(cls, v: str) -> str:
"""Validate API key."""
if not v:
v = os.getenv("DOLIBARR_API_KEY", "")
if not v:
print(
"⚠️ DOLIBARR_API_KEY not configured - API authentication will fail",
file=sys.stderr,
)
print(
"📝 Please set DOLIBARR_API_KEY in your .env file or Claude configuration",
file=sys.stderr,
)
return "placeholder_api_key"
if v == "your_dolibarr_api_key_here":
print(
"⚠️ Using placeholder API key - please configure a real API key",
file=sys.stderr,
)
return v
@field_validator("log_level")
@classmethod
def validate_log_level(cls, v: str) -> str:
"""Validate log level."""
if not v:
v = os.getenv("LOG_LEVEL", "INFO")
valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
if v.upper() not in valid_levels:
print(f"⚠️ Invalid LOG_LEVEL '{v}', using INFO", file=sys.stderr)
return "INFO"
return v.upper()
@field_validator("mcp_transport")
@classmethod
def validate_transport(cls, v: str) -> str:
"""Validate MCP transport selection."""
if not v:
v = os.getenv("MCP_TRANSPORT", "stdio")
normalized = v.lower()
if normalized not in {"stdio", "http"}:
print(f"⚠️ Invalid MCP_TRANSPORT '{v}', defaulting to stdio", file=sys.stderr)
return "stdio"
return normalized
@field_validator("mcp_http_host")
@classmethod
def validate_http_host(cls, v: str) -> str:
"""Validate HTTP host."""
return v or os.getenv("MCP_HTTP_HOST", "0.0.0.0")
@field_validator("mcp_http_port")
@classmethod
def validate_http_port(cls, v: int) -> int:
"""Validate HTTP port."""
try:
port = int(v)
except Exception as exc:
raise ValueError(f"Invalid MCP_HTTP_PORT '{v}': {exc}") from exc
if not 1 <= port <= 65535:
raise ValueError("MCP_HTTP_PORT must be between 1 and 65535")
return port
@classmethod
def from_env(cls) -> "Config":
"""Create configuration from environment variables with validation."""
try:
config = cls(
dolibarr_url=(
os.getenv("DOLIBARR_URL")
or os.getenv("DOLIBARR_BASE_URL")
or os.getenv("DOLIBARR_SHOP_URL")
or ""
),
dolibarr_api_key=os.getenv("DOLIBARR_API_KEY", ""),
log_level=os.getenv("LOG_LEVEL", "INFO"),
)
if os.getenv("DEBUG_CONFIG"):
print(f"✅ Config loaded:", file=sys.stderr)
print(f" URL: {config.dolibarr_url}", file=sys.stderr)
print(
f" API Key: {'*' * 10 if config.dolibarr_api_key else 'NOT SET'}",
file=sys.stderr,
)
return config
except Exception as e:
print(f"❌ Configuration Error: {e}", file=sys.stderr)
print(file=sys.stderr)
print("💡 Quick Setup Guide:", file=sys.stderr)
print("1. Copy .env.example to .env", file=sys.stderr)
print("2. Edit .env with your Dolibarr details:", file=sys.stderr)
print(
" DOLIBARR_URL=https://your-dolibarr-instance.com",
file=sys.stderr,
)
print(
" (or DOLIBARR_BASE_URL=https://your-dolibarr-instance.com/api/index.php/)",
file=sys.stderr,
)
print(" DOLIBARR_API_KEY=your_api_key_here", file=sys.stderr)
print(file=sys.stderr)
print("🔧 Dolibarr API Key Setup:", file=sys.stderr)
print(" 1. Login to Dolibarr as admin", file=sys.stderr)
print(" 2. Go to: Home → Setup → Modules", file=sys.stderr)
print(" 3. Enable: 'Web Services API REST (developer)'", file=sys.stderr)
print(" 4. Go to: Home → Setup → API/Web services", file=sys.stderr)
print(" 5. Create a new API key", file=sys.stderr)
print(file=sys.stderr)
raise
def validate_config(self) -> None:
"""Validate current configuration values."""
self.dolibarr_url = type(self).validate_dolibarr_url(self.dolibarr_url)
self.dolibarr_api_key = type(self).validate_api_key(self.dolibarr_api_key)
self.log_level = type(self).validate_log_level(self.log_level)
if self.dolibarr_url.endswith('your-dolibarr-instance.com/api/index.php') or self.dolibarr_api_key in {'', 'placeholder_api_key', 'your_dolibarr_api_key_here'}:
raise ValueError('Dolibarr configuration is incomplete')
@property
def api_key(self) -> str:
"""Backward compatibility for api_key property."""
return self.dolibarr_api_key
@api_key.setter
def api_key(self, value: str) -> None:
"""Allow updating the API key via legacy attribute."""
self.dolibarr_api_key = value