Skip to main content

Polyglot Architecture

The Oracle MCP Servers repository demonstrates a polyglot architecture where each server can be implemented in the most appropriate language for its domain.
.
├── src/
│   ├── oci-compute-mcp-server/         # Python - FastMCP framework
│   ├── oci-api-mcp-server/             # Python - OCI CLI wrapper
│   ├── mysql-mcp-server/               # Python - Direct MySQL connector
│   ├── oracle-db-mcp-java-toolkit/     # Java - Database operations
│   └── ...
Each server subdirectory is a complete, standalone MCP server with its own dependencies, tests, and documentation.

Common Server Structure

Python Servers (Most Common)

Most Oracle MCP servers follow this structure:
oci-compute-mcp-server/
├── oracle/
│   └── oci_compute_mcp_server/
│       ├── __init__.py              # Version info, project metadata
│       ├── server.py                # Main server implementation
│       ├── models.py                # Pydantic models for type safety
│       ├── consts.py                # Constants and defaults
│       └── tests/                   # Unit and integration tests
├── pyproject.toml                    # Dependencies, build config
├── README.md                         # Server-specific documentation
└── LICENSE.txt

Key Files

server.py - Core Implementation

All servers follow this pattern:
import os
from fastmcp import FastMCP
import oci

# Initialize MCP server
mcp = FastMCP(name="oracle.oci-compute-mcp-server")

# Define tools
@mcp.tool(description="List Instances in a given compartment")
def list_instances(compartment_id: str, limit: Optional[int] = None) -> list[Instance]:
    """Tool implementation with type hints and validation"""
    client = get_compute_client()
    response = client.list_instances(compartment_id=compartment_id)
    return [map_instance(inst) for inst in response.data]

# Transport mode detection
def main():
    host = os.getenv("ORACLE_MCP_HOST")
    port = os.getenv("ORACLE_MCP_PORT")
    
    if host and port:
        mcp.run(transport="http", host=host, port=int(port))
    else:
        mcp.run()  # stdio mode (default)
From src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py:419-427

models.py - Type Safety

from pydantic import BaseModel, Field
from typing import Optional

class Instance(BaseModel):
    """Represents an OCI Compute Instance"""
    id: str = Field(..., description="The OCID of the instance")
    display_name: str
    lifecycle_state: str
    availability_domain: str
    compartment_id: str
    shape: str
    # ... more fields

def map_instance(oci_instance: oci.core.models.Instance) -> Instance:
    """Convert OCI SDK model to MCP model"""
    return Instance(
        id=oci_instance.id,
        display_name=oci_instance.display_name,
        lifecycle_state=oci_instance.lifecycle_state,
        # ... map all fields
    )

pyproject.toml - Package Configuration

[project]
name = "oracle.oci-compute-mcp-server"
version = "1.2.4"
description = "OCI Compute Service MCP server"
requires-python = ">=3.13"

dependencies = [
    "fastmcp==2.14.2",
    "oci==2.160.0",
    "pydantic==2.12.3",
]

[project.scripts]
"oracle.oci-compute-mcp-server" = "oracle.oci_compute_mcp_server.server:main"
From src/oci-compute-mcp-server/pyproject.toml:1-26

Design Patterns

1. Client Factory Pattern

