Skip to content

Conversation

@Bin4yi
Copy link

@Bin4yi Bin4yi commented Nov 20, 2025

Purpose

Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc.

$subject

Summary by CodeRabbit

  • New Features

    • Secured AI-powered veterinary assistant with Asgardeo OAuth2 and JWT/JWKS validation
    • Interactive real-time chat UIs (Streamlit frontend and standalone web client) with per-session history
    • Backend MCP service exposing pet tools: list pets, vaccination history, book/cancel appointments, and AI name suggestions
    • Embedded lightweight SQLite data layer and DB initializer
  • Documentation

    • Comprehensive README with setup, auth configuration, architecture, sample flows, troubleshooting, and run instructions

✏️ Tip: You can customize this high-level summary in your review settings.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Warning

Rate limit exceeded

@Bin4yi has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 26 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0bda4bc and c7b2c67.

📒 Files selected for processing (1)
  • Pet_Care_ai_agent_secured_with_asgardeo/README.md (1 hunks)

Walkthrough

Adds a secured Pet Care AI system: Asgardeo PKCE authentication, JWKS-backed JWT validation, an authenticated FastMCP service with pet-care tools (including OpenAI name suggestions), a LangGraph-based agent integrating MCP tools and multi-pet context, plus Streamlit and standalone HTML chat frontends and comprehensive README.

Changes

Cohort / File(s) Summary
Documentation
Pet_Care_ai_agent_secured_with_asgardeo/README.md
Adds a comprehensive README describing architecture, prerequisites, Asgardeo MCP setup, env vars, installation, running instructions, sample flows, troubleshooting, and contribution/licensing notes.
JWT Validation
Pet_Care_ai_agent_secured_with_asgardeo/jwt_validator.py
New JWTValidator class: async JWKS fetching/caching, kid-based key resolution, RS256 verification, detailed error mapping, and factory create_jwt_validator.
MCP Service & Tools
Pet_Care_ai_agent_secured_with_asgardeo/main.py
New FastMCP service with JWTTokenVerifier, env config, in-memory data access integration, and authenticated tool endpoints: get_user_id_by_email, get_pets_by_user_id, get_pet_vaccination_info, book_vet_appointment, cancel_appointment, and suggest_pet_names (OpenAI integration and response parsing).
Agent Orchestration
Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py
New LangGraph-driven agent implementing Asgardeo PKCE auth (PKCE utilities, local callback server), token/email retrieval (ID token + SCIM2 fallback), MCP session & dynamic tool loading, ChatbotState TypedDict, nodes (load_context, classify, mcp_agent, greeting/service/pricing/general), graph wiring, HTTP /chat handler, and run orchestration.
Frontends
Pet_Care_ai_agent_secured_with_asgardeo/agent/frontend_app.py, Pet_Care_ai_agent_secured_with_asgardeo/agent/index.html
Adds Streamlit-based authenticated chat UI with session/token management and async chat calls; adds standalone index.html chat page with styled UI and client-side POST /chat integration.
Database & Init
Pet_Care_ai_agent_secured_with_asgardeo/database.py, Pet_Care_ai_agent_secured_with_asgardeo/init_database.py
Adds SQLite-based data layer (users, pets, vaccinations, appointments) with safe context manager queries and mutations, plus DB initialization script inserting sample data.
Dependencies
Pet_Care_ai_agent_secured_with_asgardeo/requirements.txt
Adds project dependencies: mcpauth, mcp, PyJWT, httpx, python-dotenv, openai, langgraph, langchain, and related packages.

Sequence Diagram(s)

sequenceDiagram
    participant User as User / Frontend
    participant Agent as Agent<br/>(agent.py)
    participant MCP as MCP Server<br/>(main.py)
    participant Asgardeo as Asgardeo<br/>OAuth2
    participant LLM as OpenAI<br/>LLM

    User->>Agent: Launch interactive chat
    Agent->>Agent: Generate PKCE challenge
    Agent->>Asgardeo: Initiate OAuth2 (PKCE)
    Asgardeo->>Agent: Authorization code
    Agent->>Asgardeo: Exchange code for tokens
    Asgardeo->>Agent: Access token + ID token
    Agent->>Agent: Extract user_email (ID token / SCIM2 fallback)
    Agent->>MCP: Establish MCP session
    MCP->>MCP: Load JWT validator with JWKS
    Agent->>MCP: Load available tools
    MCP->>Agent: Return tool list
    Agent->>Agent: Compile LangGraph with nodes

    rect rgb(220, 235, 255)
    Note over User,LLM: Chat Flow - Per User Message
    User->>Agent: Send chat message
    Agent->>Agent: Build initial state (user_id, pets, history)
    Agent->>Agent: Route to load_context_node
    Agent->>MCP: Call get_user_id_by_email(user_email)
    MCP->>MCP: Validate JWT, lookup user
    MCP->>Agent: Return user_id
    Agent->>MCP: Call get_pets_by_user_id(user_id)
    MCP->>Agent: Return pets list
    Agent->>Agent: Route to classify_node
    Agent->>LLM: Classify user intent
    LLM->>Agent: Return intent category
    alt Intent requires MCP action
        Agent->>Agent: Build context-aware prompt with pet info
        Agent->>LLM: Invoke agent with tools
        LLM->>Agent: Decide tool calls
        Agent->>MCP: Call tool(s) with JWT
        MCP->>Agent: Return tool results
        Agent->>LLM: Provide results to LLM
        LLM->>Agent: Return final response
    else Simple response
        Agent->>LLM: Generate predefined response
        LLM->>Agent: Return response
    end
    Agent->>Agent: Update session cache
    Agent->>User: Return bot response
    end
