Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 93 additions & 65 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,82 +134,82 @@ All tools are registered in `mcp_server.py` with `@mcp.tool()` and are discovera

### 👤 Clients (16 tools)

| Tool | Description |
|---|---|
| `search_clients` | Find clients by name, returns clientId |
| `get_client` | Show key profile details for a client |
| `get_client_accts` | List all loan and savings accounts for a client |
| `create_new_client` | Create a new banking client profile |
| `activate_pending_client` | Activate a pending client |
| `update_mobile` | Update a client's mobile number |
| `close_client_profile` | Close a client's profile |
| `get_identifiers` | List client KYC documents |
| `add_identifier` | Add a KYC document to a client |
| `list_documents` | List uploaded files for a client |
| `list_client_charges` | List client-level fees |
| `apply_client_fee` | Apply a one-time charge to a client profile |
| `list_client_txns` | List financial transactions for a client |
| `get_addresses` | Show a client's registered addresses |
| `create_lending_group` | Create a lending group |
| `get_group` | Show group details and members |
| Tool | Description |
| ------------------------- | ----------------------------------------------- |
| `search_clients` | Find clients by name, returns clientId |
| `get_client` | Show key profile details for a client |
| `get_client_accts` | List all loan and savings accounts for a client |
| `create_new_client` | Create a new banking client profile |
| `activate_pending_client` | Activate a pending client |
| `update_mobile` | Update a client's mobile number |
| `close_client_profile` | Close a client's profile |
| `get_identifiers` | List client KYC documents |
| `add_identifier` | Add a KYC document to a client |
| `list_documents` | List uploaded files for a client |
| `list_client_charges` | List client-level fees |
| `apply_client_fee` | Apply a one-time charge to a client profile |
| `list_client_txns` | List financial transactions for a client |
| `get_addresses` | Show a client's registered addresses |
| `create_lending_group` | Create a lending group |
| `get_group` | Show group details and members |

### 👥 Groups & Centers (6 tools)

| Tool | Description |
|---|---|
| `list_all_groups` | List all lending groups |
| Tool | Description |
| ------------------------ | -------------------------------- |
| `list_all_groups` | List all lending groups |
| `activate_pending_group` | Activate a pending lending group |
| `add_member_to_group` | Add a client to a group |
| `list_all_centers` | List all centers |
| `get_center` | Show details for a center |
| `create_new_center` | Create a new center |
| `add_member_to_group` | Add a client to a group |
| `list_all_centers` | List all centers |
| `get_center` | Show details for a center |
| `create_new_center` | Create a new center |

### 💳 Loans (11 tools)

| Tool | Description |
|---|---|
| `get_loan` | Get key details for a loan |
| `get_repayment_sched` | Get the repayment schedule |
| `get_loan_hist` | Get full transaction history for a loan |
| `get_overdue_loans_for_client` | List overdue loans for a client |
| `create_new_loan` | Create a new individual loan application |
| `create_group_loan_app` | Create a group loan application |
| `approve_disburse_loan` | Approve and disburse a pending loan |
| `reject_loan` | Reject a pending loan application |
| `make_repayment` | Make a repayment on an active loan |
| `apply_loan_fee` | Apply a fee/charge to a loan |
| `waive_loan_interest` | Waive interest on a loan |
| Tool | Description |
| ------------------------------ | ---------------------------------------- |
| `get_loan` | Get key details for a loan |
| `get_repayment_sched` | Get the repayment schedule |
| `get_loan_hist` | Get full transaction history for a loan |
| `get_overdue_loans_for_client` | List overdue loans for a client |
| `create_new_loan` | Create a new individual loan application |
| `create_group_loan_app` | Create a group loan application |
| `approve_disburse_loan` | Approve and disburse a pending loan |
| `reject_loan` | Reject a pending loan application |
| `make_repayment` | Make a repayment on an active loan |
| `apply_loan_fee` | Apply a fee/charge to a loan |
| `waive_loan_interest` | Waive interest on a loan |

### 💰 Savings (9 tools)

| Tool | Description |
|---|---|
| `get_savings` | Get key details of a savings account |
| `get_savings_txns` | Get transactions for a savings account |
| `create_savings` | Create a new savings account |
| `approve_activate_savings` | Approve and activate a savings account |
| `close_savings` | Close a savings account |
| `deposit` | Deposit money into a savings account |
| `withdraw` | Withdraw money from a savings account |
| `apply_savings_fee` | Apply a charge to a savings account |
| `calc_post_interest` | Post accrued interest to a savings account |
| Tool | Description |
| -------------------------- | ------------------------------------------ |
| `get_savings` | Get key details of a savings account |
| `get_savings_txns` | Get transactions for a savings account |
| `create_savings` | Create a new savings account |
| `approve_activate_savings` | Approve and activate a savings account |
| `close_savings` | Close a savings account |
| `deposit` | Deposit money into a savings account |
| `withdraw` | Withdraw money from a savings account |
| `apply_savings_fee` | Apply a charge to a savings account |
| `calc_post_interest` | Post accrued interest to a savings account |

