"""All models for the OpenAI Function Calling API app."""
from django.db import models
from smarter.apps.account.models import (
MetaDataWithOwnershipModel,
)
from smarter.apps.secret.models import Secret
from smarter.lib import logging
from smarter.lib.django.waffle.switches import SmarterWaffleSwitches
logger = logging.getSmarterLogger(__name__, any_switches=[SmarterWaffleSwitches.MCPCLIENT_LOGGING])
[docs]
class MCPTransport(models.TextChoices):
"""MCP Transport choice list."""
STDIO = "stdio", "Stdio (local subprocess)"
SSE = "sse", "Server-Sent Events"
HTTP = "http", "Streamable HTTP"
[docs]
class MCPAuthType(models.TextChoices):
"""MCP Authentication Type choice list."""
NONE = "none", "None"
API_KEY = "api_key", "API Key"
OAUTH2 = "oauth2", "OAuth 2.0"
BEARER_TOKEN = "bearer_token", "Bearer Token"
[docs]
class MCPConnectionStatus(models.TextChoices):
"""MCP Connection Status choice list."""
UNCONFIGURED = "unconfigured", "Unconfigured"
CONNECTED = "connected", "Connected"
DISCONNECTED = "disconnected", "Disconnected"
ERROR = "error", "Error"
[docs]
class MCPClient(MetaDataWithOwnershipModel):
"""Implements the MCPClient API model."""
# pylint: disable=C0115
class Meta:
verbose_name_plural = "MCPClients"
unique_together = ("user_profile", "name")
# --- connection ---
transport = models.CharField(max_length=16, choices=MCPTransport.choices, blank=True, null=True)
endpoint_url = models.URLField(
blank=True,
null=True,
help_text="Required for sse/http transports; omitted for stdio.",
)
command = models.CharField(
max_length=255,
blank=True,
null=True,
help_text="Executable + args for stdio transport, e.g. 'npx @scope/server'.",
)
config = models.JSONField(
default=dict,
blank=True,
null=True,
help_text="Transport-specific params: headers, env vars, timeout_s, retry_policy, etc.",
)
# --- auth ---
auth_type = models.CharField(max_length=16, choices=MCPAuthType.choices, default=MCPAuthType.NONE)
credentials = models.ForeignKey(
Secret, on_delete=models.deletion.CASCADE, help_text="auth credential", blank=True, null=True
)
# --- capability scope ---
allowed_tools = models.JSONField(
default=list,
blank=True,
null=True,
help_text="Tool name allowlist exposed to the harness; empty = all tools the server advertises.",
)
allowed_resources = models.JSONField(
default=list,
blank=True,
null=True,
help_text="Resource URI patterns this client may access; empty = all.",
)
# --- lifecycle ---
is_active = models.BooleanField(default=True)
status = models.CharField(
max_length=16,
choices=MCPConnectionStatus.choices,
default=MCPConnectionStatus.UNCONFIGURED,
)
priority = models.PositiveSmallIntegerField(
default=100,
help_text="Resolution order when multiple MCPClients could serve a request; lower runs first.",
)
# --- versioning / provenance ---
protocol_version = models.CharField(
max_length=16,
blank=True,
null=True,
help_text="Negotiated MCP protocol version from the last successful handshake.",
)
__all__ = ["MCPClient", "MCPTransport", "MCPAuthType", "MCPConnectionStatus"]