Skip to main content
Glama
docker_mcp_server_technical_spec.md10.9 kB
# Docker MCP Server Technical Specification ## Architecture Overview The Docker MCP Server will be built using the MCP Python SDK, which provides a standardized way for applications to expose context and tools to LLMs. Our implementation will focus on creating a server that provides Docker-related functionality through resources and tools. ## Server Implementation ### Core Components ```python # server.py from mcp.server.fastmcp import FastMCP, Context import docker import requests from typing import List, Dict, Optional, Any from pydantic import BaseModel, Field # Create the MCP server mcp = FastMCP( "Docker Explorer", dependencies=[ "docker", "requests", "pydantic", ] ) ``` ### Data Models ```python class DockerImage(BaseModel): name: str tag: str description: Optional[str] = None stars: Optional[int] = None pulls: Optional[int] = None last_updated: Optional[str] = None size: Optional[int] = None class DockerUser(BaseModel): username: str full_name: Optional[str] = None location: Optional[str] = None company: Optional[str] = None image_count: Optional[int] = None is_organization: bool = False class DockerfileInfo(BaseModel): repo: str path: str content: str size: int last_updated: Optional[str] = None class ImageLayer(BaseModel): digest: str size: int command: Optional[str] = None class ImageDetail(BaseModel): name: str tag: str digest: str architecture: str os: str layers: List[ImageLayer] created: str total_size: int labels: Dict[str, str] = Field(default_factory=dict) ``` ## API Integration ### Docker Hub API Client ```python class DockerHubClient: def __init__(self, username=None, password=None): self.base_url = "https://hub.docker.com/v2" self.session = requests.Session() self.token = None if username and password: self.login(username, password) def login(self, username, password): response = self.session.post( f"{self.base_url}/users/login", json={"username": username, "password": password} ) if response.status_code == 200: self.token = response.json()["token"] self.session.headers.update({"Authorization": f"JWT {self.token}"}) return True return False def search_images(self, query, limit=25): response = self.session.get( f"{self.base_url}/search/repositories", params={"query": query, "page_size": limit} ) return response.json()["results"] def get_image_tags(self, namespace, repository, limit=25): response = self.session.get( f"{self.base_url}/repositories/{namespace}/{repository}/tags", params={"page_size": limit} ) return response.json()["results"] def search_users(self, query, limit=25): response = self.session.get( f"{self.base_url}/search/users", params={"query": query, "page_size": limit} ) return response.json()["results"] ``` ### Docker Registry API Client ```python class DockerRegistryClient: def __init__(self, registry_url="https://registry-1.docker.io"): self.registry_url = registry_url self.session = requests.Session() self.session.headers.update({ "Accept": "application/vnd.docker.distribution.manifest.v2+json" }) def get_manifest(self, repository, reference): # Handle Docker Hub's namespace requirement if "/" not in repository: repository = f"library/{repository}" response = self.session.get( f"{self.registry_url}/v2/{repository}/manifests/{reference}" ) return response.json() def get_image_layers(self, repository, reference): manifest = self.get_manifest(repository, reference) return manifest.get("layers", []) ``` ## MCP Tools Implementation ### Search Tools ```python @mcp.tool() def search_images( query: str = Field(description="Search query for Docker images"), registry: str = Field(default="docker.io", description="Registry to search (default: Docker Hub)"), limit: int = Field(default=10, description="Maximum number of results to return") ) -> List[DockerImage]: """Search for Docker images across registries""" client = DockerHubClient() results = client.search_images(query, limit=limit) return [ DockerImage( name=r["repo_name"], tag="latest", description=r.get("short_description", ""), stars=r.get("star_count"), pulls=r.get("pull_count"), ) for r in results ] @mcp.tool() def search_tags( image_name: str = Field(description="Name of the Docker image"), tag_pattern: str = Field(default="", description="Pattern to match tags against") ) -> List[str]: """Search for specific tags of a Docker image""" if "/" in image_name: namespace, repository = image_name.split("/", 1) else: namespace, repository = "library", image_name client = DockerHubClient() tags = client.get_image_tags(namespace, repository) if tag_pattern: tags = [t for t in tags if tag_pattern in t["name"]] return [t["name"] for t in tags] @mcp.tool() def get_image_details( image_name: str = Field(description="Name of the Docker image"), tag: str = Field(default="latest", description="Tag of the Docker image") ) -> ImageDetail: """Get detailed information about a Docker image""" registry_client = DockerRegistryClient() # Get manifest if "/" in image_name: repository = image_name else: repository = f"library/{image_name}" manifest = registry_client.get_manifest(repository, tag) config = manifest.get("config", {}) layers = manifest.get("layers", []) # Calculate total size total_size = sum(layer.get("size", 0) for layer in layers) # Convert layers to our model layer_models = [ ImageLayer( digest=layer.get("digest", ""), size=layer.get("size", 0) ) for layer in layers ] return ImageDetail( name=image_name, tag=tag, digest=manifest.get("config", {}).get("digest", ""), architecture="amd64", # Would need to extract from config os="linux", # Would need to extract from config layers=layer_models, created="", # Would need to extract from config total_size=total_size, labels={} ) ``` ## Resources Implementation ```python @mcp.resource("docker://images/{query}") def get_images_resource(query: str) -> str: """Get information about Docker images matching a query""" client = DockerHubClient() results = client.search_images(query, limit=5) output = f"# Docker Images for '{query}' " for r in results: output += f"## {r['repo_name']}\n" output += f"**Stars:** {r.get('star_count', 0)} | **Pulls:** {r.get('pull_count', 0)}\n\n" output += f"{r.get('short_description', 'No description')}\n\n" return output @mcp.resource("docker://image/{name}/{tag}") def get_image_resource(name: str, tag: str) -> str: """Get detailed information about a specific Docker image and tag""" registry_client = DockerRegistryClient() # Handle Docker Hub's namespace requirement if "/" in name: repository = name else: repository = f"library/{name}" try: manifest = registry_client.get_manifest(repository, tag) layers = manifest.get("layers", []) total_size = sum(layer.get("size", 0) for layer in layers) output = f"# {name}:{tag}\n\n" output += f"**Digest:** {manifest.get('config', {}).get('digest', '')}\n" output += f"**Size:** {total_size / 1024 / 1024:.2f} MB\n" output += f"**Layers:** {len(layers)}\n\n" output += "## Layers\n\n" for i, layer in enumerate(layers): output += f"### Layer {i+1}\n" output += f"**Digest:** {layer.get('digest', '')}\n" output += f"**Size:** {layer.get('size', 0) / 1024 / 1024:.2f} MB\n\n" return output except Exception as e: return f"Error retrieving image details: {str(e)}" ``` ## Authentication Implementation ```python from mcp.server.auth import OAuthServerProvider, AuthSettings, RevocationOptions, ClientRegistrationOptions from mcp.server.auth.provider import TokenResponse, ClientCredentials class DockerAuthProvider(OAuthServerProvider): async def authenticate_user(self, username: str, password: str) -> TokenResponse: client = DockerHubClient() if client.login(username, password): return TokenResponse( access_token=client.token, token_type="Bearer", expires_in=3600, # Docker Hub tokens typically expire after 1 hour scope="read write" ) raise ValueError("Invalid credentials") async def register_client(self, client_name: str, redirect_uris: List[str], scopes: List[str]) -> ClientCredentials: # In a real implementation, you would register the client with Docker Hub # For now, we'll just generate a dummy client ID and secret return ClientCredentials( client_id=f"docker-mcp-{client_name}", client_secret="dummy-secret", scopes=scopes ) # Configure authentication mcp = FastMCP( "Docker Explorer", auth_server_provider=DockerAuthProvider(), auth=AuthSettings( issuer_url="https://your-docker-mcp-server.com", revocation_options=RevocationOptions( enabled=True, ), client_registration_options=ClientRegistrationOptions( enabled=True, valid_scopes=["read", "write"], default_scopes=["read"], ), required_scopes=["read"], ), ) ``` ## Deployment Configuration ```python # For development if __name__ == "__main__": mcp.run() # For production deployment # app = mcp.get_asgi_app() ``` ## Testing Strategy 1. **Unit Tests**: Test individual components like the DockerHubClient and DockerRegistryClient 2. **Integration Tests**: Test the integration between the MCP server and Docker APIs 3. **End-to-End Tests**: Test the complete flow from MCP client to Docker APIs and back ## Next Steps 1. Implement the core search functionality 2. Add authentication and authorization 3. Implement Dockerfile search and analysis 4. Add caching for improved performance 5. Create comprehensive documentation and examples

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/jar285/mcp-docker'

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