Skip to content

Commit a09378c

Browse files
rakduttarakdutta1crivetimihai
authored
Replace Raw Errors with Friendly Messages in main.py (#677)
* issue 372 Signed-off-by: RAKHI DUTTA <[email protected]> * test case Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * test cases Signed-off-by: RAKHI DUTTA <[email protected]> * Rebase Signed-off-by: Mihai Criveti <[email protected]> --------- Signed-off-by: RAKHI DUTTA <[email protected]> Signed-off-by: Mihai Criveti <[email protected]> Co-authored-by: RAKHI DUTTA <[email protected]> Co-authored-by: Mihai Criveti <[email protected]>
1 parent 7334633 commit a09378c

File tree

7 files changed

+614
-70
lines changed

7 files changed

+614
-70
lines changed

mcpgateway/admin.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1960,7 +1960,6 @@ async def admin_add_tool(
19601960
except ToolError as ex:
19611961
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
19621962
except ValidationError as ex: # This block should catch ValidationError
1963-
19641963
logger.error(f"ValidationError in admin_add_tool: {str(ex)}")
19651964
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=422)
19661965
except Exception as ex:

mcpgateway/main.py

Lines changed: 111 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
ToolUpdate,
100100
)
101101
from mcpgateway.services.completion_service import CompletionService
102-
from mcpgateway.services.gateway_service import GatewayConnectionError, GatewayNameConflictError, GatewayService
102+
from mcpgateway.services.gateway_service import GatewayConnectionError, GatewayNameConflictError, GatewayNotFoundError, GatewayService
103103
from mcpgateway.services.logging_service import LoggingService
104104
from mcpgateway.services.prompt_service import (
105105
PromptError,
@@ -124,6 +124,7 @@
124124
from mcpgateway.services.tool_service import (
125125
ToolError,
126126
ToolNameConflictError,
127+
ToolNotFoundError,
127128
ToolService,
128129
)
129130
from mcpgateway.transports.sse_transport import SSETransport
@@ -1247,15 +1248,25 @@ async def create_tool(tool: ToolCreate, db: Session = Depends(get_db), user: str
12471248
try:
12481249
logger.debug(f"User {user} is creating a new tool")
12491250
return await tool_service.register_tool(db, tool)
1250-
except ToolNameConflictError as e:
1251-
if not e.enabled and e.tool_id:
1252-
raise HTTPException(
1253-
status_code=status.HTTP_409_CONFLICT,
1254-
detail=f"Tool name already exists but is inactive. Consider activating it with ID: {e.tool_id}",
1255-
)
1256-
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
1257-
except ToolError as e:
1258-
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
1251+
except Exception as ex:
1252+
logger.error(f"Error while creating tool: {ex}")
1253+
if isinstance(ex, ToolNameConflictError):
1254+
if not ex.enabled and ex.tool_id:
1255+
raise HTTPException(
1256+
status_code=status.HTTP_409_CONFLICT,
1257+
detail=f"Tool name already exists but is inactive. Consider activating it with ID: {ex.tool_id}",
1258+
)
1259+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(ex))
1260+
if isinstance(ex, (ValidationError, ValueError)):
1261+
logger.error(f"Validation error while creating tool: {ex}")
1262+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=ErrorFormatter.format_validation_error(ex))
1263+
if isinstance(ex, IntegrityError):
1264+
logger.error(f"Integrity error while creating tool: {ex}")
1265+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=ErrorFormatter.format_database_error(ex))
1266+
if isinstance(ex, ToolError):
1267+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(ex))
1268+
logger.error(f"Unexpected error while creating tool: {ex}")
1269+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while creating the tool")
12591270

12601271

12611272
@tool_router.get("/{tool_id}", response_model=Union[ToolRead, Dict])
@@ -1319,8 +1330,19 @@ async def update_tool(
13191330
try:
13201331
logger.debug(f"User {user} is updating tool with ID {tool_id}")
13211332
return await tool_service.update_tool(db, tool_id, tool)
1322-
except Exception as e:
1323-
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
1333+
except Exception as ex:
1334+
if isinstance(ex, ToolNotFoundError):
1335+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(ex))
1336+
if isinstance(ex, ValidationError):
1337+
logger.error(f"Validation error while creating tool: {ex}")
1338+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=ErrorFormatter.format_validation_error(ex))
1339+
if isinstance(ex, IntegrityError):
1340+
logger.error(f"Integrity error while creating tool: {ex}")
1341+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=ErrorFormatter.format_database_error(ex))
1342+
if isinstance(ex, ToolError):
1343+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(ex))
1344+
logger.error(f"Unexpected error while creating tool: {ex}")
1345+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while creating the tool")
13241346

13251347