Servers use factory functions to create authenticated OCI clients:
def get_compute_client():
    """Create authenticated OCI Compute client"""
    config = oci.config.from_file(
        file_location=os.getenv("OCI_CONFIG_FILE", oci.config.DEFAULT_LOCATION),
        profile_name=os.getenv("OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE),
    )
    
    # Add user agent for tracking
    config["additional_user_agent"] = f"oci-compute-mcp/{__version__}"
    
    # Security token authentication
    private_key = oci.signer.load_private_key_from_file(config["key_file"])
    token_file = os.path.expanduser(config["security_token_file"])
    with open(token_file, "r") as f:
        token = f.read()
    
    signer = oci.auth.signers.SecurityTokenSigner(token, private_key)
    return oci.core.ComputeClient(config, signer=signer)
From src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py:38-53

2. Pagination Handling

OCI APIs use cursor-based pagination. Servers implement automatic pagination:
def list_instances(compartment_id: str, limit: Optional[int] = None) -> list[Instance]:
    instances: list[Instance] = []
    client = get_compute_client()
    
    has_next_page = True
    next_page: str = None
    
    while has_next_page and (limit is None or len(instances) < limit):
        response = client.list_instances(
            compartment_id=compartment_id,
            page=next_page,
            limit=limit
        )
        
        has_next_page = response.has_next_page
        next_page = response.next_page if hasattr(response, "next_page") else None
        
        for instance_data in response.data:
            instances.append(map_instance(instance_data))
    
    return instances
From src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py:78-107

3. Resource Pattern

Servers can expose read-only resources alongside tools:
@mcp.resource("resource://oci-api-commands")
def get_oci_commands() -> str:
    """Returns helpful information on various OCI services and related commands."""
    env_copy = os.environ.copy()
    env_copy["OCI_SDK_APPEND_USER_AGENT"] = USER_AGENT
    
    result = subprocess.run(
        ["oci", "--help"],
        env=env_copy,
        capture_output=True,
        text=True,
        check=True,
    )
    return result.stdout
From src/oci-api-mcp-server/oracle/oci_api_mcp_server/server.py:47-64

4. Error Handling

Consistent error handling patterns:
@mcp.tool()
def get_instance(instance_id: str) -> Instance:
    try:
        client = get_compute_client()
        response = client.get_instance(instance_id=instance_id)
        return map_instance(response.data)
    except Exception as e:
        logger.error(f"Error in get_instance tool: {str(e)}")
        raise e  # Let FastMCP handle error serialization

5. Security Controls

Servers implement safety mechanisms like denylists:
@mcp.tool
def run_oci_command(command: str) -> dict:
    """Runs an OCI CLI command with safety checks"""
    
    # Check if command is in denylist
    if denylist_manager.isCommandInDenyList(command):
        error_message = (
            f"Command '{command}' is denied by denylist. This command is found in the "
            "deny list and is not executed as it can delete resources or alter the "
            "configuration of your cloud services."
        )
        logger.error(error_message)
        return {"error": error_message}
    
    # Execute command...
From src/oci-api-mcp-server/oracle/oci_api_mcp_server/server.py:157-167

Framework Choice

FastMCP (Python)

Most Python servers use FastMCP, a high-level framework that:
  • Provides decorators for tools and resources (@mcp.tool, @mcp.resource)
  • Handles transport layer automatically (stdio and HTTP)
  • Includes type validation via Pydantic
  • Simplifies error handling and serialization

Direct Implementation

Some servers implement MCP directly:
  • Java servers - Use MCP Java SDK
  • Custom requirements - When framework limitations require direct protocol implementation

Multi-Server Deployment

Clients can connect to multiple servers simultaneously:
{
  "mcpServers": {
    "oci-compute": {
      "command": "uvx",
      "args": ["oracle.oci-compute-mcp-server@latest"]
    },
    "mysql": {
      "command": "uvx",
      "args": ["oracle.mysql-mcp-server@latest"]
    },
    "oci-api": {
      "command": "uvx",
      "args": ["oracle.oci-api-mcp-server@latest"]
    }
  }
}
The client sees all tools from all servers as one unified interface.

Containerization

Servers support containerized deployment:
# Build container for a specific server
SUBDIRS=src/oci-api-mcp-server make containerize

# Run with volume mounts for credentials
podman run -v "/path/to/.oci:/app/.oci" \
  -e ORACLE_MCP_HOST=0.0.0.0 \
  -e ORACLE_MCP_PORT=8888 \
  -p 8888:8888 \
  oracle.oci-api-mcp-server:latest

Testing Strategy

Each server includes comprehensive tests:
tests/
├── test_compute_tools.py      # Tool functionality tests
├── test_compute_models.py     # Model serialization tests
└── conftest.py                 # Shared fixtures
Run tests across all servers:
make lint    # Code quality checks
make test    # Run all unit tests

Next Steps

Transport Modes

Learn about stdio and HTTP streaming

Authentication

Configure OCI credentials

Available Servers

Browse all available MCP servers

Development Guide

Build your own MCP server

Build docs developers (and LLMs) love