### 🏢 Staff & Offices (4 tools)

| Tool | Description |
|---|---|
| `list_all_staff` | List bank staff members |
| `get_staff` | Get details for a staff member |
| `list_all_offices` | List all bank branches/offices |
| `get_office` | Get details for a specific office |
| Tool | Description |
| ------------------ | --------------------------------- |
| `list_all_staff` | List bank staff members |
| `get_staff` | Get details for a staff member |
| `list_all_offices` | List all bank branches/offices |
| `get_office` | Get details for a specific office |

### 📊 Accounting (3 tools)

| Tool | Description |
|---|---|
| `list_accounts` | List GL accounts (Chart of Accounts) |
| Tool | Description |
| ---------------------- | ------------------------------------------------- |
| `list_accounts` | List GL accounts (Chart of Accounts) |
| `list_journal_entries` | List journal entries by account or transaction ID |
| `record_journal_entry` | Record a manual debit/credit journal entry |
| `record_journal_entry` | Record a manual debit/credit journal entry |

---

Expand Down Expand Up @@ -252,12 +252,12 @@ By keeping this server "pure," you gain:

## Environment Variables

| Variable | Description | Default |
|---|---|---|
| `MIFOSX_BASE_URL` | Full base URL of your Fineract REST API | — |
| `MIFOSX_TENANT_ID` | Fineract tenant identifier | `default` |
| `MIFOSX_USERNAME` | Fineract API username | `mifos` |
| `MIFOSX_PASSWORD` | Fineract API password | `password` |
| Variable | Description | Default |
| ------------------ | --------------------------------------- | ---------- |
| `MIFOSX_BASE_URL` | Full base URL of your Fineract REST API | — |
| `MIFOSX_TENANT_ID` | Fineract tenant identifier | `default` |
| `MIFOSX_USERNAME` | Fineract API username | `mifos` |
| `MIFOSX_PASSWORD` | Fineract API password | `password` |

---

Expand All @@ -275,6 +275,7 @@ DANGEROUSLY_OMIT_AUTH=true npx @modelcontextprotocol/inspector python mcp_server
```

This will:

- Start the MCP Inspector proxy on `localhost:6277`
- Open the Inspector UI at `http://localhost:6274`
- Automatically spawn `mcp_server.py` as the stdio server
Expand Down Expand Up @@ -308,6 +309,7 @@ python test_tools.py
```

Expected output:

```
✅ PASS — All 49 tools registered with unique names
✅ list_offices : OK
Expand Down Expand Up @@ -377,3 +379,29 @@ Contributions to expand Fineract API coverage are welcome. To add a new tool:
## License

This project is licensed under the [Mozilla Public License 2.0](LICENSE).

## Suggestion Engine (Experimental)

This repository includes an experimental `suggestion_engine.py` module that demonstrates how context-aware next-step suggestions can be generated based on MCP tool outputs.

⚠️ Important:

- The suggestion engine is NOT integrated into MCP tool responses.
- MCP server remains a pure execution layer (no decision-making logic).
- Suggestions are intended to be generated at the client/LLM layer.

### Why?

Keeping suggestions outside the MCP layer ensures:

- Clean separation of concerns
- Reduced response size
- Better flexibility for AI agents to decide next actions

### Future Scope

Suggestion logic can be integrated into:

- AI Assistants (LLM layer)
- Chat-based banking copilots
- Workflow automation systems
63 changes: 63 additions & 0 deletions python/core/suggestion_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
def generate_suggestions(intent, data):
"""
Generate context-aware suggestions based on MCP tool responses.
"""

suggestions: List[str] = []
Comment on lines +1 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add mypy-compatible type annotations.

generate_suggestions has untyped parameters/return, and List is referenced without an import. Add explicit types and import the typing symbols used.

Proposed fix
+from typing import Any, List
+
-def generate_suggestions(intent, data):
+def generate_suggestions(intent: str, data: Any) -> List[str]:
     """
     Generate context-aware suggestions based on MCP tool responses.
     """
 
     suggestions: List[str] = []