Loading
sequenceDiagram
    participant Client as HTTP Client
    participant Frontend as Frontend<br/>(frontend_app.py / index.html)
    participant Agent as Agent<br/>HTTP Server
    participant MCP as MCP Server<br/>(JWT validated)

    Client->>Frontend: Open chat UI
    Frontend->>Frontend: Check session auth
    alt Not authenticated
        Frontend->>Frontend: Show Asgardeo login (PKCE)
        Frontend->>Frontend: Store access_token
    end

    Client->>Frontend: Send message
    Frontend->>Agent: POST /chat { message }
    Agent->>Agent: Extract user state (cache or rebuild)
    Agent->>MCP: Validate JWT from agent session
    Agent->>MCP: Call tool endpoints as needed
    MCP->>Agent: Tool results
    Agent->>LLM: Compose final response
    Agent->>Frontend: Return JSON response
    Frontend->>Client: Render message + response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas requiring extra attention:
    • PKCE OAuth2 flow and local callback handling in agent/agent.py (token exchange, redirect, email extraction fallback).
    • JWKS fetching/caching and RS256 verification in jwt_validator.py.
    • LangGraph node routing, state transitions, and multi-pet context handling in agent/agent.py.
    • MCP tool authorization, error wrapping and consistent response shapes in main.py.
    • OpenAI integration and robust parsing in suggest_pet_names.
    • Database schema, transactional safety, and sample-data correctness in init_database.py and database.py.
    • Frontend async behavior and token/session persistence in frontend_app.py and index.html.

Poem

🐰 A paws-ome journey hops along the wire,
Tokens, graphs, and gentle code inspire.
Pets and humans chat with secure delight,
Tools, names, and vet visits set just right.
Hooray — the rabbit cheers, tail twitching bright!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete with only a placeholder ($subject) in the Purpose section and missing all substantive content required by the template (Goals, Approach, User stories, Release note, etc.). Complete all required template sections including Purpose (with linked issues if applicable), Goals, Approach, User stories, Release note, Documentation, and relevant security/test information. Remove placeholder tokens and provide actual content.
Docstring Coverage ⚠️ Warning Docstring coverage is 65.85% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'vet-clinic-ai-agent-with-secure-mcp' clearly and specifically describes the main change: a veterinary clinic AI agent with secure Model Context Protocol integration.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (13)
Pet_Care_ai_agent_secured_with_asgardeo/jwt_validator.py (1)

48-58: Tighten exception handling and preserve original error context

Two improvements would make diagnostics and behavior clearer:

  1. Avoid broad except Exception in _fetch_jwks and validate_token

    • In _fetch_jwks, catching Exception and re‑raising adds little value; or at least narrow to httpx.HTTPError (or similar) and let unexpected errors bubble up.
    • In validate_token, the final except Exception as e will also catch infrastructure issues (network, JWKS parsing) and re‑label them as ValueError("Token validation failed: ..."), making it hard for callers to distinguish “bad token” vs “auth infra unavailable”.
  2. Preserve original exceptions via chaining and richer logging

    • For the specific JWT error branches, consider chaining:
      except jwt.ExpiredSignatureError as e:
          raise ValueError("Token has expired") from e
    • For the generic branch, logger.exception("Token validation error") will record the stack trace, and you can either re‑raise the original error or use a custom exception type so callers can treat infra failures differently from invalid tokens.

These changes keep behavior mostly the same for callers while giving you clearer observability and debuggability.

Also applies to: 124-137

Pet_Care_ai_agent_secured_with_asgardeo/agent/frontend_app.py (1)

13-17: Auth gating and chat flow look solid; consider wrapping backend errors for UX

The Streamlit state handling and auth checks (forcing login before sending, clearing state on logout, storing history in st.session_state.messages) are consistent and easy to follow.

One optional improvement: if perform_login() or run_chat_turn() raise (network issues, unexpected backend error), users will see a raw Streamlit exception. You could wrap these calls in try/except and surface a friendly st.error("Something went wrong talking to the agent. Please try again.") while logging the traceback for debugging.

Also applies to: 24-40, 55-87

Pet_Care_ai_agent_secured_with_asgardeo/requirements.txt (1)

1-10: ---

Pin AI dependencies to stable LTS versions to ensure reproducible deployments

openai, langgraph, langchain, langchain-openai, and langchain-core are unpinned while others are version-constrained. These libraries release frequently with breaking changes and inter-package incompatibilities; leaving them unpinned risks silent breakage on deployment.

Recommended production set (mutually compatible, stable 1.0 LTS line):

  • langchain~=1.0.7
  • langchain-core~=1.0.5
  • langchain-openai~=1.0.3
  • langgraph~=1.0.3

Verify the latest 1.0.* patch releases against PyPI and your integration tests before committing.

Pet_Care_ai_agent_secured_with_asgardeo/main.py (5)

98-135: JWTTokenVerifier looks good; consider tightening generic Exception catch

The mapping from validated JWT payload to AccessToken is reasonable and logs failures. The final except Exception will hide unexpected bugs (e.g., programming errors) by returning None.

If you want stricter behavior, consider only catching ValueError (from JWTValidator) and letting other exceptions surface, or at least logging with logger.exception to preserve the traceback.


161-196: Email lookup logic fine; reconsider MD5 for synthetic user IDs and broad Exception

The email normalization and lookup logic are solid, and the synthetic ID fallback is helpful for unknown users. Two minor points:

  • hashlib.md5 is flagged as insecure; even though this is only for generating a non-secret user ID, consider hashlib.sha256 for better defaults.
  • The broad except Exception wraps all errors into a ValueError, losing the original stack trace. Narrowing this to expected errors (or using logger.exception and raise ValueError(...) from error) would aid debugging.

Example tweak:

-            import hashlib
-            hash_object = hashlib.md5(email_clean.encode())
+            import hashlib
+            hash_object = hashlib.sha256(email_clean.encode())
@@
-    except Exception as error:
-        logger.error(f"System error looking up user ID: {error}")
-        raise ValueError(f"Failed to retrieve user ID: {str(error)}")
+    except Exception as error:
+        logger.exception("System error looking up user ID")
+        raise ValueError("Failed to retrieve user ID") from error

Please ensure any hash change doesn’t break existing synthetic IDs you might persist elsewhere.


268-334: Consider validating pet_id (and optionally time) when booking appointments

The booking flow (date validation, synthetic appointment_id, rich confirmation payload, and logging) is good for a sample.