13261348
@tool_router.delete("/{tool_id}")
@@ -1716,10 +1738,24 @@ async def create_prompt(
17161738
logger.debug(f"User: {user} requested to create prompt: {prompt}")
17171739
try:
17181740
return await prompt_service.register_prompt(db, prompt)
1719-
except PromptNameConflictError as e:
1720-
raise HTTPException(status_code=409, detail=str(e))
1721-
except PromptError as e:
1722-
raise HTTPException(status_code=400, detail=str(e))
1741+
except Exception as e:
1742+
if isinstance(e, PromptNameConflictError):
1743+
# If the prompt name already exists, return a 409 Conflict error
1744+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
1745+
if isinstance(e, PromptError):
1746+
# If there is a general prompt error, return a 400 Bad Request error
1747+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
1748+
if isinstance(e, ValidationError):
1749+
# If there is a validation error, return a 422 Unprocessable Entity error
1750+
logger.error(f"Validation error while creating prompt: {e}")
1751+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=ErrorFormatter.format_validation_error(e))
1752+
if isinstance(e, IntegrityError):
1753+
# If there is an integrity error, return a 409 Conflict error
1754+
logger.error(f"Integrity error while creating prompt: {e}")
1755+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=ErrorFormatter.format_database_error(e))
1756+
# For any other unexpected errors, return a 500 Internal Server Error
1757+
logger.error(f"Unexpected error while creating prompt: {e}")
1758+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while creating the prompt")
17231759

17241760

