Lodestar MCP Server
by kanlanc
from mcp.server.fastmcp import FastMCP
# from mcp.common.messages import ContextRequest, ContextResponse # Corrected import
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field, validator
from datetime import datetime
from uuid import UUID, uuid4
# class ContextMetadata(BaseModel):
# """Metadata for MCP server responses.
# THIS IS NOT FINALIZED!!
# Attributes:
# code_snippets (List[str]): List of relevant code snippets
# doc_links (List[str]): List of related documentation links
# timestamp (datetime): When the response was generated
# additional_info (Dict[str, Any]): Any additional metadata
# """
# code_snippets: List[str] = Field(default_factory=list)
# doc_links: List[str] = Field(default_factory=list)
# timestamp: datetime = Field(default_factory=datetime.utcnow)
# additional_info: Dict[str, Any] = Field(default_factory=dict)
class ContextResponse(BaseModel):
"""Response model for MCP server.
The response includes the main text content, a unique identifier,
and structured metadata about the response.
"""
text: str = Field(..., description="Main response text or answer")
error: str = Field(default="", description="Error message")
context_id: UUID = Field(default_factory=uuid4, description="Unique response identifier")
# metadata: ContextMetadata = Field(..., description="Structured response metadata")
class ContextRequest(BaseModel):
"""Container for MCP server requests.
Attributes:
query (str): The query text from the user
api_key (str): Authentication key for the request
project_id (str): Identifier for the target project
"""
query: str
api_key: str
project_id: str
# Create an MCP server
mcp = FastMCP("DocQueryServer")
# --- Placeholder functions for authentication, index loading, RAG, logging, tracing ---
def authenticate_api_key(api_key):
"""Authenticate the provided API key.
Args:
api_key (str): The API key to validate
Returns:
bool: True if authentication successful, False otherwise
"""
return True
# return api_key == "valid-api-key" # Example: hardcoded valid key
def load_project_index(project_id):
"""Load the document index for a specific project.
Args:
project_id (str): Unique identifier for the project
Returns:
dict: Project index data containing:
- index_type (str): Type of the index
- project_id (str): Project identifier
"""
return {"index_type": "placeholder", "project_id": project_id} # Placeholder index
def run_rag_query(index, query_text):
"""Execute a RAG (Retrieval-Augmented Generation) query against the project index.
Args:
index (dict): The project index to query against
query_text (str): The user's query text
Returns:
dict: Query results containing:
- answer_text (str): Generated answer text
- code_snippets (list): Relevant code snippets
- doc_links (list): Related documentation links
"""
return {
"answer_text": f"RAG answer for: '{query_text}' (placeholder)",
"code_snippets": ["#Placeholder code from RAG"],
"doc_links": ["https://placeholder-doc-link-from-rag.com"]
} # Placeholder RAG result
def log_error(error_data):
"""Log error information for monitoring and debugging.
Args:
error_data (dict): Error information including:
- api_key (str): API key from the request
- project_id (str): Project identifier
- query_text (str): Original query text
- error_type (str): Type of error
- error_message (str): Error description
Returns:
dict: Logging status containing:
- status (str): Status of the logging operation
- data (dict): The logged error data
"""
return {"status": "error_logged", "data": error_data}
def trace_answer(trace_data):
"""Record answer tracing information for analytics and improvement.
Args:
trace_data (dict): Tracing information including:
- api_key (str): API key from the request
- project_id (str): Project identifier
- query_text (str): Original query text
- context_response (dict): The generated response
Returns:
dict: Tracing status containing:
- status (str): Status of the tracing operation
- data (dict): The traced answer data
"""
return {"status": "answer_traced", "data": trace_data}
# --- End Placeholders ---
@mcp.resource("echo://{message}")
def echo_resource(message: str) -> str:
"""Echo a message as a resource"""
return f"Resource echo: {message}"
@mcp.tool()
def doc_query(query: str, api_key: str, project_id: str)-> Dict[str, Any]:
"""Query project documentation.
Args:
query (str): The query text from the user
api_key (str): Authentication key for the request
project_id (str): Identifier for the target project
Returns:
ContextResponse: The generated response
"""
try:
# 1. Authentication
if not authenticate_api_key(api_key):
return ContextResponse(
text="Authentication failed.",
error="authentication_failed",
# metadata=ContextMetadata(additional_info={"error": "authentication_failed"})
).model_dump()
# 2. Project Context Loading
project_index = load_project_index(project_id)
# 3. RAG Query Processing
rag_result = run_rag_query(project_index, query)
# 4. Create ContextResponse from RAG result
response_metadata = {
"code_snippets": rag_result.get("code_snippets", []),
"doc_links": rag_result.get("doc_links", []),
}
response = ContextResponse(
text=rag_result.get("answer_text", "No answer from RAG.")
)
# 5. Answer Tracing (for successful responses)
trace_data = {
"api_key": api_key,
"project_id": project_id,
"query_text": query,
"context_response": response.model_dump() # Convert response to dictionary for logging
}
trace_answer(trace_data)
return response
except Exception as e:
# 6. Error Logging (for any exceptions)
error_data = {
"api_key": api_key,
"project_id": project_id,
"query_text": query,
"error_type": type(e).__name__,
"error_message": str(e),
# "traceback": ... (include traceback if needed)
}
log_error(error_data)
return ContextResponse(
text=str(e),
error="processing_failed"
# metadata=ContextMetadata(additional_info={
# "error": "processing_failed",
# "error_type": type(e).__name__,
# "error_message": str(e)
# })
).model_dump()
# return {
# "text": "Error processing your query.",
# "context_id": "error-processing",
# "metadata": {}
# }
if __name__ == "__main__":
print("Starting MCP server...")
mcp.run()