As per coding guidelines, “Type Safety: Flag any new function signatures, complex variable assignments, or class attributes that are missing mypy-compatible type hints.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/core/suggestion_engine.py` around lines 1 - 6, The function
generate_suggestions is missing mypy-compatible annotations and references List
without importing it; update the signature to include concrete types (e.g.,
intent: str, data: Dict[str, Any]) and a return type of List[str], and add the
necessary typing imports (from typing import List, Dict, Any) at the top of the
file so the variable suggestions: List[str] and the function signature are fully
typed for mypy.


# 🔹 Case 1: Overdue loans
if intent == "get_overdue_loans":

# Extract loan list safely
if isinstance(data, dict):
if "error" in data:
return [] # Don't generate suggestions for error responses
loans = data.get("overdueLoans", [])
else:
loans = data or []

# Ensure it's iterable list
if not isinstance(loans, list):
return []

for loan in loans:
if not isinstance(loan, dict):
continue

loan_id = loan.get("loanId") or loan.get("id")

if not loan_id:
continue

suggestions.append(f"Apply a late fee to loan {loan_id}")
suggestions.append(f"View repayment schedule for loan {loan_id}")
suggestions.append(f"Send a repayment reminder for loan {loan_id}")

# 🔹 Case 2: Loan details
elif intent == "get_loan_details":

if not isinstance(data, dict):
return suggestions

if "error" in data:
return suggestions

loan_id = data.get("loanId")

# Safe normalization (prevents None.lower() crash)
status = (data.get("status") or "").lower()

if not loan_id:
return suggestions

# Active loan actions
if "active" in status:
suggestions.append(f"Make a repayment for loan {loan_id}")
suggestions.append(f"View repayment schedule for loan {loan_id}")

# Pending/submitted actions
if "pending" in status or "submitted" in status:
suggestions.append(f"Approve loan {loan_id}")
suggestions.append(f"Reject loan {loan_id}")

return suggestions
30 changes: 28 additions & 2 deletions python/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,16 @@ def get_addresses(clientId: int) -> dict:
@mcp.tool()
def get_loan(loanId: int) -> dict:
"""Get key details of a specific loan."""

data = get_loan_details.func(loanId)

if not isinstance(data, dict):
return data

tl = data.get("timeline", {})
return {

# 🔹 Step 1: Prepare clean response
response = {
"loanId": data.get("id"),
"accountNo": data.get("accountNo"),
"productName": data.get("loanProductName"),
Expand All @@ -310,6 +315,14 @@ def get_loan(loanId: int) -> dict:
"repaymentFrequency": f"Every {data.get('repaymentEvery')} {data.get('repaymentFrequencyType', {}).get('value','')}",
}

# 🔹 Step 2: Generate suggestions
suggestions = generate_suggestions("get_loan_details", response)

# 🔹 Step 3: Return enhanced response
return {
"data": response,
"suggestions": suggestions
}
Comment on lines +318 to +325

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Do not wrap loan responses with server-side suggestions.

generate_suggestions is not imported in this file, so get_loan raises NameError. This also changes the tool contract from top-level loan fields to {data, suggestions} despite the documented MCP-only execution scope.

Proposed fix
-    # 🔹 Step 2: Generate suggestions
-    suggestions = generate_suggestions("get_loan_details", response)
-
-    # 🔹 Step 3: Return enhanced response
-    return {
-        "data": response,
-        "suggestions": suggestions
-    }
+    return response

As per coding guidelines, “Ensure mcp_server.py, mcp_adapter.py, and registry.py properly decouple the transport protocol from the business schemas.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 🔹 Step 2: Generate suggestions
suggestions = generate_suggestions("get_loan_details", response)
# 🔹 Step 3: Return enhanced response
return {
"data": response,
"suggestions": suggestions
}
return response
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/mcp_server.py` around lines 318 - 325, The current get_loan handler
calls an undefined generate_suggestions and wraps the loan payload into {"data",
"suggestions"}, changing the expected MCP schema and causing a NameError; revert
get_loan to return the original loan response object directly (remove the
generate_suggestions call and the {"data","suggestions"} wrapper) so the
top-level loan fields remain unchanged, and do not import or reference
generate_suggestions in mcp_server.py; also ensure corresponding
adapters/registry code (e.g., mcp_adapter and registry integration points)
remain decoupled from any transport-level "suggestions" envelope so business
schemas stay unchanged.

@mcp.tool()
def get_repayment_sched(loanId: int) -> dict:
"""Get the repayment schedule for a loan."""
Expand Down Expand Up @@ -414,10 +427,23 @@ def waive_loan_interest(loanId: int, amount: float, note: str = "AI Authorized W
return waive_interest.func(loanId, amount, note)

@mcp.tool()
def get_overdue_loans_for_client(clientId: int) -> dict:
def get_overdue_loans_for_client(clientId: int) -> list:
"""Get all overdue or in-arrears loans for a client"""

# Step 1: Get actual data

return get_overdue_loans.func(clientId)

# 🔹 Step 2: Generate smart suggestions
suggestions = generate_suggestions("get_overdue_loans", result)

# 🔹 Step 3: Return enhanced response
# ✅ correct
return {
**result,
"suggestions": suggestions
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

@mcp.tool()
def create_group_loan_app(groupId: int, principal: float, months: int, productId: int = 1) -> dict:
"""Create a group loan application for an existing lending group"""
Expand Down
Loading