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