Two potential improvements:

  • Validate that pet_id exists in PETS_DB before confirming the booking, so you don’t create appointments for non-existent pets.
  • Optionally validate the time string (e.g., using a fixed format like HH:MM or a parsing helper) if you expect a particular format.

Example addition:

-    try:
-        # Validate date format
+    try:
+        # Validate date format
         try:
             datetime.strptime(date, "%Y-%m-%d")
         except ValueError:
             raise ValueError("Invalid date format. Please use YYYY-MM-DD format.")
+
+        # Optional: ensure pet exists
+        if pet_id not in PETS_DB:
+            raise ValueError(f"Unknown pet_id '{pet_id}'")

335-359: Typo in tool name (cancel_appinment) and internal variable

Functionality and payload look fine, but:

  • The tool name is misspelled (cancel_appinment instead of cancel_appointment), which leaks into the MCP tool surface and may confuse consumers.
  • Local variable cancelllation_details has an extra l (only internal, but distracting).

If you can still safely change the API, consider:

-@mcp.tool()
-async def cancel_appinment(appointment_id: str, reason: str) -> dict[str, Any]:
+@mcp.tool()
+async def cancel_appointment(appointment_id: str, reason: str) -> dict[str, Any]:
@@
-        cancelllation_details = {
+        cancellation_details = {
@@
-        return cancelllation_details
+        return cancellation_details

361-501: Simplify OpenAI usage and make JSON parsing a bit more robust

The overall flow (validation of inputs, prompt construction, JSON-only response contract, cleanup of code-block fences, detailed error handling) is solid. A few targeted refinements:

  • You’re maintaining both a global openai_client and openai.api_key. Since all calls go through openai_client.chat.completions.create, you can drop the openai.api_key logic and just require openai_client to be non-None.
  • When JSON parsing fails you already log the full raw response; consider logger.exception to preserve tracebacks.
  • In the RateLimitError and AuthenticationError branches the caught exception variable e is unused; you can drop the as e.

Sketch:

-    try:
-        # Ensure OpenAI API key is set from environment if available
-        if OPENAI_API_KEY and not openai.api_key:
-            openai.api_key = OPENAI_API_KEY
-        
-        if not openai.api_key:
-            raise ValueError("OpenAI API key is not configured. Please set OPENAI_API_KEY in environment variables.")
+    try:
+        if not openai_client:
+            raise ValueError("OpenAI API key not configured. Please set OPENAI_API_KEY environment variable.")
@@
-        if not openai_client:
-            raise ValueError("OpenAI API key not configured. Please set OPENAI_API_KEY environment variable.")
+        # openai_client guaranteed non-None here
@@
-    except openai.RateLimitError as e:
+    except openai.RateLimitError:
@@
-    except openai.AuthenticationError as e:
+    except openai.AuthenticationError:

And for JSON parsing:

-        except json.JSONDecodeError:
-            logger.error(f"Failed to parse OpenAI response as JSON: {ai_response}")
+        except json.JSONDecodeError:
+            logger.exception("Failed to parse OpenAI response as JSON")
             raise ValueError("Failed to parse AI response. Please try again.")

Please confirm this matches the openai SDK version you have pinned (client construction & exception classes).

Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (5)

74-135: PKCE flow and callback server are reasonable; tighten timeout error type if needed

generate_pkce and start_callback_server are implemented correctly, and the async auth flow with asyncio.wait_for is clear.

The timeout path currently raises a generic Exception("Authentication timed out."). If you plan to surface this to callers/UI distinctly, consider defining a small custom exception (e.g., AuthTimeoutError) so upstream code can distinguish timeouts from other failures.


213-287: Context loader works; consider improving JSON parsing and exception granularity

The logic to resolve user_id and pets via MCP tools is good and nicely separated into two steps. Two small things:

  • The JSON parsing uses try/except: pass, which silently ignores parsing errors and leaves data = {}. This makes debugging harder if the MCP tool output changes format.
  • Both steps wrap everything in a broad except Exception and print a message; you might want to at least log the stacktrace.

Example tweak for Step 1 (similarly for Step 2):

-                if isinstance(result, str):
-                    try:
-                        clean = result.replace('```json', '').replace('```', '').strip()
-                        data = json.loads(clean)
-                    except: pass
+                if isinstance(result, str):
+                    try:
+                        clean = result.replace('```json', '').replace('```', '').strip()
+                        data = json.loads(clean)
+                    except json.JSONDecodeError as e:
+                        print(f"   ❌ Failed to parse get_user_id_by_email result as JSON: {e}")

410-426: Intent routing and simple nodes are fine for this use case

The greeting, services, pricing, and general nodes are minimal but functional, and route_intent cleanly dispatches based on the classifier output.

Only note: state is unused in greeting_node, service_node, and pricing_node, which is fine but will trigger linters; keep as-is unless you want to evolve them later.


451-510: Chat handler is coherent; consider more structured error logging

The flow — parse request, pull graph & tools from app, restore history/context, run the graph, update caches, and return the last AI message — is well-structured.

Minor suggestions:

  • In the except Exception block, you currently print the error and traceback. For a longer-lived service, switching to logging.exception (or at least logging alongside the JSON error response) would help observability.
  • If final_state["messages"] ever diverges from history + new_messages, the slice final_state["messages"][len(history):] could become brittle, but given how nodes are implemented today this looks safe.

Example:

-    except Exception as e:
-        print(f"Error: {e}")
-        import traceback
-        traceback.print_exc()
-        return web.json_response({"error": str(e)}, status=500)
+    except Exception as e:
+        import logging, traceback
+        logging.error("Error handling /chat request: %s", e)
+        traceback.print_exc()
+        return web.json_response({"error": str(e)}, status=500)

511-553: Auth fallback to mock token is convenient; guard it for non-demo environments

The orchestration of auth, MCP connection, tool loading, graph compilation, and aiohttp server startup is nicely done.

One caution: on auth failure you fall back to a hardcoded access_token = "mock_token" and fixed email. That’s great for local demos but risky if this code is ever reused in a less controlled environment.

Consider gating this behavior behind an explicit “demo mode” flag or environment variable so production deployments fail fast instead of silently using a mock token.

-    try:
-        access_token, user_email = await authenticate_with_asgardeo()
-    except Exception as e:
-        print(f"Auth failed (using mock): {e}")
-        access_token = "mock_token"
-        user_email = "[email protected]"
+    try:
+        access_token, user_email = await authenticate_with_asgardeo()
+    except Exception as e:
+        print(f"Auth failed: {e}")
+        if os.getenv("DEMO_MODE", "false").lower() == "true":
+            print("⚠️ DEMO_MODE enabled: falling back to mock token.")
+            access_token = "mock_token"
+            user_email = "[email protected]"
+        else:
+            raise

If you expect to run this against real Asgardeo tenants, please confirm whether a mock-token fallback is acceptable operationally.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0517fbe and 6cc9f84.

⛔ Files ignored due to path filters (4)
  • Pet_Care_ai_agent_secured_with_asgardeo/images/mcp-auth-configs.png is excluded by !**/*.png
  • Pet_Care_ai_agent_secured_with_asgardeo/images/mcp-client-app-selection.png is excluded by !**/*.png
  • Pet_Care_ai_agent_secured_with_asgardeo/images/mcp-client-creation.png is excluded by !**/*.png
  • Pet_Care_ai_agent_secured_with_asgardeo/images/mcp-server-connect.png is excluded by !**/*.png
📒 Files selected for processing (7)
  • Pet_Care_ai_agent_secured_with_asgardeo/README.md (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/frontend_app.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/index.html (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/jwt_validator.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/main.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/requirements.txt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Pet_Care_ai_agent_secured_with_asgardeo/main.py (1)
Pet_Care_ai_agent_secured_with_asgardeo/jwt_validator.py (2)
  • JWTValidator (26-136)
  • validate_token (78-136)
🪛 Gitleaks (8.29.0)
Pet_Care_ai_agent_secured_with_asgardeo/README.md

[high] 81-81: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 markdownlint-cli2 (0.18.1)
Pet_Care_ai_agent_secured_with_asgardeo/README.md

24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


227-227: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


240-240: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


252-252: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.5)
Pet_Care_ai_agent_secured_with_asgardeo/jwt_validator.py

56-56: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


69-69: Avoid specifying long messages outside the exception class

(TRY003)


76-76: Avoid specifying long messages outside the exception class

(TRY003)


122-122: Consider moving this statement to an else block

(TRY300)


125-125: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


125-125: Avoid specifying long messages outside the exception class

(TRY003)


127-127: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


127-127: Avoid specifying long messages outside the exception class

(TRY003)


129-129: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


129-129: Avoid specifying long messages outside the exception class

(TRY003)


131-131: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


131-131: Avoid specifying long messages outside the exception class

(TRY003)


133-133: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


133-133: Avoid specifying long messages outside the exception class

(TRY003)


134-134: Do not catch blind exception: Exception

(BLE001)


135-135: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


136-136: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


136-136: Avoid specifying long messages outside the exception class

(TRY003)

Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py

121-121: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


121-121: Create your own exception

(TRY002)


121-121: Avoid specifying long messages outside the exception class

(TRY003)


152-152: Do not catch blind exception: Exception

(BLE001)


200-200: Do not catch blind exception: Exception

(BLE001)


238-238: Multiple statements on one line (colon)

(E701)


243-243: Do not use bare except

(E722)


243-243: try-except-pass detected, consider logging the exception

(S110)


243-243: Multiple statements on one line (colon)

(E701)


249-249: Do not catch blind exception: Exception

(BLE001)


265-265: Multiple statements on one line (colon)

(E701)


270-270: Do not use bare except

(E722)


270-270: try-except-pass detected, consider logging the exception

(S110)


270-270: Multiple statements on one line (colon)

(E701)


283-283: Do not catch blind exception: Exception

(BLE001)


399-399: Do not catch blind exception: Exception

(BLE001)


402-402: Consider [*messages_with_context, response, *tool_outputs] instead of concatenation

Replace with [*messages_with_context, response, *tool_outputs]

(RUF005)


405-405: Consider [response, *tool_outputs, final_response] instead of concatenation

Replace with [response, *tool_outputs, final_response]

(RUF005)


410-410: Unused function argument: state

(ARG001)


412-412: Unused function argument: state

(ARG001)


414-414: Unused function argument: state

(ARG001)


421-421: Multiple statements on one line (colon)

(E701)


422-422: Multiple statements on one line (colon)

(E701)


423-423: Multiple statements on one line (colon)

(E701)


424-424: Multiple statements on one line (colon)

(E701)


427-427: Unused function argument: mcp_tools

(ARG001)


469-469: Consider [*history, HumanMessage(content=user_input)] instead of concatenation

Replace with [*history, HumanMessage(content=user_input)]

(RUF005)


505-505: Do not catch blind exception: Exception

(BLE001)


518-518: Do not catch blind exception: Exception

(BLE001)


520-520: Possible hardcoded password assigned to: "access_token"

(S105)


538-538: Unused lambda argument: r

(ARG005)


544-544: Multiple statements on one line (colon)

(E701)

Pet_Care_ai_agent_secured_with_asgardeo/main.py

132-132: Do not catch blind exception: Exception

(BLE001)


133-133: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


143-143: Avoid specifying long messages outside the exception class

(TRY003)


166-166: Abstract raise to an inner function

(TRY301)


166-166: Avoid specifying long messages outside the exception class

(TRY003)


184-184: Probable use of insecure hash functions in hashlib: md5

(S324)


193-193: Do not catch blind exception: Exception

(BLE001)


194-194: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


195-195: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


195-195: Avoid specifying long messages outside the exception class

(TRY003)


195-195: Use explicit conversion flag

Replace with conversion flag

(RUF010)


214-214: Abstract raise to an inner function

(TRY301)


214-214: Avoid specifying long messages outside the exception class

(TRY003)


233-233: Do not catch blind exception: Exception

(BLE001)


235-235: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


237-237: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


237-237: Avoid specifying long messages outside the exception class

(TRY003)


237-237: Use explicit conversion flag

Replace with conversion flag

(RUF010)


246-246: Abstract raise to an inner function

(TRY301)


246-246: Avoid specifying long messages outside the exception class

(TRY003)


261-261: Consider moving this statement to an else block

(TRY300)


263-263: Do not catch blind exception: Exception

(BLE001)


264-264: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


265-265: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


265-265: Avoid specifying long messages outside the exception class

(TRY003)


265-265: Use explicit conversion flag

Replace with conversion flag

(RUF010)


293-293: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


293-293: Avoid specifying long messages outside the exception class

(TRY003)


324-324: Consider moving this statement to an else block

(TRY300)


328-328: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


329-329: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


329-329: Avoid specifying long messages outside the exception class

(TRY003)


330-330: Do not catch blind exception: Exception

(BLE001)


332-332: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


333-333: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


333-333: Avoid specifying long messages outside the exception class

(TRY003)


355-355: Consider moving this statement to an else block

(TRY300)


356-356: Do not catch blind exception: Exception

(BLE001)


358-358: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


359-359: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


359-359: Avoid specifying long messages outside the exception class

(TRY003)


385-385: Abstract raise to an inner function

(TRY301)


385-385: Avoid specifying long messages outside the exception class

(TRY003)


393-393: Abstract raise to an inner function

(TRY301)


393-393: Avoid specifying long messages outside the exception class

(TRY003)


397-397: Abstract raise to an inner function

(TRY301)


397-397: Avoid specifying long messages outside the exception class

(TRY003)


427-427: Abstract raise to an inner function

(TRY301)


427-427: Avoid specifying long messages outside the exception class

(TRY003)


466-466: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


467-467: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


467-467: Avoid specifying long messages outside the exception class

(TRY003)


481-481: Consider moving this statement to an else block

(TRY300)


484-484: Use explicit conversion flag

Replace with conversion flag

(RUF010)


485-485: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


486-486: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


486-486: Avoid specifying long messages outside the exception class

(TRY003)


487-487: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


489-489: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


490-490: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


491-491: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


493-493: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


494-494: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


496-496: Use raise without specifying exception name

Remove exception name

(TRY201)


497-497: Do not catch blind exception: Exception

(BLE001)


499-499: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


500-500: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


500-500: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (4)
Pet_Care_ai_agent_secured_with_asgardeo/main.py (3)

197-238: Pet lookup by user ID is correct and straightforward

The join through USERS_DB to resolve owner_email and then filter PETS_DB is clear and defensively handles unknown user IDs.

No functional issues spotted here.


240-266: Vaccination info tool is consistent with in-memory schema

Lookup against PETS_DB and VACCINATIONS_DB plus the merged response structure (history, upcoming, timestamp) all look consistent. Error wrapping into ValueError is aligned with other tools.

No changes needed from my side.


145-159: The original code is correct; disregard this review comment.

The review's claim that AnyHttpUrl is "not callable" and will raise a TypeError is incorrect. In Pydantic v2, URL types like AnyHttpUrl are callable constructors that validate input. Furthermore, FastMCP's AuthSettings documentation examples show using AnyHttpUrl for issuer_url and resource_server_url fields, which matches your code exactly.

Your current implementation is valid and consistent with FastMCP's documented patterns. No changes are needed.

Likely an incorrect or invalid review comment.

Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (1)

427-447: Graph wiring matches the intended flow

The graph correctly sets load_context as the entry, runs classification, then conditionally routes to the appropriate node, all terminating in END. mcp_tools aren’t used directly here, which is fine since tools are consumed inside nodes.

No issues from my side.

@pavinduLakshan
Copy link
Member

Let's also add the WSO2 open source code license header to all files.

@Bin4yi Bin4yi changed the title pet care ai agent with mcp secured by asgardeo vet-clinic-ai-agent-with-secure-mcp Nov 24, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
Pet_Care_ai_agent_secured_with_asgardeo/README.md (1)

72-81: Replace realistic API key placeholder with clearly fake placeholder.

The example contains OPENAI_API_KEY=sk-proj-abc123xyz789 which matches the pattern of real OpenAI keys and triggers secret scanning tools (Gitleaks). Even as a dummy value, this should be replaced with an obvious placeholder.

Additionally, as suggested in a prior review, these examples should be moved to a separate .env.example file rather than included in the README.

Apply this diff to replace the realistic key:

   # OpenAI Configuration
-  OPENAI_API_KEY=sk-proj-abc123xyz789
+  OPENAI_API_KEY=<your-openai-api-key>

Recommended further step: Create a .env.example file in the project root with all template values, update the README to instruct users to "copy .env.example to .env and update values," and mention that .env should be in .gitignore.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cc9f84 and e34306d.

📒 Files selected for processing (1)
  • Pet_Care_ai_agent_secured_with_asgardeo/README.md (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.29.0)
Pet_Care_ai_agent_secured_with_asgardeo/README.md

[high] 80-80: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 markdownlint-cli2 (0.18.1)
Pet_Care_ai_agent_secured_with_asgardeo/README.md

23-23: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


226-226: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


239-239: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


251-251: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Bin4yi and others added 7 commits November 24, 2025 09:03
Refactored code to remove mock data stores and integrate database functions for user, pet, and vaccination record management.
Added database helper functions and initialization script to README.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
Pet_Care_ai_agent_secured_with_asgardeo/main.py (1)

1-23: Add the standard WSO2 open source license header to this module.

This file currently has no license header; please add the WSO2 OSS license block consistently with the rest of the repo before the imports.

Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (3)

1-34: Add the standard WSO2 open source license header here as well.

This agent entrypoint also lacks the WSO2 OSS license header; please add it above the imports to align with project licensing.


140-209: Remove or redact full SCIM /Me response logging and reconsider ID token decoding without verification.

print(f"🔍 RAW SCIM DATA: {json.dumps(scim_data)}") logs the entire SCIM /Me payload (including user PII) to stdout, which is not acceptable for anything beyond a local demo. In addition, jwt.decode(id_token, options={"verify_signature": False}) skips signature and claim validation.

Recommended changes:

-        if id_token:
-            try:
-                decoded = jwt.decode(id_token, options={"verify_signature": False})
+        if id_token:
+            try:
+                # For production, validate the ID token using your JWKS-based validator.
+                decoded = jwt.decode(id_token, options={"verify_signature": False})
@@
-                    scim_data = scim_resp.json()
-                    
-                    # DEBUG: Print the actual structure so we can see it
-                    print(f"🔍 RAW SCIM DATA: {json.dumps(scim_data)}")
+                    scim_data = scim_resp.json()
+                    # Optional: log only minimal, non-PII metadata in debug mode
+                    # logger.debug("SCIM /Me keys: %s", list(scim_data.keys()))

Please confirm against Asgardeo’s logging/PII guidance for your deployment environment.


300-365: Handle stale active_pet_id safely when multiple pets exist.

In Scenario C you assume active_pet is not None; if active_pet_id is cached but that pet was removed, active_pet['pet_name'] will raise a TypeError and break the request.

Suggested fix (fall back to the “multiple pets, none selected” branch and clear the stale ID):

-    # SCENARIO C: Multiple Pets, BUT one is Selected (Active)
-    elif len(pets) > 1 and active_pet_id:
-        # Find the name of the active pet
-        active_pet = next((p for p in pets if p['pet_id'] == active_pet_id), None)
-        pet_info = f"User has {len(pets)} pets. CURRENT CONTEXT: {active_pet['pet_name']} (ID: {active_pet_id})."
-        specific_instruction = f"Focus ONLY on {active_pet['pet_name']} (ID: {active_pet['pet_id']}) for this response."
+    # SCENARIO C: Multiple Pets, BUT one is Selected (Active)
+    elif len(pets) > 1 and active_pet_id:
+        active_pet = next((p for p in pets if p["pet_id"] == active_pet_id), None)
+        if active_pet:
+            pet_info = (
+                f"User has {len(pets)} pets. CURRENT CONTEXT: "
+                f"{active_pet['pet_name']} (ID: {active_pet_id})."
+            )
+            specific_instruction = (
+                f"Focus ONLY on {active_pet['pet_name']} "
+                f"(ID: {active_pet['pet_id']}) for this response."
+            )
+        else:
+            # Stale ID → treat as ambiguity
+            names = [f"{p['pet_name']} (ID: {p['pet_id']})" for p in pets]
+            pet_info = f"User has {len(pets)} pets: " + ", ".join(names)
+            specific_instruction = (
+                "The user has multiple pets and the previously selected pet is no longer available.\n"
+                "Ask which pet they are referring to before calling tools."
+            )
+            active_pet_id = None
🧹 Nitpick comments (7)
Pet_Care_ai_agent_secured_with_asgardeo/main.py (4)

86-121: Avoid MD5 for synthetic user IDs; use SHA‑256 instead.

The fallback branch uses hashlib.md5 to derive a synthetic user_id from the email, which triggers security tooling (S324) even though it’s only used as an opaque ID. You can get the same behavior with a stronger hash and keep scanners happy.

-            import hashlib
-            hash_object = hashlib.md5(email_clean.encode())
-            user_id_suffix = hash_object.hexdigest()[:8].upper()
+            import hashlib
+            hash_object = hashlib.sha256(email_clean.encode("utf-8"))
+            user_id_suffix = hash_object.hexdigest()[:8].upper()

118-187: Harden exception handling and logging across MCP tool functions.

Most tool handlers catch a bare Exception, log with logger.error, then wrap as ValueError. This makes it harder to distinguish validation issues from system faults and loses stack traces.

Consider a pattern like:

-    except Exception as error:
-        logger.error(f"System error retrieving pets: {error}")
-        raise ValueError(f"Failed to retrieve pets: {str(error)}")
+    except ValueError:
+        # Let explicit validation errors propagate as-is
+        raise
+    except Exception as error:
+        logger.exception("System error retrieving pets")
+        raise ValueError("Failed to retrieve pets") from error

Applying this pattern (or equivalent) to get_user_id_by_email, get_pets_by_user_id, get_pet_vaccination_info, book_vet_appointment, and cancel_appointment will address BLE001/TRY400/B904 and clarify error semantics.

Also applies to: 255-263, 294-297


189-248: Use a collision‑resistant appointment ID generator.

appointment_id = f"APT-{pet_id}-{datetime.now().strftime('%Y%m%d%H%M%S')}" can collide if two bookings are created for the same pet within the same second.

Consider incorporating a random/unique component, e.g.:

+        import uuid
+        unique_suffix = uuid.uuid4().hex[:8].upper()
-        appointment_id = f"APT-{pet_id}-{datetime.now().strftime('%Y%m%d%H%M%S')}"
+        appointment_id = f"APT-{pet_id}-{datetime.now().strftime('%Y%m%d%H%M%S')}-{unique_suffix}"

299-438: Simplify OpenAI client usage and clean up error handling in suggest_pet_names.

This function mixes the module‑level openai_client with checks/mutations of openai.api_key, and the exception handlers have unused variables plus a redundant raise ve. With the modern OpenAI SDK, it’s simpler and safer to rely solely on the instantiated client.

Suggested adjustments:

-        # Ensure OpenAI API key is set from environment if available
-        if OPENAI_API_KEY and not openai.api_key:
-            openai.api_key = OPENAI_API_KEY
-        
-        if not openai.api_key:
-            raise ValueError("OpenAI API key is not configured. Please set OPENAI_API_KEY in environment variables.")
+        if not openai_client:
+            raise ValueError("OpenAI API key is not configured. Please set OPENAI_API_KEY in environment variables.")
@@
-        response = openai_client.chat.completions.create(
+        response = openai_client.chat.completions.create(
@@
-    except openai.APIError as e:
-        error_message = f"OpenAI API error: {str(e)}"
+    except openai.APIError as e:
+        error_message = f"OpenAI API error: {e}"
         logger.error(error_message)
         raise ValueError(f"Failed to generate pet names: {error_message}")
-    except openai.RateLimitError as e:
+    except openai.RateLimitError:
         error_message = "OpenAI API rate limit exceeded. Please try again later."
         logger.error(error_message)
         raise ValueError(error_message)
-    except openai.AuthenticationError as e:
+    except openai.AuthenticationError:
         error_message = "OpenAI API authentication failed. Please check your API key."
         logger.error(error_message)
         raise ValueError(error_message)
-    except ValueError as ve:
-        raise ve
+    except ValueError:
+        # Re-raise validation/JSON errors unchanged
+        raise

Also consider switching logger.error to logger.exception in the JSON decode path so failures include stack traces.

Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (3)

213-287: Avoid bare except and multi‑statement lines in load_context_node JSON parsing.

The parsing blocks use patterns like if hasattr(result, 'content'): result = result.content and try: … except: pass, which trigger E701/E722 and silently drop parse errors.

A minimal cleanup that preserves behavior:

-                data = {}
-                if hasattr(result, 'content'): result = result.content
-                if isinstance(result, str):
-                    try:
-                        clean = result.replace('```json', '').replace('```', '').strip()
-                        data = json.loads(clean)
-                    except: pass
+                data = {}
+                if hasattr(result, "content"):
+                    result = result.content
+                if isinstance(result, str):
+                    try:
+                        clean = result.replace("```json", "").replace("```", "").strip()
+                        data = json.loads(clean)
+                    except json.JSONDecodeError:
+                        # Optionally log at debug level if needed
+                        pass

Apply the same structure in STEP 2 to address Ruff’s warnings and make failures more diagnosable.


451-510: Use structured logging instead of print and narrow the catch‑all in handle_chat_request.

Catching a bare Exception and printing the stack trace to stdout while returning str(e) to the client can leak internal details and makes logs harder to consume in production.

Pattern to consider:

-    except Exception as e:
-        print(f"Error: {e}")
-        import traceback
-        traceback.print_exc()
-        return web.json_response({"error": str(e)}, status=500)
+    except web.HTTPException:
+        raise
+    except Exception as e:
+        # logger.exception("Unexpected error in /chat handler")
+        return web.json_response({"error": "Internal server error"}, status=500)

You can keep print for local demos, but using logging and a generic 500 message is safer for real deployments.


516-522: Be aware that the mock access_token will not pass JWT validation on the secured MCP server.

The fallback path assigns access_token = "mock_token" but the MCP server is configured with a strict JWTTokenVerifier; calls from this agent will fail once the resource server enforces real Asgardeo tokens.

Either document this as “demo‑only” behavior or gate it behind a clearly named development flag/env var so it’s not accidentally used in environments where the FastMCP service expects valid JWTs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b660332 and 5472e53.

📒 Files selected for processing (4)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/frontend_app.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/index.html (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/main.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/frontend_app.py
  • Pet_Care_ai_agent_secured_with_asgardeo/agent/index.html
🧰 Additional context used
🧬 Code graph analysis (1)
Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py (1)
hotel-booking-agent-autogen-agent-iam/staff-management-agent/app/service.py (1)
  • invoke (183-210)
🪛 Ruff (0.14.5)
Pet_Care_ai_agent_secured_with_asgardeo/agent/agent.py

121-121: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


121-121: Create your own exception

(TRY002)


121-121: Avoid specifying long messages outside the exception class

(TRY003)


152-152: Do not catch blind exception: Exception

(BLE001)


200-200: Do not catch blind exception: Exception

(BLE001)


238-238: Multiple statements on one line (colon)

(E701)


243-243: Do not use bare except

(E722)


243-243: try-except-pass detected, consider logging the exception

(S110)


243-243: Multiple statements on one line (colon)

(E701)


249-249: Do not catch blind exception: Exception

(BLE001)


265-265: Multiple statements on one line (colon)

(E701)


270-270: Do not use bare except

(E722)


270-270: try-except-pass detected, consider logging the exception

(S110)


270-270: Multiple statements on one line (colon)

(E701)


283-283: Do not catch blind exception: Exception

(BLE001)


399-399: Do not catch blind exception: Exception

(BLE001)


402-402: Consider [*messages_with_context, response, *tool_outputs] instead of concatenation

Replace with [*messages_with_context, response, *tool_outputs]

(RUF005)


405-405: Consider [response, *tool_outputs, final_response] instead of concatenation

Replace with [response, *tool_outputs, final_response]

(RUF005)


410-410: Unused function argument: state

(ARG001)


412-412: Unused function argument: state

(ARG001)


414-414: Unused function argument: state

(ARG001)


421-421: Multiple statements on one line (colon)

(E701)


422-422: Multiple statements on one line (colon)

(E701)


423-423: Multiple statements on one line (colon)

(E701)


424-424: Multiple statements on one line (colon)

(E701)


427-427: Unused function argument: mcp_tools

(ARG001)


469-469: Consider [*history, HumanMessage(content=user_input)] instead of concatenation

Replace with [*history, HumanMessage(content=user_input)]

(RUF005)


505-505: Do not catch blind exception: Exception

(BLE001)


518-518: Do not catch blind exception: Exception

(BLE001)


520-520: Possible hardcoded password assigned to: "access_token"

(S105)


542-542: Unused lambda argument: r

(ARG005)


548-548: Multiple statements on one line (colon)

(E701)

Pet_Care_ai_agent_secured_with_asgardeo/main.py

57-57: Do not catch blind exception: Exception

(BLE001)


58-58: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


68-68: Avoid specifying long messages outside the exception class

(TRY003)


91-91: Abstract raise to an inner function

(TRY301)


91-91: Avoid specifying long messages outside the exception class

(TRY003)


109-109: Probable use of insecure hash functions in hashlib: md5

(S324)


118-118: Do not catch blind exception: Exception

(BLE001)


119-119: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


120-120: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


120-120: Avoid specifying long messages outside the exception class

(TRY003)


120-120: Use explicit conversion flag

Replace with conversion flag

(RUF010)


132-132: Abstract raise to an inner function

(TRY301)


132-132: Avoid specifying long messages outside the exception class

(TRY003)


155-155: Do not catch blind exception: Exception

(BLE001)


156-156: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


157-157: Avoid specifying long messages outside the exception class

(TRY003)


157-157: Use explicit conversion flag

Replace with conversion flag

(RUF010)


166-166: Abstract raise to an inner function

(TRY301)


166-166: Avoid specifying long messages outside the exception class

(TRY003)


182-182: Consider moving this statement to an else block

(TRY300)


184-184: Do not catch blind exception: Exception

(BLE001)


185-185: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


186-186: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


186-186: Avoid specifying long messages outside the exception class

(TRY003)


186-186: Use explicit conversion flag

Replace with conversion flag

(RUF010)


213-213: Abstract raise to an inner function

(TRY301)


213-213: Avoid specifying long messages outside the exception class

(TRY003)


219-219: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


219-219: Avoid specifying long messages outside the exception class

(TRY003)


254-254: Consider moving this statement to an else block

(TRY300)


258-258: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


259-259: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


259-259: Avoid specifying long messages outside the exception class

(TRY003)


260-260: Do not catch blind exception: Exception

(BLE001)


262-262: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


263-263: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


263-263: Avoid specifying long messages outside the exception class

(TRY003)


280-280: Abstract raise to an inner function

(TRY301)


280-280: Avoid specifying long messages outside the exception class

(TRY003)


292-292: Consider moving this statement to an else block

(TRY300)


294-294: Do not catch blind exception: Exception

(BLE001)


296-296: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


297-297: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


297-297: Avoid specifying long messages outside the exception class

(TRY003)


323-323: Abstract raise to an inner function

(TRY301)


323-323: Avoid specifying long messages outside the exception class

(TRY003)


331-331: Abstract raise to an inner function

(TRY301)


331-331: Avoid specifying long messages outside the exception class

(TRY003)


335-335: Abstract raise to an inner function

(TRY301)


335-335: Avoid specifying long messages outside the exception class

(TRY003)


365-365: Abstract raise to an inner function

(TRY301)


365-365: Avoid specifying long messages outside the exception class

(TRY003)


404-404: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


405-405: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


405-405: Avoid specifying long messages outside the exception class

(TRY003)


419-419: Consider moving this statement to an else block

(TRY300)


422-422: Use explicit conversion flag

Replace with conversion flag

(RUF010)


423-423: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


424-424: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


424-424: Avoid specifying long messages outside the exception class

(TRY003)


425-425: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


427-427: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


428-428: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


429-429: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


431-431: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


432-432: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


434-434: Use raise without specifying exception name

Remove exception name

(TRY201)


435-435: Do not catch blind exception: Exception

(BLE001)


437-437: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


438-438: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


438-438: Avoid specifying long messages outside the exception class

(TRY003)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (4)
Pet_Care_ai_agent_secured_with_asgardeo/README.md (4)

23-33: Add language to project-structure code fence and switch to .env.example

The project-structure block is missing a language identifier (MD040) and still lists .env directly. Consider both tightening lint and aligning with the earlier guidance to use an env template:

-```
+```text
 ├── main.py              # FastMCP server with secured endpoints
@@
-├── .env                # Environment variables configuration
+├── .env.example        # Environment variable template (copy to .env and edit)
@@
-└── README.md          # This file
+└── README.md           # This file

[Suggest adjusting the surrounding text to tell users to copy .env.example to .env and populate values.]


239-274: Add language identifiers to conversation example code blocks

The three sample conversation blocks are fenced with bare ``` and flagged by markdownlint (MD040). Mark them as plain text for clearer rendering:

-### Example 1: Single Pet User
-```
+### Example 1: Single Pet User
+```text
@@
-### Example 2: Multiple Pet User
-```
+### Example 2: Multiple Pet User
+```text
@@
-### Example 3: Booking Appointment
-```
+### Example 3: Booking Appointment
+```text

1-4: Prepend the WSO2 license header to the README

This markdown file should begin with the standard WSO2 Apache 2.0 license HTML comment block, with the year set to 2025, before the # Pawsome Pet Care title. Reuse the header snippet already suggested in earlier review feedback.


72-95: Remove realistic-looking OpenAI API key from example and prefer .env.example

The “Example with actual values” block includes:

OPENAI_API_KEY=sk-proj-abc123xyz789

This still looks like a real key and will keep triggering secret scanners. Use a clearly fake placeholder and, ideally, move all concrete examples into a committed .env.example instead of inline “actual values”:

-   # OpenAI Configuration
-   OPENAI_API_KEY=sk-proj-abc123xyz789
+   # OpenAI Configuration
+   OPENAI_API_KEY=<your-openai-api-key>

Then instruct users to copy .env.example.env and fill in real values locally.

🧹 Nitpick comments (2)
Pet_Care_ai_agent_secured_with_asgardeo/init_database.py (1)

10-16: Optional: clarify/descope destructive DB reset behavior

init_database() deletes any existing pet_clinic.db unconditionally. That’s fine for a demo, but it’s worth either (a) clearly documenting “this will delete any existing data” in the README, or (b) gating it behind a prompt/flag if you expect people to run it repeatedly against non-throwaway data.

Pet_Care_ai_agent_secured_with_asgardeo/database.py (1)

18-21: Use logger.exception to preserve traceback on DB errors

Inside the except block, using logger.exception will log the full traceback, which is more useful for diagnosing DB issues:

-    except Exception as e:
-        conn.rollback()
-        logger.error(f"Database error: {e}")
-        raise
+    except Exception:
+        conn.rollback()
+        logger.exception("Database error")
+        raise

This still re-raises the error but improves observability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5472e53 and 0bda4bc.

📒 Files selected for processing (3)
  • Pet_Care_ai_agent_secured_with_asgardeo/README.md (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/database.py (1 hunks)
  • Pet_Care_ai_agent_secured_with_asgardeo/init_database.py (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
Pet_Care_ai_agent_secured_with_asgardeo/README.md

23-23: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


240-240: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


253-253: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


265-265: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.5)
Pet_Care_ai_agent_secured_with_asgardeo/database.py

20-20: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

Copy link
Author

@Bin4yi Bin4yi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix the changes

@Bin4yi
Copy link
Author

Bin4yi commented Nov 25, 2025

solved the reviews

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants