Skip to main content
Glama
server.py5.38 kB
""" MCP server that provides a tool to convert between EDIFACT and BO4E formats using the TransformerBeeClient. """ import logging import os from contextlib import asynccontextmanager from dataclasses import dataclass from typing import Any, AsyncIterator from aiohttp import ClientResponseError from efoli import EdifactFormatVersion, get_current_edifact_format_version from mcp.server.fastmcp import Context, FastMCP from transformerbeeclient import ( AuthenticatedTransformerBeeClient, BOneyComb, TransformerBeeClient, UnauthenticatedTransformerBeeClient, ) _logger = logging.getLogger(__name__) _HOST_KEY = "TRANSFORMERBEE_HOST" _CLIENT_ID_KEY = "TRANSFORMERBEE_CLIENT_ID" _CLIENT_SECRET_KEY = "TRANSFORMERBEE_CLIENT_SECRET" @dataclass class AppContext: """global context for the application""" transformerbeeclient: TransformerBeeClient def create_client(host: str, client_id: str | None, client_secret: str | None) -> TransformerBeeClient: """create a new transformer.bee client""" if not client_id or not client_secret: _logger.info( "Environment variables '%s' and/or '%s' are not set, using unauthenticated client", _CLIENT_ID_KEY, _CLIENT_SECRET_KEY, ) return UnauthenticatedTransformerBeeClient(host) _logger.info("Using authenticated client id '%s' and respective secret", client_id) return AuthenticatedTransformerBeeClient( host, oauth_client_id=client_id, oauth_client_secret=client_secret, ) @asynccontextmanager async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pylint:disable=unused-argument """Manage application lifecycle with type-safe context""" _logger.info("Trying to get environment variables; Start with '%s'", _HOST_KEY) transformerbee_host: str | None = os.environ.get(_HOST_KEY, None) if not transformerbee_host: raise ValueError(f"Environment variable '{_HOST_KEY}' not set") _logger.info("Using host '%s'", transformerbee_host) transformerbee_client_id: str | None = os.environ.get(_CLIENT_ID_KEY, None) transformerbee_client_secret: str | None = os.environ.get(_CLIENT_SECRET_KEY, None) transformerbee_client = create_client(transformerbee_host, transformerbee_client_id, transformerbee_client_secret) try: _logger.info("Instantiating context") yield AppContext(transformerbeeclient=transformerbee_client) finally: if hasattr(transformerbee_client, "close_session"): await transformerbee_client.close_session() mcp = FastMCP("TransformerBee.mcp", dependencies=["transformerbeeclient"], lifespan=app_lifespan) @mcp.tool(description="Convert an EDIFACT message to its BO4E equivalent") async def convert_edifact_to_bo4e( ctx: Context, # type:ignore[type-arg] # no idea what the second type arg is edifact: str, edifact_format_version: EdifactFormatVersion | None = None, ) -> dict[str, Any]: """Tool that uses initialized resources""" _logger.debug("Context: %s", str(ctx.request_context.lifespan_context)) client: TransformerBeeClient = ctx.request_context.lifespan_context.transformerbeeclient if not edifact_format_version: edifact_format_version = get_current_edifact_format_version() try: marktnachrichten = await client.convert_to_bo4e(edifact=edifact, edifact_format_version=edifact_format_version) except ClientResponseError as cre: _logger.warning("transformer.bee rejected the request %s: %s", cre.request_info, cre.message) _logger.exception(cre) raise except Exception: _logger.exception("Error while converting EDIFACT to BO4E") raise if len(marktnachrichten) > 1: raise NotImplementedError(f"More than 1 Marktnachricht (got {len(marktnachrichten)}) not support yet") marktnachricht = marktnachrichten[0] await ctx.info(f"Successfully converted Marktnachricht with UNH {marktnachricht.unh} to BO4E") if len(marktnachricht.transaktionen) > 1: raise NotImplementedError(f"More than 1 transaction (got {len(marktnachricht.transaktionen)}) not support yet") transaktion = marktnachricht.transaktionen[0] return transaktion.model_dump(mode="json") @mcp.tool(description="Convert a BO4E transaktion to its EDIFACT equivalent") async def convert_bo4e_to_edifact( ctx: Context, # type:ignore[type-arg] # no idea what the second type arg is transaktion: BOneyComb, edifact_format_version: EdifactFormatVersion | None = None, ) -> str: """Tool that uses initialized resources""" if not edifact_format_version: edifact_format_version = get_current_edifact_format_version() client: TransformerBeeClient = ctx.request_context.lifespan_context.transformerbeeclient try: edifact = await client.convert_to_edifact(boney_comb=transaktion, edifact_format_version=edifact_format_version) except Exception: _logger.exception("Error while converting BO4E to edifact") raise await ctx.info(f"Successfully converted BO4E to EDIFACT with format version {edifact_format_version}") return edifact def main() -> None: """entry point for the CLI tools defined in pyproject.toml""" mcp.run() if __name__ == "__main__": # called by 'mcp install server.py' and 'mcp dev server.py' main()

Implementation Reference

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/Hochfrequenz/TransformerBee.mcp'

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