Skip to main content
Glama

Omega MCP Server

A modular, production-grade Model Context Protocol (MCP) server built with Python (mcp 2.x) and MCPServer. It exposes self-documenting, reusable tools to autonomous agents, multi-agent frameworks, and external services over Server-Sent Events (SSE) using an enterprise-ready Class-Based Tool Registry.


1. Architectural Overview

Omega MCP Server acts as a centralized tool provider across consumer projects:

  • SSE Transport: Runs an HTTP service exposing an SSE stream at /sse and a JSON-RPC message endpoint at /messages/.

  • Class-Based Registry: Every tool inherits from BaseTool, ensuring strict schema validation via Pydantic and eliminating circular import risks.

  • Dynamic Autodiscovery: Modules placed in src/mcp_server/tools/ are scanned and registered on startup using Python inspection (pkgutil and inspect).

  • Predictable Agent Envelope: All tools wrap execution output in a typed AgentResponse contract (success, data, error, summary) optimized for LLM reasoning loops.

┌─────────────────────────────────────────────────────────────┐
│  Client Projects (Custom Agents, LangGraph, CrewAI, IDEs)   │
└──────────────────────────────┬──────────────────────────────┘
                               │ SSE (http://<host>:8080/sse)
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                      Omega MCP Server                       │
│                   (MCPServer SSE Engine)                    │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ ToolRegistry Engine (Dynamic Scanner & Binder)        │  │
│  │  ├── SystemStatusTool  (subclass of BaseTool)         │  │
│  │  ├── CalculateSumTool  (subclass of BaseTool)         │  │
│  │  └── <NewTool>         (subclass of BaseTool)         │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

Related MCP server: Graft

2. Standardized Agent Contract (AgentResponse)

Agents need deterministic responses to evaluate tool outcomes and plan subsequent actions. All tools return the AgentResponse schema defined in src/mcp_server/common.py.

Response Schema

{
  "success": true,
  "data": {
    "result_key": "result_value"
  },
  "error": null,
  "summary": "Concise natural language summary of what occurred."
}

Field Definitions

Field

Type

Description

success

bool

Execution status flag.

data

`dict

list

error

`string

null`

summary

`string

null`


3. Project Structure

omega-mcp-server/
├── Dockerfile
├── docker-compose.yaml
├── pyproject.toml
├── .dockerignore
├── README.md
├── scripts/
│   └── mcp_client_test.py     # Verification client test script
└── src/
    └── mcp_server/
        ├── __init__.py
        ├── common.py          # Standard AgentResponse contract model
        ├── registry.py        # BaseTool ABC & ToolRegistry engine
        ├── server.py          # MCPServer bootstrap & adapter binding
        └── tools/             # Class-based tool modules
            ├── __init__.py
            ├── system.py      # SystemStatusTool (host inspection)
            └── math.py        # CalculateSumTool (arithmetic computation)

4. Quick Start

Running Locally with uv

  1. Install dependencies:

uv sync
  1. Start the server:

uv run python -m mcp_server.server

The server starts on http://0.0.0.0:8080. 3. Verify with the test client: In a separate terminal:

uv run python -m scripts.mcp_client_test

Running with Docker

  1. Build and launch the container:

docker compose up --build -d
  1. Check logs and verify loaded tools:

docker compose logs -f
  1. Inspect the SSE endpoint:

curl -N http://localhost:8080/sse

5. Adding New Tools

The auto-discovery engine scans src/mcp_server/tools/ on boot. Adding a tool requires no modifications to server.py or registry.py.

The 4 Rules for Every Tool

  1. Subclass BaseTool: Inherit from BaseTool in mcp_server.registry.

  2. Define an Input Schema: Create a Pydantic BaseModel using Field(..., description="...") for each parameter.

  3. Set Class Attributes: Provide name, description, and args_schema.

  4. Wrap Output in AgentResponse: Return AgentResponse.ok() on success and catch exceptions with AgentResponse.fail().


Implementation Template: src/mcp_server/tools/<tool_name>.py

from typing import Any
from pydantic import BaseModel, Field
from mcp_server.common import AgentResponse
from mcp_server.registry import BaseTool


# 1. Define input parameters and descriptions
class CustomToolInput(BaseModel):
    query: str = Field(..., description="Input query or target argument")
    limit: int = Field(default=5, description="Maximum number of items to return")


# 2. Implement the tool class
class CustomTool(BaseTool):
    name = "custom_tool"
    description = "Detailed explanation of what the tool accomplishes."
    args_schema = CustomToolInput

    async def run(self, query: str, limit: int = 5) -> dict:
        try:
            # Core execution logic
            result_payload = {"echo": query, "count": limit}

            return AgentResponse.ok(
                data=result_payload,
                summary=f"Processed query '{query}' with limit {limit}."
            ).model_dump()

        except Exception as exc:
            return AgentResponse.fail(str(exc)).model_dump()

Reloading

  • Local: Restart the process (uv run python -m mcp_server.server).

  • Docker: Run docker compose restart.


6. Consuming Tools from External Agentic AI Projects

To keep agent workflows decoupled from MCP networking logic, use a two-layer structure:

  1. mcp_client.py: Manages SSE transport, session lifecycle, and maps tools to standard callables.

  2. agent.py: Imports and binds the callables directly into the agent.


Step 1: Create the Reusable Client Adapter (mcp_client.py)

Place this file inside your consumer project. It connects over SSE, executes remote tools, and extracts the payload from AgentResponse.

import asyncio
import json
import os
from typing import Any, Callable, Dict, List
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client

DEFAULT_SSE_URL = os.getenv("OMEGA_MCP_URL", "http://localhost:8080/sse")


class OmegaMCPClient:
    """Manages SSE connection and execution for remote MCP tools."""

    def __init__(self, sse_url: str = DEFAULT_SSE_URL):
        self.sse_url = sse_url

    async def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> str:
        """Call remote tool over SSE and unwrap the AgentResponse envelope."""
        async with sse_client(self.sse_url) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                response = await session.call_tool(tool_name, arguments=arguments)
                raw_text = response.content[0].text

                try:
                    payload = json.loads(raw_text)
                    if not payload.get("success", False):
                        return f"Tool Error ({tool_name}): {payload.get('error')}"
                    return str(payload.get("data"))
                except (json.JSONDecodeError, AttributeError):
                    return raw_text

    async def list_tools(self) -> List[str]:
        """Query available tool names exposed by the server."""
        async with sse_client(self.sse_url) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                res = await session.list_tools()
                return [t.name for t in res.tools]


# Shared client instance
client = OmegaMCPClient()


def create_agent_tool(name: str, description: str = "") -> Callable:
    """Factory that produces a generic callable compatible with agent frameworks."""

    async def async_dispatch(**kwargs) -> str:
        return await client.execute_tool(name, kwargs)

    def dispatch(**kwargs) -> str:
        try:
            loop = asyncio.get_running_loop()
        except RuntimeError:
            loop = None

        if loop and loop.is_running():
            import nest_asyncio
            nest_asyncio.apply()
            return loop.run_until_complete(async_dispatch(**kwargs))
        else:
            return asyncio.run(async_dispatch(**kwargs))

    dispatch.__name__ = name
    dispatch.__doc__ = description or f"Executes the remote MCP tool '{name}'."
    return dispatch


# Map remote tools to standard Python callables
get_system_status = create_agent_tool(
    name="get_system_status",
    description="Retrieves current host CPU and memory usage statistics."
)

calculate_sum = create_agent_tool(
    name="calculate_sum",
    description="Calculates the sum of two numbers (a, b)."
)

Step 2: Inject Tools into Your Agent (agent.py)

The agent imports the tool functions like native Python functions.

import os
# Replace with your framework's imports (Google GenAI, LangChain, CrewAI, AutoGen, etc.)
from your_agent_framework import Agent, llm

# Import pre-configured callables from the MCP client adapter
from mcp_client import calculate_sum, get_system_status

generic_agent = Agent(
    name="GenericTaskAgent",
    description="Executes tasks using modular tools served over MCP.",
    model=llm,
    tools=[get_system_status, calculate_sum],
    instruction="""You are a task execution agent.
    When asked about system metrics or arithmetic operations, call the appropriate 
    registered tool and summarize the result cleanly for the user.
    """
)

if __name__ == "__main__":
    # Example invocation
    prompt = "Check current system resources and calculate 45 + 55."
    print(f"User Request: {prompt}\n")

    # Run agent according to your framework API:
    # response = generic_agent.run(prompt)
    # print(response)

Step 3: Containerized Agent Communication (Docker-to-Docker)

When running both Omega MCP Server and your agent inside Docker, communicate using Docker's bridge network:

  1. Create a shared network:

docker network create agent-network
  1. Attach Omega MCP Server in docker-compose.yaml:

services:
  omega-mcp-server:
    # ...
    networks:
      - agent-network

networks:
  agent-network:
    external: true
  1. In the agent container, set the environment variable:

OMEGA_MCP_URL=http://omega_mcp_server:8080/sse

7. Common Operations

Command

Action

uv run python -m mcp_server.server

Start local server with uv

uv run python -m scripts.mcp_client_test

Run verification client test script

docker compose up --build -d

Build and run server in background

docker compose logs -f

Tail container startup and tool registration logs

docker compose restart

Restart server to reload newly added tools

docker compose down

Stop containerized server

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables building agent-ready APIs that expose tools as both HTTP and MCP endpoints from a single server definition, with automatic OpenAPI, discovery docs, and interactive API reference.
    5
    Apache 2.0