Skip to main content
Glama
standards.md35.7 kB
# Coding Standards & Conventions: SSO MCP Server **Version**: 1.1 | **Date**: 2025-12-15 | **Status**: Active **Maintained by**: Development Team | **Last Reviewed**: 2025-12-15 **Note**: This document defines the coding standards, naming conventions, and best practices for the entire product. All developers must follow these standards to ensure consistency, maintainability, and quality across the codebase. --- ## Document Control | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | 2025-12-11 | Development Team | Initial standards document | | 1.1 | 2025-12-15 | Development Team | Update title to reflect multi-function server | **Related Documents**: - Architecture: `docs/architecture.md` - Ground Rules: `memory/ground-rules.md` - Feature Specifications: - `specs/001-mcp-sso-checklist/spec.md` - Checklist feature - `specs/003-process-query/spec.md` - Process Query feature --- ## Table of Contents 1. [Introduction](#1-introduction) 2. [UI Naming Conventions](#2-ui-naming-conventions) 3. [Code Naming Conventions](#3-code-naming-conventions) 4. [File and Directory Structure](#4-file-and-directory-structure) 5. [MCP Tool Design Standards](#5-mcp-tool-design-standards) 6. [Database Standards](#6-database-standards) 7. [Testing Standards](#7-testing-standards) 8. [Git Workflow](#8-git-workflow) 9. [Documentation Standards](#9-documentation-standards) 10. [Code Style Guide](#10-code-style-guide) 11. [Enforcement](#11-enforcement) 12. [Appendices](#12-appendices) --- ## 1. Introduction ### 1.1 Purpose This document establishes comprehensive coding standards and naming conventions for the SSO MCP Checklist Server. Following these standards ensures: - **Consistency**: Code looks uniform across the codebase - **Maintainability**: Code is easier to understand and modify - **Collaboration**: Team members can read and work with each other's code - **Quality**: Automated tools can enforce standards - **Onboarding**: New team members can quickly understand conventions ### 1.2 Scope These standards apply to: - All source code in the repository - All documentation - All configuration files - All MCP tool definitions - All test code ### 1.3 Technology Stack **Backend**: Python 3.11+ **MCP Framework**: mcp ^1.23.3 (FastMCP) **Authentication**: msal ^1.30.0, msal-extensions ^1.2.0 **Configuration**: python-dotenv ^1.0.0 **Checklist Parsing**: python-frontmatter ^1.1.0 **Logging**: structlog ^24.0.0 **Testing**: pytest ^8.0.0, pytest-asyncio ^0.23.0 **Linting/Formatting**: ruff ^0.5.0 **Security Scanning**: bandit ^1.7.0 ### 1.4 How to Use This Document - **Developers**: Follow these standards in all code you write - **Code Reviewers**: Verify adherence to these standards in PRs - **Team Leads**: Enforce standards and update this document as needed - **New Team Members**: Read this document during onboarding --- ## 2. UI Naming Conventions **N/A - No UI Layer** This project is a backend-only CLI application implementing an MCP server. There are no frontend components, web interfaces, or graphical user interfaces. **Project Type**: Command-line MCP server application **User Interaction**: AI assistants (GitHub Copilot, Claude Code) interact via MCP protocol **Configuration**: Environment variables and JSON configuration files If UI components are added in the future, this section should be updated following React/TypeScript conventions. --- ## 3. Code Naming Conventions ### 3.1 Variables #### 3.1.1 Local Variables **Convention**: snake_case, descriptive nouns or noun phrases ```python # Good - snake_case, descriptive user_name = "John" total_amount = 100.50 is_authenticated = True checklist_items = [] current_token = None access_token_expiry = datetime.now() # Bad - unclear, abbreviated, wrong case usr = "John" amt = 100.50 auth = True lst = [] tok = None accessTokenExpiry = datetime.now() # Wrong case ``` #### 3.1.2 Constants **Convention**: SCREAMING_SNAKE_CASE ```python # Good - SCREAMING_SNAKE_CASE MAX_RETRY_COUNT = 3 DEFAULT_TIMEOUT_SECONDS = 30 TOKEN_REFRESH_THRESHOLD_MINUTES = 5 DEFAULT_MCP_PORT = 8080 TOKEN_CACHE_FILENAME = "token_cache.bin" CHECKLIST_FILE_EXTENSION = ".md" # Bad - inconsistent casing maxRetryCount = 3 Max_Retry_Count = 3 MAXRETRYCOUNT = 3 default_timeout = 30 # Should be SCREAMING_SNAKE_CASE ``` #### 3.1.3 Module-Level Variables **Convention**: Leading underscore for internal use, SCREAMING_SNAKE_CASE for public globals ```python # Good - module-level conventions _INTERNAL_CACHE: dict[str, Any] = {} # Leading underscore for internal _logger = structlog.get_logger() # Internal logger instance # Public module constants DEFAULT_CONFIG = { "port": 8080, "log_level": "INFO" } ``` ### 3.2 Functions and Methods #### 3.2.1 Function Names **Convention**: snake_case, verb-based, descriptive ```python # Good - verb + noun, descriptive def calculate_token_expiry(token: dict) -> datetime: pass def fetch_checklist_content(name: str) -> str: pass def validate_configuration(config: dict) -> bool: pass def format_error_response(error: Exception) -> dict: pass def parse_frontmatter(content: str) -> tuple[dict, str]: pass # Bad - noun-based, unclear, too generic def total(items): # Missing verb def checklist(name): # Not descriptive def check(config): # Too generic def do_it(): # Meaningless ``` #### 3.2.2 Boolean Functions **Convention**: Prefix with `is_`, `has_`, `should_`, `can_` ```python # Good - boolean intent clear def is_authenticated(manager: AuthManager) -> bool: return manager.token is not None def has_valid_token(token: dict) -> bool: return token.get("expires_at", 0) > time.time() def should_refresh_token(token: dict, threshold_minutes: int = 5) -> bool: expiry = token.get("expires_at", 0) return (expiry - time.time()) < (threshold_minutes * 60) def can_access_checklist(user: str, checklist: str) -> bool: return True # All authenticated users can access # Bad - unclear return type def valid(token): # Could return anything def token_expired(token): # Not clearly boolean (use is_token_expired) def refresh(token): # Unclear intent ``` #### 3.2.3 Async Functions **Convention**: Same as sync functions, use `async def` ```python # Good - async functions follow same conventions async def fetch_user_profile(access_token: str) -> dict: pass async def get_checklist(name: str) -> ChecklistResponse: pass async def list_checklists() -> ListChecklistsResponse: pass # Internal async helpers async def _refresh_token_if_needed(manager: AuthManager) -> None: pass ``` #### 3.2.4 Private Functions **Convention**: Leading underscore for internal/private functions ```python # Good - private function naming def _validate_port_range(port: int) -> None: """Internal validation helper.""" if not 1024 <= port <= 65535: raise ValueError(f"Port must be between 1024 and 65535, got {port}") def _format_log_context(user: str, action: str) -> dict: """Internal logging helper.""" return {"user": user, "action": action, "timestamp": datetime.now()} # Public API def validate_configuration(config: dict) -> None: """Public configuration validation.""" _validate_port_range(config.get("port", 8080)) ``` ### 3.3 Classes and Types #### 3.3.1 Class Names **Convention**: PascalCase, nouns ```python # Good - PascalCase, descriptive nouns class AuthManager: """Manages authentication state and token lifecycle.""" pass class TokenStore: """Handles secure token persistence.""" pass class ChecklistService: """Business logic for checklist operations.""" pass class FrontmatterParser: """Parses YAML frontmatter from markdown files.""" pass class ServerConfiguration: """Holds server configuration settings.""" pass # Bad - wrong case, verbs, abbreviations class authManager: # Wrong case class ProcessAuth: # Verb-based class TokStore: # Abbreviation class svc: # Too short ``` #### 3.3.2 Exception Classes **Convention**: PascalCase with `Error` suffix ```python # Good - clear exception naming class AuthenticationError(Exception): """Raised when authentication fails.""" pass class TokenExpiredError(AuthenticationError): """Raised when the access token has expired.""" pass class ChecklistNotFoundError(Exception): """Raised when a requested checklist doesn't exist.""" pass class ConfigurationError(Exception): """Raised when configuration is invalid.""" pass class FileReadError(Exception): """Raised when a checklist file cannot be read.""" pass ``` #### 3.3.3 Type Aliases and TypedDicts **Convention**: PascalCase, descriptive ```python from typing import TypedDict, TypeAlias # Type aliases ChecklistName: TypeAlias = str AccessToken: TypeAlias = str FilePath: TypeAlias = str # TypedDict for structured data class ChecklistMetadata(TypedDict): name: str description: str | None class ChecklistResponse(TypedDict): name: str description: str | None content: str class ListChecklistsResponse(TypedDict): checklists: list[ChecklistMetadata] count: int class TokenData(TypedDict): access_token: str refresh_token: str | None expires_at: float ``` #### 3.3.4 Enums **Convention**: PascalCase for enum, SCREAMING_SNAKE_CASE for values ```python from enum import Enum, auto class LogLevel(Enum): DEBUG = "DEBUG" INFO = "INFO" WARNING = "WARNING" ERROR = "ERROR" class AuthState(Enum): NOT_AUTHENTICATED = auto() AUTHENTICATED = auto() TOKEN_EXPIRED = auto() REFRESHING = auto() class ErrorCode(Enum): NOT_AUTHENTICATED = "NOT_AUTHENTICATED" CHECKLIST_NOT_FOUND = "CHECKLIST_NOT_FOUND" FILE_READ_ERROR = "FILE_READ_ERROR" DIRECTORY_READ_ERROR = "DIRECTORY_READ_ERROR" CONFIGURATION_ERROR = "CONFIGURATION_ERROR" ``` ### 3.4 Modules and Packages **Convention**: lowercase with underscores ```python # Good - lowercase with underscores # File names auth_manager.py token_store.py checklist_service.py frontmatter_parser.py file_discovery.py # Package names sso_mcp_server/ auth/ checklists/ config/ tools/ ``` --- ## 4. File and Directory Structure ### 4.1 File Naming #### 4.1.1 Source Code Files **Convention**: snake_case.py ``` # Good - snake_case server.py auth_manager.py token_store.py browser_auth.py checklist_service.py frontmatter_parser.py file_discovery.py settings.py get_checklist.py list_checklists.py ``` #### 4.1.2 Test Files **Convention**: `test_<module_name>.py` ``` # Good - test_ prefix test_auth_manager.py test_token_store.py test_checklist_service.py test_frontmatter_parser.py test_file_discovery.py test_mcp_tools.py test_auth_flow.py ``` #### 4.1.3 Configuration Files ``` # Root level configuration .env # Environment variables (not committed) .env.example # Example environment template pyproject.toml # Project configuration and dependencies ruff.toml # Ruff linter configuration (if not in pyproject.toml) .pre-commit-config.yaml # Pre-commit hooks .editorconfig # Editor configuration ``` ### 4.2 Directory Structure **Per architecture.md §6.1:** ``` sso-mcp-server/ ├── src/ │ └── sso_mcp_server/ │ ├── __init__.py # Package init with logging setup │ ├── __main__.py # CLI entry point │ ├── server.py # MCP Server Core (FastMCP, HTTP Streamable) │ ├── config/ │ │ ├── __init__.py │ │ └── settings.py # Environment configuration │ ├── auth/ │ │ ├── __init__.py # Auth module exports │ │ ├── manager.py # Auth Manager (orchestration + token refresh) │ │ ├── browser.py # Browser Auth (PKCE flow) │ │ ├── token_store.py # Token persistence (msal-extensions) │ │ └── middleware.py # Auth middleware for tool calls │ ├── checklists/ │ │ ├── __init__.py # Checklist module exports │ │ ├── service.py # Checklist business logic │ │ ├── discovery.py # File discovery (glob) │ │ └── parser.py # Frontmatter parser │ └── tools/ │ ├── __init__.py # Tool registration │ ├── get_checklist.py # get_checklist tool │ └── list_checklists.py # list_checklists tool ├── checklists/ # Default checklist directory │ ├── coding.md │ ├── architecture.md │ └── detailed-design.md ├── tests/ │ ├── conftest.py # Shared fixtures │ ├── unit/ │ │ ├── test_auth_manager.py │ │ ├── test_token_store.py │ │ ├── test_checklist_service.py │ │ ├── test_frontmatter_parser.py │ │ └── test_file_discovery.py │ └── integration/ │ ├── test_auth_flow.py │ └── test_mcp_tools.py ├── docs/ │ ├── architecture.md │ ├── standards.md # This file │ └── adr/ # Architecture Decision Records ├── specs/ │ └── 001-mcp-sso-checklist/ │ ├── spec.md │ ├── design.md │ └── tasks.md ├── memory/ │ └── ground-rules.md ├── .env.example ├── pyproject.toml ├── README.md └── CLAUDE.md ``` ### 4.3 Module Organization **Package `__init__.py` conventions:** ```python # src/sso_mcp_server/auth/__init__.py """Authentication module for SSO MCP Server.""" from .manager import AuthManager from .token_store import TokenStore from .browser import BrowserAuth from .middleware import auth_middleware __all__ = [ "AuthManager", "TokenStore", "BrowserAuth", "auth_middleware", ] ``` --- ## 5. MCP Tool Design Standards This project implements MCP (Model Context Protocol) tools, not REST APIs. This section defines standards for MCP tool design. ### 5.1 Tool Naming **Convention**: snake_case, verb-noun pattern ```python # Good - MCP tool names @mcp.tool() async def get_checklist(name: str) -> ChecklistResponse: """Retrieve a specific checklist by name.""" pass @mcp.tool() async def list_checklists() -> ListChecklistsResponse: """List all available checklists.""" pass # Bad - wrong conventions @mcp.tool() async def GetChecklist(name: str): # Wrong case pass @mcp.tool() async def checklist(name: str): # Missing verb pass ``` ### 5.2 Tool Input/Output Schemas **Convention**: Use TypedDict or dataclasses with JSON schema annotations ```python from typing import TypedDict # Input schemas (when tool has parameters) class GetChecklistInput(TypedDict): name: str # Required: Name of the checklist to retrieve # Output schemas class ChecklistResponse(TypedDict): name: str description: str | None content: str class ChecklistMetadata(TypedDict): name: str description: str | None class ListChecklistsResponse(TypedDict): checklists: list[ChecklistMetadata] count: int ``` ### 5.3 Tool Documentation **Convention**: Docstrings with clear descriptions ```python @mcp.tool() async def get_checklist(name: str) -> ChecklistResponse: """Retrieve a specific checklist by name. Args: name: The name of the checklist to retrieve (e.g., "coding", "architecture"). This corresponds to the markdown filename without extension. Returns: ChecklistResponse containing: - name: The checklist name - description: Optional description from YAML frontmatter - content: The full markdown content of the checklist Raises: McpError: With code CHECKLIST_NOT_FOUND if checklist doesn't exist McpError: With code NOT_AUTHENTICATED if user is not authenticated McpError: With code FILE_READ_ERROR if file cannot be read """ pass ``` ### 5.4 Error Responses **Convention**: Use MCP error codes with descriptive messages ```python from mcp.server.fastmcp import FastMCP from mcp import McpError, ErrorCode # Standard error codes for this project ERROR_CODES = { "NOT_AUTHENTICATED": "User is not authenticated. Please authenticate first.", "CHECKLIST_NOT_FOUND": "Checklist '{name}' not found. Available: {available}", "FILE_READ_ERROR": "Failed to read checklist file: {error}", "DIRECTORY_READ_ERROR": "Failed to read checklist directory: {error}", } # Error handling in tools @mcp.tool() async def get_checklist(name: str) -> ChecklistResponse: if not auth_manager.is_authenticated(): raise McpError( ErrorCode.UNAUTHORIZED, ERROR_CODES["NOT_AUTHENTICATED"] ) checklist = checklist_service.get(name) if checklist is None: available = ", ".join(checklist_service.list_names()) raise McpError( ErrorCode.NOT_FOUND, ERROR_CODES["CHECKLIST_NOT_FOUND"].format(name=name, available=available) ) return checklist ``` ### 5.5 Tool Contract JSON Schema **Per `specs/001-mcp-sso-checklist/contracts/mcp-tools.json`:** ```json { "tools": [ { "name": "get_checklist", "description": "Retrieve a specific checklist by name", "inputSchema": { "type": "object", "properties": { "name": { "type": "string", "description": "Name of the checklist to retrieve" } }, "required": ["name"] } }, { "name": "list_checklists", "description": "List all available checklists", "inputSchema": { "type": "object", "properties": {}, "required": [] } } ] } ``` --- ## 6. Database Standards **N/A - No Database** This project uses local file system storage for checklists and token persistence: - **Checklists**: Markdown files (`.md`) in configurable directory - **Token Cache**: Encrypted binary file (`~/.sso-mcp-server/token_cache.bin`) - **Configuration**: Environment variables **File Storage Conventions:** ```python # Checklist files - YAML frontmatter + markdown """ --- name: Coding Standards Checklist description: Quality checklist for code implementation --- # Coding Standards Checklist ## Pre-Implementation - [ ] Requirements understood - [ ] Design reviewed ... """ # Path conventions CHECKLIST_DIR = os.environ.get("CHECKLIST_DIR", "./checklists") TOKEN_CACHE_DIR = Path.home() / ".sso-mcp-server" TOKEN_CACHE_FILE = TOKEN_CACHE_DIR / "token_cache.bin" ``` --- ## 7. Testing Standards ### 7.1 Test File Organization ``` tests/ ├── conftest.py # Shared fixtures ├── unit/ # Unit tests (isolated, fast) │ ├── test_auth_manager.py │ ├── test_token_store.py │ ├── test_checklist_service.py │ ├── test_frontmatter_parser.py │ └── test_file_discovery.py └── integration/ # Integration tests (with dependencies) ├── test_auth_flow.py └── test_mcp_tools.py ``` ### 7.2 Test Naming **Convention**: `test_<what>_<condition>_<expected>` ```python # Good - descriptive test names def test_get_checklist_with_valid_name_returns_content(): pass def test_get_checklist_with_nonexistent_name_raises_not_found(): pass def test_auth_manager_refresh_when_token_expires_soon_succeeds(): pass def test_frontmatter_parser_with_missing_frontmatter_returns_empty_metadata(): pass def test_list_checklists_when_directory_empty_returns_empty_list(): pass # Bad - unclear test names def test_checklist_1(): pass def test_get(): pass def test_auth(): pass ``` ### 7.3 Test Structure (AAA Pattern) **Convention**: Arrange-Act-Assert with clear sections ```python def test_checklist_service_get_with_valid_name_returns_content(): # Arrange service = ChecklistService(checklist_dir="/tmp/test_checklists") checklist_path = Path("/tmp/test_checklists/coding.md") checklist_path.write_text("""--- name: Coding description: Coding standards --- # Coding Checklist - Item 1 """) # Act result = service.get("coding") # Assert assert result is not None assert result["name"] == "coding" assert result["description"] == "Coding standards" assert "# Coding Checklist" in result["content"] ``` ### 7.4 Fixtures **Convention**: Descriptive names, scoped appropriately ```python # conftest.py import pytest from pathlib import Path import tempfile @pytest.fixture def temp_checklist_dir(): """Create a temporary directory for checklist files.""" with tempfile.TemporaryDirectory() as tmpdir: yield Path(tmpdir) @pytest.fixture def sample_checklist_content(): """Return sample checklist content with frontmatter.""" return """--- name: Test Checklist description: A test checklist for unit tests --- # Test Checklist ## Section 1 - [ ] Item 1 - [ ] Item 2 """ @pytest.fixture def checklist_service(temp_checklist_dir): """Create a ChecklistService with temp directory.""" return ChecklistService(checklist_dir=str(temp_checklist_dir)) @pytest.fixture def mock_msal_app(mocker): """Mock MSAL PublicClientApplication.""" mock_app = mocker.MagicMock() mock_app.acquire_token_interactive.return_value = { "access_token": "test_access_token", "refresh_token": "test_refresh_token", "expires_in": 3600, } return mock_app ``` ### 7.5 Async Test Conventions **Convention**: Use pytest-asyncio with `@pytest.mark.asyncio` ```python import pytest @pytest.mark.asyncio async def test_get_checklist_tool_returns_content(): # Arrange mcp_server = create_test_server() # Act result = await mcp_server.call_tool("get_checklist", {"name": "coding"}) # Assert assert result["name"] == "coding" assert "content" in result @pytest.mark.asyncio async def test_auth_manager_ensure_authenticated_opens_browser(): # Arrange auth_manager = AuthManager(client_id="test", tenant_id="test") # Act & Assert with pytest.raises(AuthenticationError): await auth_manager.ensure_authenticated() ``` ### 7.6 Coverage Requirements **Per ground-rules.md and architecture.md:** - **Minimum Coverage**: 80% for critical paths (auth, MCP modules) - **Critical Paths**: - `auth/manager.py` - Authentication orchestration - `auth/token_store.py` - Token persistence - `checklists/service.py` - Checklist operations - `tools/get_checklist.py` - MCP tool implementation - `tools/list_checklists.py` - MCP tool implementation ```bash # Run tests with coverage pytest --cov=src/sso_mcp_server --cov-report=term-missing --cov-fail-under=80 ``` --- ## 8. Git Workflow ### 8.1 Branch Naming **Convention**: `{type}/{issue-number}-{brief-description}` ```bash # Good - structured branch names feature/001-mcp-sso-checklist feature/002-add-rate-limiting bugfix/003-fix-token-refresh hotfix/004-security-patch chore/005-update-dependencies docs/006-update-readme # Bad - unclear branches john-branch fix-bug new-feature wip ``` **Branch Types**: - `feature/` - New features - `bugfix/` - Bug fixes - `hotfix/` - Critical production fixes - `chore/` - Maintenance tasks - `docs/` - Documentation updates - `refactor/` - Code refactoring ### 8.2 Commit Messages **Convention**: Conventional Commits format ``` <type>[optional scope]: <description> [optional body] [optional footer(s)] ``` **Types**: - `feat`: New feature - `fix`: Bug fix - `docs`: Documentation changes - `style`: Code style changes (formatting, no logic changes) - `refactor`: Code refactoring - `test`: Adding or updating tests - `chore`: Maintenance tasks - `perf`: Performance improvements - `ci`: CI/CD changes **Examples**: ```bash # Good - conventional commits feat(auth): add token refresh when less than 5 minutes remaining fix(checklist): handle missing frontmatter gracefully docs(readme): add quickstart guide for VSCode configuration refactor(auth): simplify token validation logic test(checklist): add unit tests for frontmatter parser chore(deps): update mcp library to 1.23.3 # With body and footer feat(mcp): implement get_checklist tool Implement the get_checklist MCP tool that retrieves checklist content by name. Includes YAML frontmatter parsing for metadata extraction. Closes #123 # Bad - unclear commits update code fix bug changes WIP ``` ### 8.3 Pull Request Format **Convention**: Clear title and structured description ```markdown ## Summary Brief description of what this PR does. ## Changes - Bullet point list of changes - Another change ## Test Plan - [ ] Unit tests pass - [ ] Integration tests pass - [ ] Manual testing completed ## Checklist - [ ] Code follows project standards - [ ] Tests added/updated - [ ] Documentation updated - [ ] No security vulnerabilities ``` --- ## 9. Documentation Standards ### 9.1 Code Comments **Convention**: Explain "why", not "what" ```python # Good - explains why def should_refresh_token(token: dict, threshold_minutes: int = 5) -> bool: """Check if token should be refreshed. We refresh proactively when less than 5 minutes remain to ensure uninterrupted operation during long-running tool calls. """ expiry = token.get("expires_at", 0) # Threshold is configurable but defaults to 5 minutes per spec clarification return (expiry - time.time()) < (threshold_minutes * 60) # Bad - states the obvious def calculate_expiry(seconds: int) -> datetime: # Get the current time now = datetime.now() # Add seconds to current time expiry = now + timedelta(seconds=seconds) # Return the expiry time return expiry ``` ### 9.2 Docstrings **Convention**: Google style docstrings ```python def get_checklist(name: str) -> ChecklistResponse: """Retrieve a checklist by name. Loads the checklist file from the configured directory, parses YAML frontmatter for metadata, and returns the complete content. Args: name: The checklist name (filename without .md extension). Must match an existing file in CHECKLIST_DIR. Returns: ChecklistResponse containing name, description, and content. Raises: ChecklistNotFoundError: If no checklist with the given name exists. FileReadError: If the checklist file cannot be read. Example: >>> service = ChecklistService("/path/to/checklists") >>> result = service.get("coding") >>> print(result["name"]) 'coding' """ pass ``` ### 9.3 Module Docstrings ```python """Authentication manager for SSO MCP Server. This module provides the AuthManager class which orchestrates the OAuth 2.0 authentication flow with Azure Entra ID. It handles: - Initial browser-based authentication with PKCE - Secure token persistence using msal-extensions - Automatic token refresh when expiry is near - Session state management Example: >>> manager = AuthManager(client_id="...", tenant_id="...") >>> await manager.ensure_authenticated() >>> token = manager.get_access_token() See Also: - spec.md FR-001 through FR-005 for authentication requirements - architecture.md §5.2 for component design """ ``` ### 9.4 Type Hints **Convention**: Full type hints for all public APIs ```python from typing import Optional, Any from pathlib import Path from datetime import datetime # Good - complete type hints def parse_frontmatter(content: str) -> tuple[dict[str, Any], str]: """Parse YAML frontmatter from markdown content.""" pass def discover_checklists(directory: Path) -> list[Path]: """Discover all markdown files in directory.""" pass async def get_access_token(manager: AuthManager) -> str | None: """Get current access token if authenticated.""" pass # Class with type hints class TokenStore: def __init__(self, cache_path: Path) -> None: self._cache_path = cache_path def save(self, token_data: dict[str, Any]) -> None: pass def load(self) -> dict[str, Any] | None: pass def clear(self) -> None: pass ``` --- ## 10. Code Style Guide ### 10.1 Indentation **Convention**: 4 spaces (Python standard) ```python # Good - 4 spaces def example_function(): if condition: do_something() if nested_condition: do_nested_thing() # Bad - inconsistent def example_function(): if condition: # 2 spaces do_something() # 8 spaces ``` ### 10.2 Line Length **Convention**: Maximum 88 characters (ruff default) ```python # Good - break long lines checklist_response = ChecklistResponse( name=checklist_name, description=metadata.get("description"), content=content, ) error_message = ( f"Checklist '{name}' not found. " f"Available checklists: {', '.join(available_names)}" ) # Bad - exceeds line length checklist_response = ChecklistResponse(name=checklist_name, description=metadata.get("description"), content=content) ``` ### 10.3 Blank Lines **Convention**: 2 blank lines between top-level definitions, 1 within classes ```python """Module docstring.""" import os from pathlib import Path from mcp import McpError CONSTANT_VALUE = 42 class FirstClass: """First class docstring.""" def __init__(self): pass def method_one(self): pass def method_two(self): pass class SecondClass: """Second class docstring.""" pass def top_level_function(): """Function docstring.""" pass ``` ### 10.4 Import Ordering **Convention**: isort-compatible ordering (handled by ruff) ```python # Standard library imports import os import sys from datetime import datetime from pathlib import Path from typing import Any # Third-party imports import msal import structlog from mcp.server.fastmcp import FastMCP # Local imports from sso_mcp_server.auth import AuthManager from sso_mcp_server.checklists import ChecklistService from sso_mcp_server.config import settings ``` ### 10.5 String Formatting **Convention**: f-strings for string interpolation ```python # Good - f-strings name = "coding" message = f"Checklist '{name}' not found" log.info(f"Processing checklist: {name}") # Also acceptable - for complex formatting template = "Checklist '{name}' has {count} items" message = template.format(name=name, count=len(items)) # Bad - old-style formatting message = "Checklist '%s' not found" % name message = "Checklist '{}' not found".format(name) ``` --- ## 11. Enforcement ### 11.1 Automated Tools **Linting and Formatting**: ruff ```toml # pyproject.toml [tool.ruff] target-version = "py311" line-length = 88 select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "B", # flake8-bugbear "C4", # flake8-comprehensions "UP", # pyupgrade "ARG", # flake8-unused-arguments "SIM", # flake8-simplify ] ignore = [ "E501", # line too long (handled by formatter) ] [tool.ruff.isort] known-first-party = ["sso_mcp_server"] ``` **Security Scanning**: bandit ```toml # pyproject.toml [tool.bandit] exclude_dirs = ["tests"] skips = ["B101"] # assert statements in tests ``` ### 11.2 Pre-commit Hooks ```yaml # .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-json - id: check-added-large-files - id: check-merge-conflict - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.5.0 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/PyCQA/bandit rev: 1.7.8 hooks: - id: bandit args: [-c, pyproject.toml] additional_dependencies: ["bandit[toml]"] ``` ### 11.3 CI/CD Integration ```yaml # .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v1 - run: uv sync --dev - run: uv run ruff check . - run: uv run ruff format --check . - run: uv run bandit -c pyproject.toml -r src/ test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v1 - run: uv sync --dev - run: uv run pytest --cov=src/sso_mcp_server --cov-fail-under=80 ``` ### 11.4 Code Review Checklist - [ ] Follows naming conventions (Section 3) - [ ] Follows file structure (Section 4) - [ ] MCP tools follow standards (Section 5) - [ ] Has appropriate tests (Section 7) - [ ] Has meaningful commit messages (Section 8) - [ ] Has proper documentation and type hints (Section 9) - [ ] Passes linter checks (Section 11) - [ ] No security vulnerabilities (bandit) --- ## 12. Appendices ### 12.1 Glossary | Term | Definition | |------|------------| | PascalCase | Capitalized first letter of each word: `AuthManager` | | camelCase | Lowercase first letter, capitalized subsequent words: `authManager` | | snake_case | Lowercase with underscores: `auth_manager` | | SCREAMING_SNAKE_CASE | Uppercase with underscores: `AUTH_MANAGER` | | MCP | Model Context Protocol - Anthropic's protocol for AI assistant tools | | PKCE | Proof Key for Code Exchange - OAuth 2.0 security extension | | SSO | Single Sign-On | | FastMCP | High-level Python framework for building MCP servers | ### 12.2 Quick Reference Checklist **Python Naming**: - [ ] Variables: snake_case - [ ] Constants: SCREAMING_SNAKE_CASE - [ ] Functions: snake_case, verb-based - [ ] Classes: PascalCase, nouns - [ ] Files: snake_case.py - [ ] Packages: lowercase_with_underscores **MCP Tools**: - [ ] Tool names: snake_case - [ ] Input/Output: TypedDict with type hints - [ ] Errors: MCP error codes with descriptive messages - [ ] Documentation: Google-style docstrings **Testing**: - [ ] Files: test_<module>.py - [ ] Functions: test_<what>_<condition>_<expected> - [ ] Pattern: Arrange-Act-Assert - [ ] Coverage: >80% for critical paths **Git**: - [ ] Branches: type/issue-description - [ ] Commits: Conventional Commits format - [ ] PRs: Clear title, structured description ### 12.3 Tool Configuration Summary | Tool | Purpose | Config Location | |------|---------|-----------------| | ruff | Linting & formatting | pyproject.toml | | bandit | Security scanning | pyproject.toml | | pytest | Testing | pyproject.toml | | pre-commit | Git hooks | .pre-commit-config.yaml | | editorconfig | Editor settings | .editorconfig | ### 12.4 Resources - [PEP 8 - Style Guide for Python Code](https://peps.python.org/pep-0008/) - [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html) - [Conventional Commits](https://www.conventionalcommits.org/) - [MCP Specification](https://modelcontextprotocol.io/) - [Ruff Documentation](https://docs.astral.sh/ruff/) - [pytest Documentation](https://docs.pytest.org/) --- **END OF STANDARDS DOCUMENT** --- ## Maintenance Notes This document should be: - **Reviewed quarterly** by the team - **Updated** when new technologies are adopted - **Referenced** in code reviews - **Shared** with new team members during onboarding - **Enforced** through automated tools For questions or suggestions, contact the Development Team.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DauQuangThanh/sso-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server