MCP Services API
This module implements the client-side logic for the Model Context Protocol (MCP), allowing the AI Agent to connect to, discover, and execute tools on remote servers (like Notion, GitHub, or local dev tools).
Location: backend/app/services/mcp/
MCP Connector
File: connector.py
The MCPConnector class is the heavy lifter. It manages the persistent connection lifecycle, handling the complexities of SSE (Server-Sent Events) or HTTP streams, and ensuring authentication remains valid.
Class: MCPConnector
class MCPConnector:
def __init__(
self,
server_url: str,
credentials: Optional[str] = None,
oauth_config: Optional[Dict] = None,
...
)Key Responsibilities:
- Session Management: Maintains a persistent
ClientSessionusingmcp.client. It automatically detects whether to use SSE or HTTP transport. - Header Injection: Inject auth tokens (e.g.,
Authorization: Bearer <token>,X-Figma-Token) based on the server domain. - Automatic Retries: Wraps operations in
_execute_with_retryto handle network blips.
Core Methods
run_tool
async def run_tool(self, tool_name: str, parameters: dict) -> AnyExecutes a specific tool on the remote server.
- Retry Policy: If execution fails with a 401 (Unauthorized), it automatically attempts to refresh the OAuth token and retry the execution once.
- Timeout: Enforces a 60-second timeout to prevent the agent from hanging indefinitely on slow tools.
list_tools
async def list_tools(self) -> List[Dict[str, Any]]Fetches the tools/list from the server.
- Caching: Implements a module-level
_TOOLS_CACHEto avoid repeated network calls for tool definitions during the same server lifecycle.
Token Manager
File: token_manager.py
A specialized utility for handling OAuth2 token lifecycles. It ensures the agent never fails mid-task due to an expired access token.
refresh_oauth_token
async def refresh_oauth_token(
server_name: str,
credentials: dict,
oauth_config: dict
) -> Optional[dict]Performs a standard refresh_token grant exchange:
- Extracts the
refresh_tokenfrom current credentials. - POSTs to the provider's
token_url. - Calculates the new
expires_attimestamp based onexpires_inresponse (defaulting to 1 hour if unspecified).
is_token_expired
def is_token_expired(
credentials: dict,
buffer_seconds: int = 300
) -> boolLogic: Returns True if now >= (expires_at - 300).
- Why the buffer? We proactively treat a token as "expired" 5 minutes early. This prevents race conditions where a token might be valid now but expires 2 seconds later while the request is in flight.
Authentication
File: auth.py
Handles the "Calibration Phase" - the initial handshake when a user adds a new MCP server.
exchange_code_for_token: Swaps the temporary OAuth authorization code for the initial Access/Refresh token pair.init_connection: Verifies the connection works immediately after setup by attempting a "ping" or listing tools.