diff --git a/deploy/docker/.env b/deploy/docker/.env index 990434a7..1952218d 100644 --- a/deploy/docker/.env +++ b/deploy/docker/.env @@ -5,6 +5,6 @@ CHATBOT_SERVER_PORT=5002 ENABLE_SHELL_INJECTION=false ENABLE_LOG4J=false LISTEN_IP="127.0.0.1" -TLS_ENABLED=true +TLS_ENABLED=false VERSION=latest LOG_LEVEL=INFO \ No newline at end of file diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml index 6a081cf9..e93d1815 100755 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -15,8 +15,8 @@ services: crapi-identity: container_name: crapi-identity image: crapi/crapi-identity:${VERSION:-latest} - #ports: - # - "${LISTEN_IP:-127.0.0.1}:8080:8080" + ports: + - "${LISTEN_IP:-127.0.0.1}:8080:8080" volumes: - ./keys:/app/keys environment: @@ -170,6 +170,8 @@ services: depends_on: mongodb: condition: service_healthy + crapi-identity: + condition: service_healthy # ports: # - "${LISTEN_IP:-127.0.0.1}:5002:5002" diff --git a/services/chatbot/Dockerfile b/services/chatbot/Dockerfile index ad987886..fa28895e 100644 --- a/services/chatbot/Dockerfile +++ b/services/chatbot/Dockerfile @@ -16,6 +16,8 @@ RUN pip install --no-cache-dir -r requirements.txt COPY src /app COPY certs /app/certs COPY retrieval /app/retrieval +RUN mkdir -p /app/resources +COPY src/resources/crapi-openapi-spec.json /app/resources/crapi-openapi-spec.json ENV PYTHONPATH="/app" COPY entrypoint.sh /app/entrypoint.sh CMD /app/entrypoint.sh diff --git a/services/chatbot/requirements.txt b/services/chatbot/requirements.txt index 149ca658..f3d0248c 100644 --- a/services/chatbot/requirements.txt +++ b/services/chatbot/requirements.txt @@ -6,7 +6,7 @@ langchain-openai==0.3.16 langchain-text-splitters==0.3.8 markdown==3.8 pymongo==4.12.1 -python-dotenv==1.0.1 +python-dotenv==1.1.0 unstructured==0.17.2 numpy==1.26.4 langchain-mcp-adapters==0.1.8 @@ -15,7 +15,7 @@ quart-cors==0.8.0 motor==3.7.1 openai==1.77.0 langgraph==0.5.1 -python-dotenv==1.0.1 faiss-cpu==1.11.0 psycopg2-binary -uvicorn==0.35.0 \ No newline at end of file +uvicorn==0.35.0 +fastmcp==2.10.2 \ No newline at end of file diff --git a/services/chatbot/src/chatbot/langgraph_agent.py b/services/chatbot/src/chatbot/langgraph_agent.py index 6f08f8a4..678ba9a8 100644 --- a/services/chatbot/src/chatbot/langgraph_agent.py +++ b/services/chatbot/src/chatbot/langgraph_agent.py @@ -48,7 +48,7 @@ async def get_retriever_tool(api_key): return retriever_tool -async def build_langgraph_agent(api_key, session_id): +async def build_langgraph_agent(api_key): system_prompt = textwrap.dedent( """ You are crAPI Assistant — an expert agent that helps users explore and test the Completely Ridiculous API (crAPI), a vulnerable-by-design application for learning and evaluating modern API security issues. @@ -93,12 +93,12 @@ async def build_langgraph_agent(api_key, session_id): tools = mcp_tools + db_tools # retriever_tool = await get_retriever_tool(api_key) # tools.append(retriever_tool) - agent_node = create_react_agent(llm, tools=tools, prompt=system_prompt) + agent_node = create_react_agent(model=llm, tools=tools, prompt=system_prompt) return agent_node async def execute_langgraph_agent(api_key, messages, session_id=None): - agent = await build_langgraph_agent(api_key, session_id) + agent = await build_langgraph_agent(api_key) print("messages", messages) print("Session ID", session_id) response = await agent.ainvoke({"messages": messages}) diff --git a/services/chatbot/src/mcpserver/__main__.py b/services/chatbot/src/mcpserver/__main__.py index 844c453b..1e67dc86 100644 --- a/services/chatbot/src/mcpserver/__main__.py +++ b/services/chatbot/src/mcpserver/__main__.py @@ -1,7 +1,7 @@ import logging import os -from .mcp_server import app +from .server import mcp as app # Configure logging logging.basicConfig( @@ -11,11 +11,5 @@ if __name__ == "__main__": logger.info("Starting MCP server...") - app.settings.host = "0.0.0.0" - app.settings.port = 5002 - app.settings.debug = os.getenv("DEBUG", "False").lower() in ( - "true", - "1", - "yes", - ) - app.run(transport="streamable-http") + mcp_server_port = int(os.environ.get("MCP_SERVER_PORT", 5500)) + app.run(transport="streamable-http", host="0.0.0.0", port=mcp_server_port) diff --git a/services/chatbot/src/mcpserver/server.py b/services/chatbot/src/mcpserver/server.py old mode 100755 new mode 100644 index 279ebad0..d658fdf4 --- a/services/chatbot/src/mcpserver/server.py +++ b/services/chatbot/src/mcpserver/server.py @@ -1,19 +1,8 @@ -#!/usr/bin/env python3 -""" -MCP Server Implementation for OWASP crAPI API - -This MCP server exposes the API operations defined in the OpenAPI specification -as MCP tools and resources. -""" - -import argparse -import logging -import os -from typing import Any, Dict, List, Optional, Union -import asyncio - import httpx -from mcp.server.fastmcp import Context, FastMCP +from fastmcp import FastMCP, settings +import json +import os +import logging # Configure logging logging.basicConfig( @@ -22,1734 +11,63 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) -# Create MCP server -mcp = FastMCP(name="OWASP crAPI MCP Server") -mcp.settings.host = "0.0.0.0" -mcp_server_port = int(os.environ.get("MCP_SERVER_PORT", 5500)) -mcp.settings.port = mcp_server_port - -# API configuration -WEB_SERVICE = os.environ.get("WEB_SERVICE", "crapi-web:8888") +WEB_SERVICE = os.environ.get("WEB_SERVICE", "crapi-web") +IDENTITY_SERVICE = os.environ.get("IDENTITY_SERVICE", "crapi-identity:8080") TLS_ENABLED = os.environ.get("TLS_ENABLED", "false").lower() in ("true", "1", "yes") -API_URL = f"{'https' if TLS_ENABLED else 'http'}://{WEB_SERVICE}" -API_USER = os.environ.get("API_USER", "admin") +BASE_URL = f"{'https' if TLS_ENABLED else 'http'}://{WEB_SERVICE}" +BASE_IDENTITY_URL = f"{'https' if TLS_ENABLED else 'http'}://{IDENTITY_SERVICE}" + +API_USER = os.environ.get("API_USER", "admin@example.com") API_PASSWORD = os.environ.get("API_PASSWORD", "Admin!123") +API_URL = f"{'https' if TLS_ENABLED else 'http'}://{WEB_SERVICE}" API_KEY = None API_AUTH_TYPE = "ApiKey" - -async def get_api_key(): +def get_api_key(): global API_KEY if API_KEY is None: - login_body = {"username": API_USER, "password": API_PASSWORD} - apikey_url = f"{API_URL}/identity/management/user/apikey" - headers = {} - async with httpx.AsyncClient( + login_body = {"email": API_USER, "password": API_PASSWORD} + apikey_url = f"{BASE_IDENTITY_URL}/identity/management/user/apikey" + headers = { + "Content-Type": "application/json", + } + with httpx.Client( base_url=API_URL, headers=headers, ) as client: - response = await client.post(apikey_url, json=login_body) + response = client.post(apikey_url, json=login_body) response.raise_for_status() - API_KEY = response.json().get("apiKey") - logger.info(f"Chatbot API Key: {API_KEY[:5]}...") + response_json = response.json() + logger.info(f"Response: {response_json}") + API_KEY = response_json.get("apiKey") + logger.info(f"Chatbot API Key: {API_KEY}") return API_KEY return API_KEY # Async HTTP client for API calls -async def get_http_client(): +def get_http_client(): """Create and configure the HTTP client with appropriate authentication.""" - headers = {} + headers = { + "Authorization": "ApiKey " + get_api_key(), + } return httpx.AsyncClient( base_url=API_URL, headers=headers, ) +# Load your OpenAPI spec +with open("/app/resources/crapi-openapi-spec.json", "r") as f: + openapi_spec = json.load(f) -# MCP tools for API operations - - -@mcp.tool(description="Used to create an account") -async def signup(ctx: Context) -> str: - """ - Used to create an account - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/signup" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/auth/login") -async def login(ctx: Context) -> str: - """ - POST /identity/api/auth/login - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/login" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Sends an OTP to email to reset password") -async def forgot_password(ctx: Context) -> str: - """ - Sends an OTP to email to reset password - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/forget-password" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="To validate the One-Time-Password sent using `forgot password`") -async def check_otp_v3(ctx: Context) -> str: - """ - To validate the One-Time-Password sent using `forgot password` - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/v3/check-otp" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="To validate the One-Time-Password sent using `forgot password`") -async def check_otp_v2(ctx: Context) -> str: - """ - To validate the One-Time-Password sent using `forgot password` - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/v2/check-otp" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/auth/v4.0/user/login-with-token") -async def login_with_token(ctx: Context) -> str: - """ - POST /identity/api/auth/v4.0/user/login-with-token - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/v4.0/user/login-with-token" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/auth/v2.7/user/login-with-token") -async def login_with_token_v2_7(ctx: Context) -> str: - """ - POST /identity/api/auth/v2.7/user/login-with-token - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/auth/v2.7/user/login-with-token" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Reset user password using JWT token") -async def reset_password(ctx: Context) -> str: - """ - Reset user password using JWT token - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/reset-password" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Sends token to new email") -async def change_email(ctx: Context) -> str: - """ - Sends token to new email - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/change-email" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Verify token sent for changing email") -async def verify_email_token(ctx: Context) -> str: - """ - Verify token sent for changing email - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/verify-email-token" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="GET /identity/api/v2/user/dashboard") -async def get_dashboard(ctx: Context) -> str: - """ - GET /identity/api/v2/user/dashboard - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/dashboard" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/v2/user/pictures") -async def update_profile_pic(ctx: Context) -> str: - """ - POST /identity/api/v2/user/pictures - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/pictures" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/v2/user/videos") -async def upload_profile_video(ctx: Context) -> str: - """ - POST /identity/api/v2/user/videos - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/videos" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Get the video associated with the user's profile.") -async def get_profile_video(video_id: int, ctx: Context) -> str: - """ - Get the video associated with the user's profile. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/videos/{video_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if video_id is not None: - url = url.replace("{video_id}", str(video_id)) - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Update the video identified by video_id in this user's profile.") -async def update_profile_video(video_id: int, ctx: Context) -> str: - """ - Update the video identified by video_id in this user's profile. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/videos/{video_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if video_id is not None: - url = url.replace("{video_id}", str(video_id)) - - # Make the request - response = await client.put( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool( - description="Delete the video identified by video_id from this user's profile." +# Create the MCP server +mcp = FastMCP.from_openapi( + openapi_spec=openapi_spec, + client=get_http_client(), + name="My crAPI MCP Server" ) -async def delete_profile_video(video_id: int, ctx: Context) -> str: - """ - Delete the video identified by video_id from this user's profile. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/videos/{video_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if video_id is not None: - url = url.replace("{video_id}", str(video_id)) - - # Make the request - response = await client.delete( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Convert the format for the specified video.") -async def convert_profile_video(ctx: Context, video_id: int = 0) -> str: - """ - Convert the format for the specified video. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/user/videos/convert_video" - - # Extract query parameters - query_params = {} - request_body = None - - if video_id is not None: - query_params["video_id"] = video_id - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Delete profile video of other users by video_id as admin") -async def admin_delete_profile_video(video_id: int, ctx: Context) -> str: - """ - Delete profile video of other users by video_id as admin - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/admin/videos/{video_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if video_id is not None: - url = url.replace("{video_id}", str(video_id)) - - # Make the request - response = await client.delete( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="GET /identity/api/v2/vehicle/vehicles") -async def get_vehicles(ctx: Context) -> str: - """ - GET /identity/api/v2/vehicle/vehicles - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/vehicle/vehicles" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="POST /identity/api/v2/vehicle/add_vehicle") -async def add_vehicle(ctx: Context) -> str: - """ - POST /identity/api/v2/vehicle/add_vehicle - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/vehicle/add_vehicle" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Get user's vehicle location") -async def get_location(ctx: Context) -> str: - """ - Get user's vehicle location - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/vehicle/{vehicleId}/location" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Resend vehicles details to be added to the user dashboard") -async def vehicle_resend_email(ctx: Context) -> str: - """ - Resend vehicles details to be added to the user dashboard - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/identity/api/v2/vehicle/resend_email" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get a specific post in the forum") -async def get_post(ctx: Context) -> str: - """ - Used to get a specific post in the forum - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/community/posts/{postId}" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to create a new post in the forum") -async def create_post(ctx: Context) -> str: - """ - Used to create a new post in the forum - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/community/posts" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to add a comment to an existing post in the forum") -async def post_comment(ctx: Context) -> str: - """ - Used to add a comment to an existing post in the forum - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/community/posts/{postId}/comment" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to fetch the most recent posts in the forum.") -async def get_recent_posts(ctx: Context, limit: int = 0, offset: int = 0) -> str: - """ - Used to fetch the most recent posts in the forum. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/community/posts/recent" - - # Extract query parameters - query_params = {} - request_body = None - - if limit is not None: - query_params["limit"] = limit - if offset is not None: - query_params["offset"] = offset - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to add a new coupon to the shop database") -async def add_new_coupon(ctx: Context) -> str: - """ - Used to add a new coupon to the shop database - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/coupon/new-coupon" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to validate the provided discount coupon code") -async def validate_coupon(ctx: Context) -> str: - """ - Used to validate the provided discount coupon code - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/community/api/v2/coupon/validate-coupon" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get products for the shop") -async def get_products(ctx: Context) -> str: - """ - Used to get products for the shop - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/products" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, - params=query_params, - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to add the specified product to the product catalog.") -async def add_new_product(ctx: Context) -> str: - """ - Used to add the specified product to the product catalog. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/products" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to create a new order for a product in the shop.") -async def create_order(ctx: Context) -> str: - """ - Used to create a new order for a product in the shop. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/orders" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to update the order specified by the order_id.") -async def update_order(order_id: int, ctx: Context) -> str: - """ - Used to update the order specified by the order_id. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/orders/{order_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if order_id is not None: - url = url.replace("{order_id}", str(order_id)) - - # Make the request - response = await client.put( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get the order details for order identified by order_id.") -async def get_order_byID(order_id: int, ctx: Context) -> str: - """ - Used to get the order details for order identified by order_id. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/orders/{order_id}" - - # Extract query parameters - query_params = {} - request_body = None - - if order_id is not None: - url = url.replace("{order_id}", str(order_id)) - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get user's past orders") -async def get_orders(limit: int, offset: int, ctx: Context) -> str: - """ - Used to get user's past orders - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/orders/all" - - # Extract query parameters - query_params = {} - request_body = None - - if limit is not None: - query_params["limit"] = limit - if offset is not None: - query_params["offset"] = offset - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to return order specified by the order_id") -async def return_order(order_id: int, ctx: Context) -> str: - """ - Used to return order specified by the order_id - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/orders/return_order" - - # Extract query parameters - query_params = {} - request_body = None - - if order_id is not None: - query_params["order_id"] = order_id - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to apply the coupon for the current user.") -async def apply_coupon(ctx: Context) -> str: - """ - Used to apply the coupon for the current user. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/apply_coupon" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get the return qr code image for UPS shipments.") -async def get_workshop_qr_code(Accept: str, ctx: Context) -> str: - """ - Used to get the return qr code image for UPS shipments. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/shop/return_qr_code" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get all the users in the workshop database.") -async def get_workshop_users_all(ctx: Context, limit: int = 0, offset: int = 0) -> str: - """ - Used to get all the users in the workshop database. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/management/users/all" - - # Extract query parameters - query_params = {} - request_body = None - - if limit is not None: - query_params["limit"] = limit - if offset is not None: - query_params["offset"] = offset - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get all the available mechanics") -async def get_mechanics(ctx: Context) -> str: - """ - Used to get all the available mechanics - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/mechanic/" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool( - description="Used to contact a mechanic for a service request on your vehicle" -) -async def contact_mechanic(ctx: Context) -> str: - """ - Used to contact a mechanic for a service request on your vehicle - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/merchant/contact_mechanic" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to create the service report and assign to the mechanic") -async def create_service_report( - mechanic_code: str, problem_details: str, vin: str, ctx: Context -) -> str: - """ - Used to create the service report and assign to the mechanic - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/mechanic/receive_report" - - # Extract query parameters - query_params = {} - request_body = None - - if mechanic_code is not None: - query_params["mechanic_code"] = mechanic_code - if problem_details is not None: - query_params["problem_details"] = problem_details - if vin is not None: - query_params["vin"] = vin - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to get the service report specified by the report_id") -async def get_report_byID(report_id: int, ctx: Context) -> str: - """ - Used to get the service report specified by the report_id - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/mechanic/mechanic_report" - - # Extract query parameters - query_params = {} - request_body = None - - if report_id is not None: - query_params["report_id"] = report_id - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Fetch all service requests assigned to this specific mechanic.") -async def get_service_requests_for_mechanic( - limit: int, offset: int, ctx: Context -) -> str: - """ - Fetch all service requests assigned to this specific mechanic. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/mechanic/service_requests" - - # Extract query parameters - query_params = {} - request_body = None - - if limit is not None: - query_params["limit"] = limit - if offset is not None: - query_params["offset"] = offset - - # Make the request - response = await client.get( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -@mcp.tool(description="Used to register a new mechanic in the workshop.") -async def mechanic_signup(ctx: Context) -> str: - """ - Used to register a new mechanic in the workshop. - """ - async with await get_http_client() as client: - try: - # Build the URL with path parameters - url = "/workshop/api/mechanic/signup" - - # Extract query parameters - query_params = {} - request_body = None - - # Make the request - response = await client.post( - url, params=query_params, json=request_body if request_body else None - ) - - # Check if the request was successful - response.raise_for_status() - - # Return the response - return str(response.text) - - except httpx.HTTPStatusError as e: - return f"API Error: {e.response.status_code} - {e.response.text}" - except Exception as e: - return f"Error: {str(e)}" - - -# MCP resources - - -@mcp.resource("api://info") -def get_api_info() -> str: - """ - Get API information - """ - return f""" - Title: OWASP crAPI API - Version: 1-oas3 - Description: API description - """ - - -@mcp.resource("schema://Order") -def get_Order_schema() -> str: - """ - Get the Order schema definition - """ - return """ - properties:\n created_on:\n format: date-time\n type: string\n id:\n readOnly: true\n type: integer\n product:\n $ref: '#/components/schemas/Product'\n quantity:\n type: integer\n status:\n $ref: '#/components/schemas/OrderStatusEnum'\n user:\n $ref: '#/components/schemas/User'\nrequired:\n- created_on\n- id\n- product\n- user\ntype: object\n - """ - - -@mcp.resource("schema://User") -def get_User_schema() -> str: - """ - Get the User schema definition - """ - return """ - properties:\n email:\n type: string\n number:\n nullable: true\n type: string\nrequired:\n- email\ntype: object\n - """ - - -@mcp.resource("schema://NewProduct") -def get_NewProduct_schema() -> str: - """ - Get the NewProduct schema definition - """ - return """ - example:\n image_url: http://example.com/wheelbase.png\n name: WheelBase\n price: '10.12'\nproperties:\n image_url:\n format: url\n type: string\n name:\n type: string\n price:\n format: decimal\n pattern: ^\\d{0,18}(\\.\\d{0,2})?$\n type: string\nrequired:\n- image_url\n- name\n- price\ntype: object\n - """ - - -@mcp.resource("schema://Products") -def get_Products_schema() -> str: - """ - Get the Products schema definition - """ - return """ - items:\n $ref: '#/components/schemas/Product'\ntype: array\n - """ - - -@mcp.resource("schema://Product") -def get_Product_schema() -> str: - """ - Get the Product schema definition - """ - return """ - example:\n id: 1\n image_url: images/seat.svg\n name: Seat\n price: '10.00'\nproperties:\n id:\n readOnly: true\n type: integer\n image_url:\n format: url\n type: string\n name:\n type: string\n price:\n format: decimal\n pattern: ^\\d{0,18}(\\.\\d{0,2})?$\n type: string\nrequired:\n- id\n- image_url\n- name\n- price\ntype: object\n - """ - - -@mcp.resource("schema://OrderStatusEnum") -def get_OrderStatusEnum_schema() -> str: - """ - Get the OrderStatusEnum schema definition - """ - return """ - enum:\n- delivered\n- return pending\n- returned\ntype: string\n - """ - - -@mcp.resource("schema://ProductQuantity") -def get_ProductQuantity_schema() -> str: - """ - Get the ProductQuantity schema definition - """ - return """ - properties:\n product_id:\n example: 1\n type: integer\n quantity:\n example: 1\n type: integer\nrequired:\n- product_id\n- quantity\ntype: object\n - """ - - -@mcp.resource("schema://Post") -def get_Post_schema() -> str: - """ - Get the Post schema definition - """ - return """ - example:\n CreatedAt: '2021-09-16T01:46:32.432Z'\n author:\n created_at: '2021-09-16T01:46:32.432Z'\n email: hacker@darkweb.com\n nickname: Hacker\n profile_pic_url: ''\n vehicleid: abac4018-5a38-466c-ab7f-361908afeab6\n authorid: 3\n comments: []\n content: Hello world 3\n id: ConZLXacq3MqhbLQDrbNLf\n title: Title 3\nproperties:\n CreatedAt:\n type: string\n author:\n $ref: '#/components/schemas/Author'\n authorid:\n format: int32\n type: integer\n comments:\n description: ''\n items:\n type: string\n type: array\n content:\n type: string\n id:\n type: string\n title:\n type: string\nrequired:\n- id\n- title\n- content\n- author\n- comments\n- authorid\n- CreatedAt\ntitle: Post\ntype: object\n - """ - - -@mcp.resource("schema://Author") -def get_Author_schema() -> str: - """ - Get the Author schema definition - """ - return """ - example:\n created_at: '2021-09-16T01:46:32.432Z'\n email: hacker@darkweb.com\n nickname: Hacker\n profile_pic_url: ''\n vehicleid: 4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5\nproperties:\n created_at:\n type: string\n email:\n type: string\n nickname:\n type: string\n profile_pic_url:\n type: string\n vehicleid:\n type: string\nrequired:\n- nickname\n- email\n- vehicleid\n- profile_pic_url\n- created_at\ntitle: Author\ntype: object\n - """ - - -@mcp.resource("schema://VideoForm") -def get_VideoForm_schema() -> str: - """ - Get the VideoForm schema definition - """ - return """ - properties:\n conversion_params:\n type: string\n id:\n format: int64\n type: integer\n videoName:\n type: string\n video_url:\n type: string\ntype: object\n - """ - - -@mcp.resource("schema://CRAPIResponse") -def get_CRAPIResponse_schema() -> str: - """ - Get the CRAPIResponse schema definition - """ - return """ - properties:\n message:\n type: string\n status:\n format: int32\n type: integer\ntype: object\n - """ - - -@mcp.resource("schema://OtpForm") -def get_OtpForm_schema() -> str: - """ - Get the OtpForm schema definition - """ - return """ - properties:\n email:\n example: Cristobal.Weissnat@example.com\n maxLength: 30\n minLength: 5\n type: string\n otp:\n example: '9969'\n maxLength: 4\n minLength: 3\n type: string\n password:\n example: 5hmb0gvyC__hVQg\n maxLength: 30\n minLength: 5\n type: string\nrequired:\n- email\n- otp\n- password\ntype: object\n - """ - - -@mcp.resource("schema://JwtResponse") -def get_JwtResponse_schema() -> str: - """ - Get the JwtResponse schema definition - """ - return """ - properties:\n message:\n type: string\n role:\n enum:\n - ROLE_UNDEFINED\n - ROLE_USER\n - ROLE_MECHANIC\n - ROLE_ADMIN\n type: string\n token:\n type: string\n type:\n type: string\ntype: object\n - """ - - -@mcp.resource("schema://LoginWithEmailToken") -def get_LoginWithEmailToken_schema() -> str: - """ - Get the LoginWithEmailToken schema definition - """ - return """ - properties:\n email:\n maxLength: 60\n minLength: 3\n type: string\n token:\n maxLength: 60\n minLength: 3\n type: string\nrequired:\n- email\n- token\ntype: object\n - """ - - -@mcp.resource("schema://ProfileVideo") -def get_ProfileVideo_schema() -> str: - """ - Get the ProfileVideo schema definition - """ - return """ - example:\n conversion_params: -v codec h264\n id: 1\n profileVideo: data:image/jpeg;base64,aGFrZmhhcw==\n video_name: abc.mp4\nproperties:\n conversion_params:\n type: string\n id:\n type: number\n user:\n $ref: '#/components/schemas/User'\n video:\n type: string\n video_name:\n type: string\nrequired:\n- id\n- video_name\n- converstion_params\n- video\n- user\ntype: object\n - """ - - -@mcp.resource("schema://ApplyCouponRequest") -def get_ApplyCouponRequest_schema() -> str: - """ - Get the ApplyCouponRequest schema definition - """ - return """ - example:\n amount: 75\n coupon_code: TRAC075\nproperties:\n amount:\n type: integer\n coupon_code:\n type: string\nrequired:\n- amount\n- coupon_code\ntype: object\n - """ - - -@mcp.resource("schema://ApplyCouponResponse") -def get_ApplyCouponResponse_schema() -> str: - """ - Get the ApplyCouponResponse schema definition - """ - return """ - example:\n credit: 165\n message: Coupon successfully applied!\nproperties:\n credit:\n type: integer\n message:\n type: string\nrequired:\n- credit\n- message\ntype: object\n - """ - - -@mcp.resource("schema://AddCouponRequest") -def get_AddCouponRequest_schema() -> str: - """ - Get the AddCouponRequest schema definition - """ - return """ - example:\n amount: 75\n coupon_code: TRAC075\nproperties:\n amount:\n type: integer\n coupon_code:\n type: string\nrequired:\n- coupon_code\n- amount\ntype: object\n - """ - - -@mcp.resource("schema://AddCouponResponse") -def get_AddCouponResponse_schema() -> str: - """ - Get the AddCouponResponse schema definition - """ - return """ - example:\n CreatedAt: '2023-12-07T14:22:29.832Z'\n amount: '75'\n coupon_code: TRAC075\nproperties:\n amount:\n type: string\n coupon_code:\n type: string\n createdAt:\n type: string\nrequired:\n- amount\n- coupon_code\n- CreatedAt\ntype: object\n - """ - - -@mcp.resource("schema://ValidateCouponRequest") -def get_ValidateCouponRequest_schema() -> str: - """ - Get the ValidateCouponRequest schema definition - """ - return """ - example:\n coupon_code: TRAC075\nproperties:\n coupon_code:\n type: string\nrequired:\n- coupon_code\ntype: object\n - """ - - -@mcp.resource("schema://ValidateCouponResponse") -def get_ValidateCouponResponse_schema() -> str: - """ - Get the ValidateCouponResponse schema definition - """ - return """ - example:\n CreatedAt: '2023-12-07T14:22:29.832Z'\n amount: '75'\n coupon_code: TRAC075\nproperties:\n amount:\n type: string\n coupon_code:\n type: string\n createdAt:\n type: string\nrequired:\n- amount\n- coupon_code\n- CreatedAt\ntype: object\n - """ - - -@mcp.resource("schema://ServiceRequests") -def get_ServiceRequests_schema() -> str: - """ - Get the ServiceRequests schema definition - """ - return """ - properties:\n service_requests:\n items:\n properties:\n created_on:\n format: date-time\n type: string\n id:\n readOnly: true\n type: integer\n mechanic:\n properties:\n id:\n readOnly: true\n type: integer\n mechanic_code:\n type: string\n user:\n properties:\n email:\n type: string\n number:\n nullable: true\n type: string\n required:\n - email\n type: object\n required:\n - id\n - mechanic_code\n - user\n type: object\n problem_details:\n type: string\n status:\n enum:\n - Pending\n - Finished\n type: string\n vehicle:\n properties:\n id:\n readOnly: true\n type: integer\n owner:\n properties:\n email:\n type: string\n number:\n nullable: true\n type: string\n required:\n - email\n type: object\n vin:\n type: string\n required:\n - id\n - owner\n - vin\n type: object\n required:\n - created_on\n - id\n - mechanic\n - vehicle\n type: object\n type: array\nrequired:\n- service_requests\ntitle: Service Requests\ntype: object\n - """ - - -def parse_args(): - """Parse command line arguments.""" - parser = argparse.ArgumentParser(description="MCP Server for OWASP crAPI API") - parser.add_argument( - "--transport", - choices=["stdio", "streamable-http"], - default="streamable-http", - help="Transport type (stdio or streamable-http)", - ) - return parser.parse_args() - - -async def main(): - # Use run_async() in async contexts - await mcp.run_streamable_http_async() - if __name__ == "__main__": - args = parse_args() - logger.info(f"Starting MCP server with {args.transport} transport") - asyncio.run(main()) + mcp_server_port = int(os.environ.get("MCP_SERVER_PORT", 5500)) + mcp.run(transport="streamable-http", host="0.0.0.0", port=mcp_server_port,) diff --git a/services/chatbot/src/mcpserver/crapi-openapi-spec.json b/services/chatbot/src/resources/crapi-openapi-spec.json similarity index 100% rename from services/chatbot/src/mcpserver/crapi-openapi-spec.json rename to services/chatbot/src/resources/crapi-openapi-spec.json diff --git a/services/identity/Dockerfile b/services/identity/Dockerfile index d85c28a5..865e6f4e 100644 --- a/services/identity/Dockerfile +++ b/services/identity/Dockerfile @@ -37,7 +37,7 @@ COPY --from=gradlebuild /app/build/libs/identity-service-1.0-SNAPSHOT.jar /app/i ARG SERVER_PORT EXPOSE ${SERVER_PORT} -EXPOSE 10001 +EXPOSE 10001 8080 8989 ENV JAVA_TOOL_OPTIONS="-Xmx128m" diff --git a/services/identity/src/main/java/com/crapi/config/WebSecurityConfig.java b/services/identity/src/main/java/com/crapi/config/WebSecurityConfig.java index 614402f7..4aa5ccb8 100644 --- a/services/identity/src/main/java/com/crapi/config/WebSecurityConfig.java +++ b/services/identity/src/main/java/com/crapi/config/WebSecurityConfig.java @@ -84,6 +84,8 @@ public SecurityFilterChain securityFilterChainWeb(HttpSecurity http) throws Exce .permitAll() .requestMatchers("/identity/api/v2/user/dashboard") .permitAll() + .requestMatchers("/identity/management/user/**") + .permitAll() .requestMatchers("/identity/management/admin/**") .hasRole("ADMIN") .anyRequest()