17251761
@prompt_router.post("/{name}")
@@ -1801,13 +1837,28 @@ async def update_prompt(
18011837
HTTPException: * **409 Conflict** - a different prompt with the same *name* already exists and is still active.
18021838
* **400 Bad Request** - validation or persistence error raised by :pyclass:`~mcpgateway.services.prompt_service.PromptService`.
18031839
"""
1840+
logger.info(f"User: {user} requested to update prompt: {name} with data={prompt}")
18041841
logger.debug(f"User: {user} requested to update prompt: {name} with data={prompt}")
18051842
try:
18061843
return await prompt_service.update_prompt(db, name, prompt)
1807-
except PromptNameConflictError as e:
1808-
raise HTTPException(status_code=409, detail=str(e))
1809-
except PromptError as e:
1810-
raise HTTPException(status_code=400, detail=str(e))
1844+
except Exception as e:
1845+
if isinstance(e, PromptNotFoundError):
1846+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
1847+
if isinstance(e, ValidationError):
1848+
logger.error(f"Validation error while updating prompt: {e}")
1849+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=ErrorFormatter.format_validation_error(e))
1850+
if isinstance(e, IntegrityError):
1851+
logger.error(f"Integrity error while updating prompt: {e}")
1852+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=ErrorFormatter.format_database_error(e))
1853+
if isinstance(e, PromptNameConflictError):
1854+
# If the prompt name already exists, return a 409 Conflict error
1855+
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
1856+
if isinstance(e, PromptError):
1857+
# If there is a general prompt error, return a 400 Bad Request error
1858+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
1859+
# For any other unexpected errors, return a 500 Internal Server Error
1860+
logger.error(f"Unexpected error while updating prompt: {e}")
1861+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while updating the prompt")
18111862

18121863

18131864
@prompt_router.delete("/{name}")
@@ -1822,15 +1873,26 @@ async def delete_prompt(name: str, db: Session = Depends(get_db), user: str = De
18221873
18231874
Returns:
18241875
Status message.
1876+
1877+
Raises:
1878+
HTTPException: If the prompt is not found, a prompt error occurs, or an unexpected error occurs during deletion.
18251879
"""
18261880
logger.debug(f"User: {user} requested deletion of prompt {name}")
18271881
try:
18281882
await prompt_service.delete_prompt(db, name)
18291883
return {"status": "success", "message": f"Prompt {name} deleted"}
1830-
except PromptNotFoundError as e:
1831-
return {"status": "error", "message": str(e)}
1832-
except PromptError as e:
1833-
return {"status": "error", "message": str(e)}
1884+
except Exception as e:
1885+
if isinstance(e, PromptNotFoundError):
1886+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
1887+
if isinstance(e, PromptError):
1888+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
1889+
logger.error(f"Unexpected error while deleting prompt {name}: {e}")
1890+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while deleting the prompt")
1891+
1892+
# except PromptNotFoundError as e:
1893+
# return {"status": "error", "message": str(e)}
1894+
# except PromptError as e:
1895+
# return {"status": "error", "message": str(e)}
18341896

18351897

18361898
################
@@ -1919,18 +1981,18 @@ async def register_gateway(
19191981
return await gateway_service.register_gateway(db, gateway)
19201982
except Exception as ex:
19211983
if isinstance(ex, GatewayConnectionError):
1922-
return JSONResponse(content={"message": "Unable to connect to gateway"}, status_code=502)
1984+
return JSONResponse(content={"message": "Unable to connect to gateway"}, status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
19231985
if isinstance(ex, ValueError):
1924-
return JSONResponse(content={"message": "Unable to process input"}, status_code=400)
1986+
return JSONResponse(content={"message": "Unable to process input"}, status_code=status.HTTP_400_BAD_REQUEST)
19251987
if isinstance(ex, GatewayNameConflictError):
1926-
return JSONResponse(content={"message": "Gateway name already exists"}, status_code=400)
1988+
return JSONResponse(content={"message": "Gateway name already exists"}, status_code=status.HTTP_409_CONFLICT)
19271989
if isinstance(ex, RuntimeError):
1928-
return JSONResponse(content={"message": "Error during execution"}, status_code=500)
1990+
return JSONResponse(content={"message": "Error during execution"}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
19291991
if isinstance(ex, ValidationError):
1930-
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=422)
1992+
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
19311993
if isinstance(ex, IntegrityError):
1932-
return JSONResponse(status_code=409, content=ErrorFormatter.format_database_error(ex))
1933-
return JSONResponse(content={"message": "Unexpected error"}, status_code=500)
1994+
return JSONResponse(status_code=status.HTTP_409_CONFLICT, content=ErrorFormatter.format_database_error(ex))
1995+
return JSONResponse(content={"message": "Unexpected error"}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
19341996

19351997

19361998
@gateway_router.get("/{gateway_id}", response_model=GatewayRead)
@@ -1970,7 +2032,24 @@ async def update_gateway(
19702032
Updated gateway.
19712033
"""
19722034
logger.debug(f"User '{user}' requested update on gateway {gateway_id} with data={gateway}")
1973-
return await gateway_service.update_gateway(db, gateway_id, gateway)
2035+
try:
2036+
return await gateway_service.update_gateway(db, gateway_id, gateway)
2037+
except Exception as ex:
2038+
if isinstance(ex, GatewayNotFoundError):
2039+
return JSONResponse(content={"message": "Gateway not found"}, status_code=status.HTTP_404_NOT_FOUND)
2040+
if isinstance(ex, GatewayConnectionError):
2041+
return JSONResponse(content={"message": "Unable to connect to gateway"}, status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
2042+
if isinstance(ex, ValueError):
2043+
return JSONResponse(content={"message": "Unable to process input"}, status_code=status.HTTP_400_BAD_REQUEST)
2044+
if isinstance(ex, GatewayNameConflictError):
2045+
return JSONResponse(content={"message": "Gateway name already exists"}, status_code=status.HTTP_409_CONFLICT)
2046+
if isinstance(ex, RuntimeError):
2047+
return JSONResponse(content={"message": "Error during execution"}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
2048+
if isinstance(ex, ValidationError):
2049+
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)
2050+
if isinstance(ex, IntegrityError):
2051+
return JSONResponse(status_code=status.HTTP_409_CONFLICT, content=ErrorFormatter.format_database_error(ex))
2052+
return JSONResponse(content={"message": "Unexpected error"}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
19742053

19752054

19762055
@gateway_router.delete("/{gateway_id}")

mcpgateway/services/gateway_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,9 @@ async def update_gateway(self, db: Session, gateway_id: str, gateway_update: Gat
634634
except GatewayNameConflictError as ge:
635635
logger.error(f"GatewayNameConflictError in group: {ge}")
636636
raise ge
637+
except GatewayNotFoundError as gnfe:
638+
logger.error(f"GatewayNotFoundError: {gnfe}")
639+
raise gnfe
637640
except IntegrityError as ie:
638641
logger.error(f"IntegrityErrors in group: {ie}")
639642
raise ie

mcpgateway/services/prompt_service.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ async def update_prompt(self, db: Session, name: str, prompt_update: PromptUpdat
527527
prompt = db.execute(select(DbPrompt).where(DbPrompt.name == name).where(DbPrompt.is_active)).scalar_one_or_none()
528528
if not prompt:
529529
inactive_prompt = db.execute(select(DbPrompt).where(DbPrompt.name == name).where(not_(DbPrompt.is_active))).scalar_one_or_none()
530+
530531
if inactive_prompt:
531532
raise PromptNotFoundError(f"Prompt '{name}' exists but is inactive")
532533

@@ -568,6 +569,10 @@ async def update_prompt(self, db: Session, name: str, prompt_update: PromptUpdat
568569
db.rollback()
569570
logger.error(f"IntegrityErrors in group: {ie}")
570571
raise ie
572+
except PromptNotFoundError as e:
573+
db.rollback()
574+
logger.error(f"Prompt not found: {e}")
575+
raise e
571576
except Exception as e:
572577
db.rollback()
573578
raise PromptError(f"Failed to update prompt: {str(e)}")

mcpgateway/services/tool_service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ async def update_tool(self, db: Session, tool_id: str, tool_update: ToolUpdate)
875875
except IntegrityError as ie:
876876
logger.error(f"IntegrityError during tool update: {ie}")
877877
raise ie
878+
except ToolNotFoundError as tnfe:
879+
logger.error(f"Tool not found during update: {tnfe}")
880+
raise tnfe
878881
except Exception as ex:
879882
db.rollback()
880883
raise ToolError(f"Failed to update tool: {str(ex)}")

0 commit comments

Comments
 (0)