|
|
@@ -0,0 +1,179 @@
|
|
|
+import json
|
|
|
+import os
|
|
|
+from mcp.server.fastmcp import FastMCP
|
|
|
+
|
|
|
+_here = os.path.dirname(__file__)
|
|
|
+
|
|
|
+with open(os.path.join(_here, "config.json")) as f:
|
|
|
+ _config = json.load(f)
|
|
|
+
|
|
|
+SERVICES = _config['services']
|
|
|
+SALES_TAX = _config['global']['taxes']['sales']
|
|
|
+
|
|
|
+mcp = FastMCP("room-price-calc")
|
|
|
+
|
|
|
+def _dollar(v: float) -> str:
|
|
|
+ return "{:.2f}".format(round(v, 2))
|
|
|
+
|
|
|
+def _details_fmt(details: dict) -> dict:
|
|
|
+ return {k.replace('_', ' ').title(): _dollar(v) for k, v in details.items()}
|
|
|
+
|
|
|
+def _resolve_service(service: str):
|
|
|
+ svc = SERVICES.get(service.lower().strip())
|
|
|
+ if svc is None:
|
|
|
+ keys = ', '.join(SERVICES.keys())
|
|
|
+ raise ValueError(f"Unknown service '{service}'. Valid keys: {keys}")
|
|
|
+ return svc
|
|
|
+
|
|
|
+def _n2l(svc, net, det):
|
|
|
+ comm_rate = svc.get('fees', {}).get('commission', 0)
|
|
|
+ wrong_rate = svc.get('taxes', {}).get('wrong', SALES_TAX)
|
|
|
+ list_ = net / (1 - comm_rate - SALES_TAX + wrong_rate)
|
|
|
+ if 'fees' in svc and 'commission' in svc['fees']:
|
|
|
+ det['commission'] = list_ * comm_rate
|
|
|
+ if 'taxes' in svc and 'wrong' in svc['taxes']:
|
|
|
+ det['wrong_tax'] = list_ * wrong_rate
|
|
|
+ det['sales_tax'] = list_ * SALES_TAX
|
|
|
+ det['wrong_tax_loss'] = det['sales_tax'] - det['wrong_tax']
|
|
|
+ return round(list_, 2)
|
|
|
+
|
|
|
+def _l2n(svc, list_, det):
|
|
|
+ net = list_
|
|
|
+ if 'fees' in svc and 'commission' in svc['fees']:
|
|
|
+ det['commission'] = net * svc['fees']['commission']
|
|
|
+ if 'taxes' in svc and 'wrong' in svc['taxes']:
|
|
|
+ det['sales_tax'] = net * SALES_TAX
|
|
|
+ det['wrong_tax'] = net * svc['taxes']['wrong']
|
|
|
+ det['wrong_tax_loss'] = det['sales_tax'] - det['wrong_tax']
|
|
|
+ for k in ['commission', 'wrong_tax_loss']:
|
|
|
+ if k in det:
|
|
|
+ net -= det[k]
|
|
|
+ return round(net, 2)
|
|
|
+
|
|
|
+def _l2r(svc, list_, det):
|
|
|
+ retail = list_
|
|
|
+ if 'fees' in svc and 'client_fee' in svc['fees']:
|
|
|
+ det['client_fee'] = retail * svc['fees']['client_fee']
|
|
|
+ retail += det['client_fee']
|
|
|
+ if 'taxes' in svc and 'wrong' in svc['taxes']:
|
|
|
+ det['wrong_tax'] = retail * svc['taxes']['wrong']
|
|
|
+ retail += det['wrong_tax']
|
|
|
+ else:
|
|
|
+ det['sales_tax'] = retail * SALES_TAX
|
|
|
+ retail += det['sales_tax']
|
|
|
+ if 'taxes' in svc and 'flat_tax' in svc['taxes']:
|
|
|
+ det['service_tax'] = svc['taxes']['flat_tax']
|
|
|
+ retail += det['service_tax']
|
|
|
+ return round(retail, 2)
|
|
|
+
|
|
|
+def _r2l(svc, retail, det):
|
|
|
+ list_ = retail
|
|
|
+ if 'taxes' in svc and 'flat_tax' in svc['taxes']:
|
|
|
+ det['service_tax'] = svc['taxes']['flat_tax']
|
|
|
+ list_ -= det['service_tax']
|
|
|
+ if 'taxes' in svc and 'wrong' in svc['taxes']:
|
|
|
+ det['wrong_tax'] = list_ - (list_ / (1 + svc['taxes']['wrong']))
|
|
|
+ list_ -= det['wrong_tax']
|
|
|
+ else:
|
|
|
+ det['sales_tax'] = list_ - (list_ / (1 + SALES_TAX))
|
|
|
+ list_ -= det['sales_tax']
|
|
|
+ if 'fees' in svc and 'client_fee' in svc['fees']:
|
|
|
+ det['client_fee'] = list_ - (list_ / (1 + svc['fees']['client_fee']))
|
|
|
+ list_ -= det['client_fee']
|
|
|
+ return round(list_, 2)
|
|
|
+
|
|
|
+def _result(service_key, **amounts) -> dict:
|
|
|
+ det = {k: _dollar(v) for k, v in amounts.items() if k != 'details'}
|
|
|
+ return {'service': service_key, **det}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def list_services() -> list:
|
|
|
+ """List all available booking services with their keys and display names."""
|
|
|
+ return [{'key': k, 'name': v['name']} for k, v in SERVICES.items()]
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def net_to_list(service: str, amount: float) -> dict:
|
|
|
+ """Convert a net (owner payout) amount to the list (booking) price for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: Net amount in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ list_ = _n2l(svc, amount, det)
|
|
|
+ return {'service': service, 'net': _dollar(amount), 'list': _dollar(list_), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def list_to_net(service: str, amount: float) -> dict:
|
|
|
+ """Convert a list (booking) price to the net (owner payout) amount for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: List price in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ net = _l2n(svc, amount, det)
|
|
|
+ return {'service': service, 'list': _dollar(amount), 'net': _dollar(net), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def list_to_retail(service: str, amount: float) -> dict:
|
|
|
+ """Convert a list (booking) price to the retail (guest-facing) price for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: List price in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ retail = _l2r(svc, amount, det)
|
|
|
+ return {'service': service, 'list': _dollar(amount), 'retail': _dollar(retail), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def retail_to_list(service: str, amount: float) -> dict:
|
|
|
+ """Convert a retail (guest-facing) price to the list (booking) price for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: Retail price in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ list_ = _r2l(svc, amount, det)
|
|
|
+ return {'service': service, 'retail': _dollar(amount), 'list': _dollar(list_), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def net_to_retail(service: str, amount: float) -> dict:
|
|
|
+ """Convert a net (owner payout) amount to the retail (guest-facing) price for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: Net amount in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ list_ = _n2l(svc, amount, det)
|
|
|
+ det2 = {}
|
|
|
+ retail = _l2r(svc, list_, det2)
|
|
|
+ det.update(det2)
|
|
|
+ return {'service': service, 'net': _dollar(amount), 'list': _dollar(list_), 'retail': _dollar(retail), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+@mcp.tool()
|
|
|
+def retail_to_net(service: str, amount: float) -> dict:
|
|
|
+ """Convert a retail (guest-facing) price to the net (owner payout) amount for a service.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ service: Service key (e.g. 'airbnb', 'booking')
|
|
|
+ amount: Retail price in dollars
|
|
|
+ """
|
|
|
+ svc = _resolve_service(service)
|
|
|
+ det = {}
|
|
|
+ list_ = _r2l(svc, amount, det)
|
|
|
+ det2 = {}
|
|
|
+ net = _l2n(svc, list_, det2)
|
|
|
+ det.update(det2)
|
|
|
+ return {'service': service, 'retail': _dollar(amount), 'list': _dollar(list_), 'net': _dollar(net), 'details': _details_fmt(det)}
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ mcp.run()
|