Spaces:
Paused
Paused
Commit
·
42f1822
1
Parent(s):
6934b27
feat: enhance configuration and API key handling in MCP server
Browse files- .env.example +10 -1
- README.md +10 -0
- main.py +57 -13
- utils/config.py +4 -0
.env.example
CHANGED
|
@@ -5,5 +5,14 @@ DS_API_MULTIMODULE_ENDPOINT=/mcp_multi_module_bug_localization
|
|
| 5 |
DS_API_SINGLEMODULE_ENDPOINT=/mcp_single_module_bug_localization
|
| 6 |
DS_API_SEARCHSPACE_ENDPOINT=/mcp_search_space_routing
|
| 7 |
|
|
|
|
|
|
|
|
|
|
| 8 |
# Request timeout in seconds (for requests between MCP Server and Defect Solver API)
|
| 9 |
-
TIMEOUT=120
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
DS_API_SINGLEMODULE_ENDPOINT=/mcp_single_module_bug_localization
|
| 6 |
DS_API_SEARCHSPACE_ENDPOINT=/mcp_search_space_routing
|
| 7 |
|
| 8 |
+
# Hugging Face Access Token for when the API is hosted on Hugging Face Spaces
|
| 9 |
+
HF_ACCESS_TOKEN=your_hf_access_token_here
|
| 10 |
+
|
| 11 |
# Request timeout in seconds (for requests between MCP Server and Defect Solver API)
|
| 12 |
+
TIMEOUT=120
|
| 13 |
+
|
| 14 |
+
# MCP Server Configuration
|
| 15 |
+
TRANSPORT_MODE=streamable-http
|
| 16 |
+
HOST=0.0.0.0
|
| 17 |
+
PORT=8000
|
| 18 |
+
LOG_LEVEL=INFO
|
README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Defect Solver MCP Server
|
| 2 |
|
| 3 |
This project implements a Model Context Protocol (MCP) server for defect solver bug localization using FastMCP. It forwards bug description data (ticket) to a remote endpoint for prediction and analysis.
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Defect Solver API
|
| 3 |
+
emoji: 🛠️
|
| 4 |
+
colorFrom: orange
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
short_description: Defect Solver - MCP Server
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
# Defect Solver MCP Server
|
| 12 |
|
| 13 |
This project implements a Model Context Protocol (MCP) server for defect solver bug localization using FastMCP. It forwards bug description data (ticket) to a remote endpoint for prediction and analysis.
|
main.py
CHANGED
|
@@ -9,6 +9,9 @@ import uuid
|
|
| 9 |
from utils.config import API_CONFIG
|
| 10 |
from utils.model import MultiModuleRequest, SingleModuleRequest, SearchSpaceRoutingRequest
|
| 11 |
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
@dataclass
|
| 14 |
class DSContext:
|
|
@@ -56,7 +59,21 @@ async def multi_module_bug_localization(request: MultiModuleRequest, ctx: Contex
|
|
| 56 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_multimodule_endpoint"]
|
| 57 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 58 |
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
try:
|
| 62 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
@@ -90,7 +107,7 @@ async def multi_module_bug_localization(request: MultiModuleRequest, ctx: Contex
|
|
| 90 |
logger.error(f"Request error while calling multimodule bug localization endpoint: {e}")
|
| 91 |
return {"error": "Request error", "details": str(e)}
|
| 92 |
except Exception as e:
|
| 93 |
-
logger.
|
| 94 |
return {"error": "Unexpected error", "details": str(e)}
|
| 95 |
|
| 96 |
|
|
@@ -118,7 +135,21 @@ async def single_module_bug_localization(request: SingleModuleRequest, ctx: Cont
|
|
| 118 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_singlemodule_endpoint"]
|
| 119 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 120 |
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
try:
|
| 124 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
@@ -151,7 +182,7 @@ async def single_module_bug_localization(request: SingleModuleRequest, ctx: Cont
|
|
| 151 |
logger.error(f"Request error while calling endpoint: {e}")
|
| 152 |
return {"error": "Request error", "details": str(e)}
|
| 153 |
except Exception as e:
|
| 154 |
-
logger.
|
| 155 |
return {"error": "Unexpected error", "details": str(e)}
|
| 156 |
|
| 157 |
|
|
@@ -179,7 +210,21 @@ async def search_space_routing(request: SearchSpaceRoutingRequest, ctx: Context)
|
|
| 179 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_searchspace_endpoint"]
|
| 180 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 181 |
|
| 182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
| 184 |
try:
|
| 185 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
@@ -211,7 +256,7 @@ async def search_space_routing(request: SearchSpaceRoutingRequest, ctx: Context)
|
|
| 211 |
logger.error(f"Request error while calling endpoint: {e}")
|
| 212 |
return {"error": "Request error", "details": str(e)}
|
| 213 |
except Exception as e:
|
| 214 |
-
logger.
|
| 215 |
return {"error": "Unexpected error", "details": str(e)}
|
| 216 |
|
| 217 |
|
|
@@ -340,11 +385,10 @@ Return a 2-3x more detailed/enhanced bug report with precise technical terms for
|
|
| 340 |
"""
|
| 341 |
|
| 342 |
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
if __name__ == "__main__":
|
| 350 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
from utils.config import API_CONFIG
|
| 10 |
from utils.model import MultiModuleRequest, SingleModuleRequest, SearchSpaceRoutingRequest
|
| 11 |
|
| 12 |
+
from fastmcp.server.dependencies import get_http_request
|
| 13 |
+
from starlette.requests import Request
|
| 14 |
+
|
| 15 |
|
| 16 |
@dataclass
|
| 17 |
class DSContext:
|
|
|
|
| 59 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_multimodule_endpoint"]
|
| 60 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 61 |
|
| 62 |
+
# Get per-user API key from MCP Context
|
| 63 |
+
user_request: Request = get_http_request()
|
| 64 |
+
defect_solver_api_key = user_request.headers.get("DS-API-Key", None)
|
| 65 |
+
logger.info(
|
| 66 |
+
"Using API key: {}".format(defect_solver_api_key)
|
| 67 |
+
if defect_solver_api_key
|
| 68 |
+
else "No Defect Solver API key provided"
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
headers = {
|
| 72 |
+
"Content-Type": "application/json",
|
| 73 |
+
"Authorization": f"Bearer {hf_token}" if hf_token else None,
|
| 74 |
+
"DS-API-Key": defect_solver_api_key,
|
| 75 |
+
}
|
| 76 |
+
headers = {k: v for k, v in headers.items() if v is not None}
|
| 77 |
|
| 78 |
try:
|
| 79 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
|
|
| 107 |
logger.error(f"Request error while calling multimodule bug localization endpoint: {e}")
|
| 108 |
return {"error": "Request error", "details": str(e)}
|
| 109 |
except Exception as e:
|
| 110 |
+
logger.error("Unexpected error during multimodule bug localization request")
|
| 111 |
return {"error": "Unexpected error", "details": str(e)}
|
| 112 |
|
| 113 |
|
|
|
|
| 135 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_singlemodule_endpoint"]
|
| 136 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 137 |
|
| 138 |
+
# Get per-user API key from MCP Context
|
| 139 |
+
user_request: Request = get_http_request()
|
| 140 |
+
defect_solver_api_key = user_request.headers.get("DS-API-Key", None)
|
| 141 |
+
logger.info(
|
| 142 |
+
"Using API key: {}".format(defect_solver_api_key)
|
| 143 |
+
if defect_solver_api_key
|
| 144 |
+
else "No Defect Solver API key provided"
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
headers = {
|
| 148 |
+
"Content-Type": "application/json",
|
| 149 |
+
"Authorization": f"Bearer {hf_token}" if hf_token else None,
|
| 150 |
+
"DS-API-Key": defect_solver_api_key,
|
| 151 |
+
}
|
| 152 |
+
headers = {k: v for k, v in headers.items() if v is not None}
|
| 153 |
|
| 154 |
try:
|
| 155 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
|
|
| 182 |
logger.error(f"Request error while calling endpoint: {e}")
|
| 183 |
return {"error": "Request error", "details": str(e)}
|
| 184 |
except Exception as e:
|
| 185 |
+
logger.error("Unexpected error during single-module bug localization")
|
| 186 |
return {"error": "Unexpected error", "details": str(e)}
|
| 187 |
|
| 188 |
|
|
|
|
| 210 |
api_url = API_CONFIG["api_base_url"] + API_CONFIG["api_searchspace_endpoint"]
|
| 211 |
hf_token = API_CONFIG.get("hf_access_token", None)
|
| 212 |
|
| 213 |
+
# Get per-user API key from MCP Context
|
| 214 |
+
user_request: Request = get_http_request()
|
| 215 |
+
defect_solver_api_key = user_request.headers.get("DS-API-Key", None)
|
| 216 |
+
logger.info(
|
| 217 |
+
"Using API key: {}".format(defect_solver_api_key)
|
| 218 |
+
if defect_solver_api_key
|
| 219 |
+
else "No Defect Solver API key provided"
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
headers = {
|
| 223 |
+
"Content-Type": "application/json",
|
| 224 |
+
"Authorization": f"Bearer {hf_token}" if hf_token else None,
|
| 225 |
+
"DS-API-Key": defect_solver_api_key,
|
| 226 |
+
}
|
| 227 |
+
headers = {k: v for k, v in headers.items() if v is not None}
|
| 228 |
|
| 229 |
try:
|
| 230 |
# Generate a UUID for issue_key even if provided (overwrite)
|
|
|
|
| 256 |
logger.error(f"Request error while calling endpoint: {e}")
|
| 257 |
return {"error": "Request error", "details": str(e)}
|
| 258 |
except Exception as e:
|
| 259 |
+
logger.error("Unexpected error during search space routing")
|
| 260 |
return {"error": "Unexpected error", "details": str(e)}
|
| 261 |
|
| 262 |
|
|
|
|
| 385 |
"""
|
| 386 |
|
| 387 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
if __name__ == "__main__":
|
| 389 |
+
TRANSPORT_MODE = API_CONFIG["transport_mode"]
|
| 390 |
+
HOST = API_CONFIG["host"]
|
| 391 |
+
PORT = API_CONFIG["port"]
|
| 392 |
+
LOG_LEVEL = API_CONFIG["log_level"]
|
| 393 |
+
|
| 394 |
+
mcp.run(transport=TRANSPORT_MODE, host=HOST, port=PORT, log_level=LOG_LEVEL)
|
utils/config.py
CHANGED
|
@@ -11,4 +11,8 @@ API_CONFIG = {
|
|
| 11 |
"api_searchspace_endpoint": os.getenv("DS_API_SEARCHSPACE_ENDPOINT", "/mcp_search_space_routing"),
|
| 12 |
"hf_access_token": os.getenv("HF_ACCESS_TOKEN", None),
|
| 13 |
"timeout": os.getenv("TIMEOUT", 120),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
|
|
|
| 11 |
"api_searchspace_endpoint": os.getenv("DS_API_SEARCHSPACE_ENDPOINT", "/mcp_search_space_routing"),
|
| 12 |
"hf_access_token": os.getenv("HF_ACCESS_TOKEN", None),
|
| 13 |
"timeout": os.getenv("TIMEOUT", 120),
|
| 14 |
+
"transport_mode": os.getenv("TRANSPORT_MODE", "streamable-http"),
|
| 15 |
+
"host": os.getenv("HOST", "0.0.0.0"),
|
| 16 |
+
"port": os.getenv("PORT", 8000),
|
| 17 |
+
"log_level": os.getenv("LOG_LEVEL", "INFO"),
|
| 18 |
}
|