Skip to main content
Glama
server.py10.1 kB
import asyncio import logging import os from mcp import LoggingLevel import mcp.server.stdio import mcp.types as types import unichat from mcp.server import NotificationOptions, Server from mcp.server.models import InitializationOptions # Set up logging logger = logging.getLogger("unichat-mcp-server") logger.setLevel(logging.INFO) # Initialize the server server = Server("unichat-mcp-server") # API configuration MODEL = os.getenv("UNICHAT_MODEL") if not MODEL: logger.error("UNICHAT_MODEL environment variable not found") raise ValueError("UNICHAT_MODEL environment variable required") if not any(MODEL in models_list for models_list in unichat.MODELS_LIST.values()): logger.error(f"Invalid model specified: {MODEL}") raise ValueError(f"Unsupported model: {MODEL}") UNICHAT_API_KEY = os.getenv("UNICHAT_API_KEY") if not UNICHAT_API_KEY: logger.error("UNICHAT_API_KEY environment variable not found") raise ValueError("UNICHAT_API_KEY environment variable required") chat_api = unichat.UnifiedChatApi(api_key=UNICHAT_API_KEY) def validate_messages(messages): logger.debug(f"Validating messages: {len(messages)} messages received") if len(messages) != 2: logger.error(f"Invalid number of messages: {len(messages)}") raise ValueError("Exactly two messages are required: one system message and one user message") if messages[0]["role"] != "system": logger.error("First message has incorrect role") raise ValueError("First message must have role 'system'") if messages[1]["role"] != "user": logger.error("Second message has incorrect role") raise ValueError("Second message must have role 'user'") def format_response(response: str) -> types.TextContent: logger.debug("Formatting response") try: formatted = {"type": "text", "text": response.strip()} logger.debug("Response formatted successfully") return formatted except Exception as e: logger.error(f"Error formatting response: {str(e)}") return {"type": "text", "text": f"Error formatting response: {str(e)}"} PROMPTS = { "code_review": types.Prompt( name="code_review", description="Review code for best practices, potential issues, and improvements", arguments=[ types.PromptArgument( name="code", description="The code to review", required=True ) ] ), "document_code": types.Prompt( name="document_code", description="Generate documentation for code including docstrings and comments", arguments=[ types.PromptArgument( name="code", description="The code to document", required=True ) ] ), "explain_code": types.Prompt( name="explain_code", description="Explain how a piece of code works in detail", arguments=[ types.PromptArgument( name="code", description="The code to explain", required=True ) ] ), "code_rework": types.Prompt( name="code_rework", description="Apply requested changes to the provided code", arguments=[ types.PromptArgument( name="changes", description="The changes to apply", required=False ), types.PromptArgument( name="code", description="The code to rework", required=True ) ] ) } @server.set_logging_level() async def set_logging_level(level: LoggingLevel) -> types.EmptyResult: logger.setLevel(level.upper()) await server.request_context.session.send_log_message( level="info", data=f"Log level set to {level}", logger="unichat-mcp-server" ) return types.EmptyResult() @server.list_prompts() async def handle_list_prompts() -> list[types.Prompt]: return list(PROMPTS.values()) @server.get_prompt() async def handle_get_prompt(name: str, arguments: dict[str, str] | None) -> types.GetPromptResult: prompt_templates = { "code_review": """You are a senior software engineer conducting a thorough code review. Review the following code for: - Best practices - Potential bugs - Performance issues - Security concerns - Code style and readability Code to review: {code} """, "document_code": """You are a technical documentation expert. Generate comprehensive documentation for the following code. Include: - Overview - Function/class documentation - Parameter descriptions - Return value descriptions - Usage examples Code to document: {code} """, "explain_code": """You are a programming instructor explaining code to a beginner level programmer. Explain how the following code works: {code} Break down: - Overall purpose - Key components - How it works step by step - Any important concepts used """, "code_rework": """You are a software architect specializing in code optimization and modernization. With a foucs on: - Modernizing syntax and approaches - Improving structure and organization - Enhancing maintainability - Optimizing performance - Applying current best practices Do: {changes} Code to transform: {code} """ } if name not in prompt_templates: logger.error(f"Prompt not found: {name}") raise ValueError(f"Unknown prompt: {name}") if not arguments or "code" not in arguments: logger.error("Missing required code argument") raise ValueError("Missing required argument: code") logger.debug("Formatting prompt template") # Format the template with provided arguments system_content = prompt_templates[name].format(**arguments) try: response = chat_api.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": system_content}, {"role": "user", "content": "Please provide your analysis."} ], stream=False ) response = format_response(response.choices[0].message.content) return types.GetPromptResult( description=f"Requested code manipulation", messages=[ types.PromptMessage( role="user", content=response, ) ], ) except Exception as e: logger.error(f"Error getting prompt completion: {str(e)}") raise Exception(f"An error occurred: {e}") @server.list_tools() async def handle_list_tools() -> list[types.Tool]: return [ types.Tool( name="unichat", description="""Chat with an assistant. Example tool use message: Ask the unichat to review and evaluate your proposal. """, inputSchema={ "type": "object", "properties": { "messages": { "type": "array", "items": { "type": "object", "properties": { "role": { "type": "string", "description": "The role of the message sender. Must be either 'system' or 'user'", "enum": ["system", "user"] }, "content": { "type": "string", "description": "The content of the message. For system messages, this should define the context or task. For user messages, this should contain the specific query." }, }, "required": ["role", "content"], }, "minItems": 2, "maxItems": 2, "description": "Array of exactly two messages: first a system message defining the task, then a user message with the specific query" }, }, "required": ["messages"], }, ), ] @server.call_tool() async def handle_call_tool(name: str, arguments: dict | None) -> list[types.TextContent]: if name != "unichat": logger.error(f"Unknown tool requested: {name}") raise ValueError(f"Unknown tool: {name}") try: logger.debug("Validating messages") validate_messages(arguments.get("messages", [])) response = chat_api.chat.completions.create( model=MODEL, messages=arguments["messages"], stream=False ) response = format_response(response.choices[0].message.content) return [response] except Exception as e: logger.error(f"Error calling tool: {str(e)}") raise Exception(f"An error occurred: {e}") async def main(): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await server.run( read_stream, write_stream, InitializationOptions( server_name="unichat-mcp-server", server_version="0.2.18", capabilities=server.get_capabilities( notification_options=NotificationOptions(), experimental_capabilities={}, ), ), ) if __name__ == "__main__": asyncio.run(main())

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/amidabuddha/unichat-mcp-server'

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