from typing import Optional
from uuid import UUID
from typing import Optional
from mcp.server.fastmcp import FastMCP
from marlo_mcp.client import MarloMCPClient
from marlo_mcp.client.schema import BillQueryParams, CreateVesselSchema, ListInvoiceParams, SearchInputData, VoyageProfitAndLoss, VesselValuationRequestSchema
mcp = FastMCP("marlo-mcp")
@mcp.tool(description="Get vessel all available vessels with minimal vessel details")
async def get_vessels():
"""Get all available vessels"""
async with MarloMCPClient() as client:
return await client.get("vessels")
@mcp.tool(description="Get all available fleets")
async def get_fleets():
"""Get all available fleets"""
async with MarloMCPClient() as client:
return await client.get("fleets")
@mcp.tool(description="Get fleet details")
async def get_fleet_details(fleet_id: UUID):
"""Get details of a specific fleet"""
async with MarloMCPClient() as client:
return await client.get(f"fleet/{fleet_id}")
@mcp.tool(description="Get vessel details")
async def get_vessel_details(vessel_id: UUID):
"""Get details of a specific vessel"""
async with MarloMCPClient() as client:
return await client.get(f"vessel/{vessel_id}")
@mcp.tool(description="create a new vessel")
async def create_vessel(vessel: CreateVesselSchema):
"""Create a new vessel"""
async with MarloMCPClient() as client:
return await client.post("vessel", data=vessel.model_dump())
@mcp.tool(description="Search multiple ports")
async def search_ports(port_names: list[str]):
"""Search for multiple ports"""
async with MarloMCPClient() as client:
return await client.post("ports", data={"port_names": port_names})
@mcp.tool(description="Search cargos")
async def search_cargos(cargo_name: str):
"""Search for cargos"""
async with MarloMCPClient() as client:
return await client.post("cargos", data={"cargo_name": cargo_name})
@mcp.tool(description="Get all available charter specialists")
async def get_all_charter_specialists():
"""Get all available charter specialists"""
async with MarloMCPClient() as client:
return await client.get("charter-specialists")
@mcp.tool(description="Search charterer contacts")
async def search_charterer_contacts(charterer_name: str):
"""Search for charterer contacts"""
async with MarloMCPClient() as client:
return await client.post("charterer-contacts", data={"charterer_name": charterer_name})
@mcp.tool(description="Get all voyages")
async def get_all_voyages():
"""Get all voyages"""
async with MarloMCPClient() as client:
return await client.get("voyages")
@mcp.tool(description="Get voyage details")
async def get_voyage_details(voyage_id: UUID):
"""Get details of a specific voyage"""
async with MarloMCPClient() as client:
return await client.get(f"voyage/{voyage_id}")
@mcp.tool(description="Get voyage profit and loss")
async def get_voyage_profit_and_loss(data: VoyageProfitAndLoss):
"""Get profit and loss of a specific voyage"""
async with MarloMCPClient() as client:
return await client.post(f"voyage/{data.voyage_id}/profit-and-loss", data=data.model_dump())
@mcp.tool(description="Get all estimates sheet")
async def get_all_estimates_sheet():
"""Get all estimates sheet"""
async with MarloMCPClient() as client:
return await client.get("estimate-sheets")
@mcp.tool(description="get details of a specific estimate sheet")
async def get_estimate_sheet_details(estimate_sheet_id: UUID):
"""Get details of a specific estimate sheet"""
async with MarloMCPClient() as client:
return await client.get(f"estimate-sheet/{estimate_sheet_id}")
@mcp.tool(description="Get all cargo books")
async def get_all_cargo_books():
"""Get all cargo books"""
async with MarloMCPClient() as client:
return await client.get("cargo-books")
@mcp.tool(description="Get cargo book details")
async def get_cargo_book_details(cargo_book_id: UUID):
"""Get details of a specific cargo book"""
async with MarloMCPClient() as client:
return await client.get(f"cargo-book/{cargo_book_id}")
@mcp.tool(description="list all vessel fixtures")
async def list_all_vessel_fixtures():
"""List all vessel fixtures"""
async with MarloMCPClient() as client:
return await client.get("vessel-fixtures")
@mcp.tool(description="get details of a specific vessel fixture")
async def get_vessel_fixture_details(vessel_fixture_id: UUID):
"""Get details of a specific vessel fixture"""
async with MarloMCPClient() as client:
return await client.get(f"vessel-fixture/{vessel_fixture_id}")
@mcp.tool(description="get voyage contacts")
async def get_voyage_contacts(voyage_id: UUID):
"""Get voyage contacts"""
async with MarloMCPClient() as client:
return await client.get(f"voyage/{voyage_id}/contacts")
@mcp.tool(description="Get financial details (bills, invoices, payments, etc.) for voyage contacts")
async def get_voyage_contacts_financial_details(voyage_id: UUID, contact_id: str, contact_type: str):
"""Get financial details (bills, invoices, payments, etc.) for voyage contacts"""
async with MarloMCPClient() as client:
return await client.get(f"voyage/{voyage_id}/contact/{contact_id}/finance?contact_type={contact_type}")
@mcp.tool(description="get contacts for a vessel fixture")
async def get_vessel_fixture_contacts(vessel_fixture_id: UUID):
"""Get contacts for a vessel fixture"""
async with MarloMCPClient() as client:
return await client.get(f"time-chartered/{vessel_fixture_id}/contacts")
@mcp.tool(description="Get vessel fixture contacts financial details (bills, invoices, payments, etc.)")
async def get_vessel_fixture_contacts_financial_details(vessel_fixture_id: UUID, contact_id: str, contact_type: str):
"""Get financial details (bills, invoices, payments, etc.) for vessel fixture contacts"""
async with MarloMCPClient() as client:
return await client.get(f"time-chartered/{vessel_fixture_id}/contact/{contact_id}/finance?contact_type={contact_type}")
@mcp.tool(description="Get a invoice details")
async def get_invoice_details(invoice_id: str):
"""Get a invoice details"""
async with MarloMCPClient() as client:
return await client.get(f"invoice/{invoice_id}")
@mcp.tool(description="Get a bill details")
async def get_bill_details(bill_id: str):
"""Get a bill details"""
async with MarloMCPClient() as client:
return await client.get(f"bill/{bill_id}")
@mcp.tool(description="voyage port disbursements")
async def voyage_port_disbursements(voyage_id: UUID):
"""Get voyage port disbursements"""
async with MarloMCPClient() as client:
return await client.get(f"voyage/{voyage_id}/port-disbursements")
@mcp.tool(description="Get voyage laytime")
async def get_voyage_laytime(voyage_id: UUID):
"""Get voyage laytime"""
async with MarloMCPClient() as client:
return await client.get(f"laytime/{voyage_id}")
@mcp.tool(description="list all customers")
async def list_all_customers(page: int = 1, per_page: int = 100, search: str = None):
"""List all customers"""
async with MarloMCPClient() as client:
return await client.get("customers", params={"page": page, "per_page": per_page, "search": search})
@mcp.tool(description="list all vendors")
async def list_all_vendors(page: int = 1, per_page: int = 100, search: str = None):
"""List all vendors"""
async with MarloMCPClient() as client:
return await client.get("vendors", params={"page": page, "per_page": per_page, "search": search})
@mcp.tool(description="list all lendors")
async def list_all_lendors(page: int = 1, per_page: int = 100, search: str = None):
"""List all lendors"""
async with MarloMCPClient() as client:
return await client.get("lendors", params={"page": page, "per_page": per_page, "search": search})
@mcp.tool(description="get customer details")
async def get_customer_details(customer_id: str):
"""Get customer details"""
async with MarloMCPClient() as client:
return await client.get(f"customer/{customer_id}")
@mcp.tool(description="get vendor details")
async def get_vendor_details(vendor_id: str):
"""Get vendor details"""
async with MarloMCPClient() as client:
return await client.get(f"vendor/{vendor_id}")
@mcp.tool(description="list all bills")
async def list_all_bills(data: BillQueryParams):
"""List all bills"""
async with MarloMCPClient() as client:
return await client.get("bills", params=data.model_dump())
@mcp.tool(description="list all invoices")
async def list_all_invoices(data: ListInvoiceParams):
"""List all invoices"""
async with MarloMCPClient() as client:
return await client.get("invoices", params=data.model_dump())
@mcp.tool(description="get journal entries")
async def get_journal_entries():
"""Get journal entries"""
async with MarloMCPClient() as client:
return await client.get("journal-entries")
@mcp.tool(description="list all vendor credits")
async def list_all_vendor_credits():
"""List all vendor credits"""
async with MarloMCPClient() as client:
return await client.get("vendor-credit-notes")
@mcp.tool(description="get vendor credit details")
async def get_vendor_credit_details(vendor_credit_id: str):
"""Get vendor credit details"""
async with MarloMCPClient() as client:
return await client.get(f"vendor-credit-notes/{vendor_credit_id}")
@mcp.tool(description="list all credit notes")
async def list_all_credit_notes():
"""List all credit notes"""
async with MarloMCPClient() as client:
return await client.get("credit-notes")
@mcp.tool(description="get credit note details")
async def get_credit_note_details(credit_note_id: str):
"""Get credit note details"""
async with MarloMCPClient() as client:
return await client.get(f"credit-notes/{credit_note_id}")
@mcp.tool(description="list all exteral loans")
async def list_all_external_loans():
"""List all external loans"""
async with MarloMCPClient() as client:
return await client.get("external-loans")
@mcp.tool(description="get external loan details")
async def get_external_loan_details(application_id: str):
"""Get external loan details"""
async with MarloMCPClient() as client:
return await client.get(f"external-loans/{application_id}")
@mcp.tool(description="list all marlo loans")
async def list_all_marlo_loans():
"""List all marlo loans"""
async with MarloMCPClient() as client:
return await client.get("loans")
@mcp.tool(description="get marlo loan details")
async def get_marlo_loan_details(application_id: str):
"""Get marlo loan details"""
async with MarloMCPClient() as client:
return await client.get(f"loans/{application_id}")
@mcp.tool(description="List all transactions")
async def list_all_transactions():
"""List all transactions"""
async with MarloMCPClient() as client:
return await client.get("/transactions")
@mcp.tool(description="get market rates")
async def get_market_rates():
"""
Fetch Market rates.
"""
async with MarloMCPClient() as client:
return await client.get(f"market-rates")
@mcp.tool(description="get market rate details")
async def get_market_rate_details(api_identifier: str):
"""
Fetch Market rate details.
Parameters:
- api_identifier: The identifier of the market rate (e.g., 'IDS9SQAME2W2VRO93ON6HOL9TOI04E7S')
"""
async with MarloMCPClient() as client:
return await client.get(f"market-rates/{api_identifier}")
@mcp.tool(description="get covenant")
async def get_covenant():
"""
Fetch Covenant.
"""
async with MarloMCPClient() as client:
return await client.get(f"covenant")
@mcp.tool(description="get credit score")
async def get_credit_score():
"""
Fetch Credit Score
"""
async with MarloMCPClient() as client:
return await client.get(f"credit-score")
@mcp.tool(description="get interest rates")
async def get_interest_rates():
"""
Fetch Interest Rates
"""
async with MarloMCPClient() as client:
return await client.get(f"interest-rates")
@mcp.tool(description="list all sanctions case manager")
async def list_all_sanctions_case_manager(page: int, per_page: int, schema: Optional[str] = None):
"""List all sanctions case manager"""
async with MarloMCPClient() as client:
return await client.get(f"list-sanction-case-manager", params={"page": page, "per_page": per_page, "schema": schema})
@mcp.tool(description="get sanctions case manager details")
async def get_sanctions_case_manager_details(source_id: str):
"""Get sanctions case manager details"""
async with MarloMCPClient() as client:
return await client.get(f"sanction-case-manager", params={"source_id": source_id})
@mcp.tool(description="search sanctions")
async def search_sanctions(request: SearchInputData):
"""Search sanctions"""
async with MarloMCPClient() as client:
return await client.post(f"sanctions", data=request.model_dump())
@mcp.tool(description="search individual sanction")
async def search_individual_sanction(source_id: str):
"""Search individual sanction"""
async with MarloMCPClient() as client:
return await client.get(f"sanction/{source_id}")
@mcp.tool(description="list all bank accounts")
async def list_all_bank_accounts():
"""List all bank accounts"""
async with MarloMCPClient() as client:
return await client.get("bank-accounts")
@mcp.tool(description="list all bank transactions")
async def list_all_bank_transactions(
bank_id: str,
status: Optional[str] = None,
date_range: Optional[str] = None,
amount_min: Optional[float] = None,
amount_max: Optional[float] = None,
base_type: Optional[list[str]] = None,
search: Optional[str] = None,
currency_code: Optional[str] = None,
page: int = 1,
per_page: int = 10
):
"""List all bank transactions with optional filters and pagination
Args:
bank_id: Bank account ID (required). Use list_all_bank_accounts to get available bank IDs
status: Filter by status (MATCHED, UNMATCHED)
date_range: Date range in format yyyy-MM-dd,yyyy-MM-dd
amount_min: Minimum amount filter
amount_max: Maximum amount filter
base_type: Filter by base_type (DEBIT, CREDIT). Multiple values allowed
search: Search term to filter transactions by (searches in transaction, invoice, bill fields)
currency_code: Filter by currency code (e.g., USD, EUR)
page: Page number (default 1)
per_page: Items per page (default 10, max 100)
"""
async with MarloMCPClient() as client:
params = {
"page": page,
"per_page": per_page,
"bank_id": bank_id
}
if status:
params["status"] = status
if date_range:
params["date_range"] = date_range
if amount_min is not None:
params["amount_min"] = amount_min
if amount_max is not None:
params["amount_max"] = amount_max
if base_type:
params["base_type"] = base_type
if search:
params["search"] = search
if currency_code:
params["currency_code"] = currency_code
return await client.get("bank-transactions", params=params)
@mcp.tool(description="get profit and loss data")
async def get_profit_loss(
period_length: str,
periods_to_compare: str,
start_month: str
):
"""Get profit and loss data with specified parameters
Args:
period_length: Length of each period (e.g., "1" for monthly)
periods_to_compare: Number of periods to compare (e.g., "6" for 6 months)
start_month: Starting month in format "MMM YYYY" (e.g., "Aug 2025")
"""
async with MarloMCPClient() as client:
payload = {
"periodLength": period_length,
"periodsToCompare": periods_to_compare,
"startMonth": start_month
}
return await client.post("profit-loss", data=payload)
@mcp.tool(description="get balance sheet data")
async def get_balance_sheet(
period_length: str,
periods_to_compare: str,
start_month: str
):
"""Get balance sheet data with specified parameters
Args:
period_length: Length of each period (e.g., "1" for monthly)
periods_to_compare: Number of periods to compare (e.g., "6" for 6 months)
start_month: Starting month in format "MMM YYYY" (e.g., "Aug 2025")
"""
async with MarloMCPClient() as client:
payload = {
"periodLength": period_length,
"periodsToCompare": periods_to_compare,
"startMonth": start_month
}
return await client.post("balance-sheet", data=payload)
@mcp.tool(description="Get a global search vessel valuation list")
async def get_global_search_vessel_list():
"""Get a global search vessel valuation list"""
async with MarloMCPClient() as client:
return await client.get("/global-vessel-search-list")
@mcp.tool(description="Get vessel valuation")
async def get_vessel_valuation(data: VesselValuationRequestSchema):
"""Get vessel valuation using DCF method"""
async with MarloMCPClient() as client:
return await client.post("vessel-valuation", data=data.model_dump())
@mcp.tool(description="List approval transactions")
async def list_approval_transactions():
"""List approval transactions"""
async with MarloMCPClient() as client:
return await client.get("/approval-transactions")
@mcp.tool(description="Get a global account currency balance")
async def get_global_account_currency_balance():
"""Get a global account currency balance"""
async with MarloMCPClient() as client:
return await client.get("/balances/current")
@mcp.tool(description="List all global accounts")
async def list_all_global_accounts():
"""List all global accounts"""
async with MarloMCPClient() as client:
return await client.get("/list-global-accounts")
@mcp.tool(description="List all payouts")
async def list_all_payouts():
"""List all payouts"""
async with MarloMCPClient() as client:
return await client.get("/list-payouts")
@mcp.tool(description="Get payout details")
async def get_payout_details(payout_id: str):
"""Get payout details"""
async with MarloMCPClient() as client:
return await client.get(f"/payout/{payout_id}")
@mcp.tool(description="Get a valuation of company")
async def get_company_valuation():
"""Get a valuation of company"""
async with MarloMCPClient() as client:
return await client.get("/company-valuation")
@mcp.tool(description="Get operational cashflow data")
async def get_operational_cashflow(
period_length: str,
periods_to_compare: str,
start_month: str
):
"""Get operational cashflow data with specified parameters
Args:
period_length: Length of each period (e.g., "1" for monthly)
periods_to_compare: Number of periods to compare (e.g., "6" for 6 months)
start_month: Starting month in format "MMM YYYY" (e.g., "Aug 2025")
"""
async with MarloMCPClient() as client:
payload = {
"periodLength": period_length,
"periodsToCompare": periods_to_compare,
"startMonth": start_month
}
return await client.post("operational-cashflow", data=payload)
@mcp.tool(description="Get cashbalance streams data")
async def get_cashbalance_streams():
"""Get cashbalance streams data"""
async with MarloMCPClient() as client:
return await client.get("cashbalance-streams")
@mcp.tool(description="Get summary of all borrowers with their loan statistics and portfolio totals")
async def get_all_borrowers_summary_for_lender():
"""
Get summary of all borrowers with their loan statistics and portfolio totals.
**What this tool does**:
Returns a complete list of all borrower companies with aggregated statistics for the lender's
entire loan portfolio including total amounts, vessel counts, alerts, and risk metrics.
**Use this tool to**:
- List all borrower companies the lender has loaned to
- See total loan amount across all borrowers ($ formatted)
- Count total active/outstanding loans
- View total vessels (fleet size) across all borrowers
- Check alerts by severity level (Low, Medium, High)
- Get portfolio-level metrics (rating, risk concentration)
- Obtain borrower company IDs for detailed queries
**Authentication**: Uses authenticated lender's credentials from token (automatic).
**Response includes**:
```
{
"total_loan_amount": "$10,500,000.00",
"total_loans": 15,
"total_vessels": 8,
"alerts": {"low": 2, "medium": 3, "high": 1},
"borrower_companies": [
{
"company_id": "abc123", // Use this ID for get_lender_dashboard_detail
"company_name": "Marine Shipping Co",
"status": "Active",
"loan_count": 3,
"vessel_count": 2,
"vessel_valuation": "$5,200,000",
"ltv_ratio": "65%",
"outstanding_amount": "$3,380,000",
"outstanding_dues": {
"loan_id_1": {"principal": 1000000, "interest": 50000, "fees": 5000},
"loan_id_2": {"principal": 2000000, "interest": 100000, "fees": 10000}
},
"covenant_violations": 0
}
],
"portfolio_rating": "A",
"high_risk_concentration": "12%"
}
```
**Next step**: Use `company_id` from any borrower to call `get_borrower_financial_details_for_lender(company_id)`
for comprehensive financial analysis.
**Returns**: Complete borrower list with portfolio-level aggregations
"""
async with MarloMCPClient() as client:
return await client.get(
"lender/all-borrowers-summary"
)
@mcp.tool(description="Get comprehensive financial analysis and loan details for a specific borrower company")
async def get_borrower_financial_details_for_lender(borrower_company_id: str):
"""
Get comprehensive financial analysis and loan details for a specific borrower company.
**What this tool does**:
Provides deep financial metrics, credit analysis, vessel-by-vessel loan breakdown,
and contact information for a single borrower company.
**IMPORTANT - How to get borrower_company_id**:
1. First call: `get_all_borrowers_summary_for_lender()`
2. From the response, find the borrower in the `borrower_companies` array
3. Extract the `company_id` field from that borrower object
4. Use that ID as `borrower_company_id` parameter here
**Use this tool to**:
- Analyze borrower's financial health and creditworthiness
- Review loan performance and outstanding amounts
- Assess collateral value and LTV ratios
- Get vessel-by-vessel loan breakdown
- Check covenant compliance and risk level
- Access borrower contact information
- Monitor changes in financial ratios (LTV, DSCR)
**Required Parameter**:
- `borrower_company_id` (string): The company ID obtained from get_all_borrowers_summary_for_lender
**Response includes**:
```
{
// Financial Ratios
"ltv_ratio": "65.0%",
"ltv_change": "-2.5%", // Improvement from previous period
"dscr": "1.45", // Debt Service Coverage Ratio
"current_ratio": "2.1", // Current assets / liabilities
// Credit & Risk
"marlo_credit_score": 720,
"credit_rating": "A", // A+, A, B, C, D, or F
"risk_profile": "Low Risk",
// Amounts
"total_outstanding": "$3,380,000.00",
"external_loan_outstanding": "$500,000.00",
"company_valuation": "$5,200,000.00",
// Company Info
"client_contact_details": {
"owner_name": "John Smith",
"business_type": "Shipping Company",
"contact_person": "Jane Doe",
"email": "jane@marineshipping.com"
},
// Vessel-level Breakdown
"vessels": [
{
"vessel_id": "v123",
"vessel_name": "Pacific Star",
"loan_count": 2,
"total_loan_amount": "$2,000,000",
"loan_status": "Active",
"market_value": "$3,000,000",
"sanction_status": "Clear",
"outstanding_dues": {
"principal": 1800000,
"interest": 90000,
"fees": 10000
}
}
]
}
```
**Typical workflow**:
1. List all borrowers: `get_all_borrowers_summary_for_lender()`
2. Select borrower of interest, get their `company_id`
3. Get detailed analysis: `get_borrower_financial_details_for_lender(company_id)`
**Args**:
borrower_company_id: The borrower's company ID from get_all_borrowers_summary_for_lender response
**Returns**: Comprehensive financial analysis with vessel-level loan breakdown
"""
async with MarloMCPClient() as client:
return await client.get(
"lender/borrower-details",
params={"borrower_company_id": borrower_company_id}
)
def main():
mcp.run()
if __name__ == "__main__":
main()