@rubytech/create-realagent-code 0.1.26 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. package/dist/index.js +28 -11
  2. package/package.json +1 -1
  3. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -90
  4. package/payload/platform/plugins/brochures/.claude-plugin/plugin.json +8 -0
  5. package/payload/platform/plugins/brochures/PLUGIN.md +36 -0
  6. package/payload/platform/plugins/brochures/commands/make-brochure.md +11 -0
  7. package/payload/platform/plugins/brochures/skills/a4-print-documents/SKILL.md +478 -0
  8. package/payload/platform/plugins/brochures/skills/brand-design/SKILL.md +192 -0
  9. package/payload/platform/plugins/brochures/skills/make-brochure/SKILL.md +354 -0
  10. package/payload/platform/plugins/brochures/skills/make-brochure/references/seller-brief-template.md +115 -0
  11. package/payload/platform/plugins/brochures/skills/property-brochure/SKILL.md +119 -0
  12. package/payload/platform/plugins/brochures/skills/property-brochure/references/build.md +270 -0
  13. package/payload/platform/plugins/brochures/skills/property-brochure/references/copy.md +211 -0
  14. package/payload/platform/plugins/brochures/skills/property-brochure/references/images.md +166 -0
  15. package/payload/platform/plugins/brochures/skills/property-brochure/references/index-landing.md +376 -0
  16. package/payload/platform/plugins/brochures/skills/property-brochure/references/index.html +1288 -0
  17. package/payload/platform/plugins/brochures/skills/property-brochure/references/placeholders.md +250 -0
  18. package/payload/platform/plugins/brochures/skills/property-brochure/references/registers.md +47 -0
  19. package/payload/platform/plugins/brochures/skills/property-brochure/references/seller-brief.md +56 -0
  20. package/payload/platform/plugins/brochures/skills/property-brochure/references/structure.md +249 -0
  21. package/payload/platform/plugins/brochures/skills/property-brochure/references/template.html +2370 -0
  22. package/payload/platform/plugins/brochures/skills/property-extract/SKILL.md +372 -0
  23. package/payload/platform/plugins/buyers/.claude-plugin/plugin.json +8 -0
  24. package/payload/platform/plugins/buyers/PLUGIN.md +35 -0
  25. package/payload/platform/plugins/buyers/skills/buyer-feedback/SKILL.md +109 -0
  26. package/payload/platform/plugins/buyers/skills/buyer-management/SKILL.md +42 -0
  27. package/payload/platform/plugins/buyers/skills/buyer-management/references/buyer-qualification-questions.md +16 -0
  28. package/payload/platform/plugins/buyers/skills/buyer-management/references/buyer-qualification.md +59 -0
  29. package/payload/platform/plugins/buyers/skills/buyer-management/references/buyer-scripts.md +63 -0
  30. package/payload/platform/plugins/buyers/skills/buyer-management/references/buyer-working-scripts.md +54 -0
  31. package/payload/platform/plugins/buyers/skills/buyer-management/references/feedback-collection.md +42 -0
  32. package/payload/platform/plugins/buyers/skills/buyer-management/references/offer-capture.md +38 -0
  33. package/payload/platform/plugins/buyers/skills/buyer-management/references/viewing-booking.md +32 -0
  34. package/payload/platform/plugins/buyers/skills/buyer-management/references/viewing-management.md +52 -0
  35. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/SKILL.md +407 -0
  36. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/care-fees-guide.md +68 -0
  37. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/divorce-sales-guide.md +61 -0
  38. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/downsizing-guide.md +45 -0
  39. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/first-time-buyers.md +92 -0
  40. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/first-time-sellers.md +78 -0
  41. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/probate-guide.md +53 -0
  42. package/payload/platform/plugins/buyers/skills/buyer-seller-guides/references/upsizing-guide.md +41 -0
  43. package/payload/platform/plugins/buyers/skills/property-enquiry/SKILL.md +126 -0
  44. package/payload/platform/plugins/buyers/skills/viewing-management/SKILL.md +111 -0
  45. package/payload/platform/plugins/estate-business/.claude-plugin/plugin.json +8 -0
  46. package/payload/platform/plugins/estate-business/PLUGIN.md +65 -0
  47. package/payload/platform/plugins/estate-business/skills/business-growth/SKILL.md +133 -0
  48. package/payload/platform/plugins/estate-business/skills/business-growth/references/buy-back-your-time.md +37 -0
  49. package/payload/platform/plugins/estate-business/skills/business-growth/references/firewave-gost-scorecards.md +14 -0
  50. package/payload/platform/plugins/estate-business/skills/business-growth/references/keller-org-model.md +17 -0
  51. package/payload/platform/plugins/estate-business/skills/business-growth/references/lencioni-team-models.md +22 -0
  52. package/payload/platform/plugins/estate-business/skills/business-growth/references/listing-management-system.md +11 -0
  53. package/payload/platform/plugins/estate-business/skills/business-growth/references/net-figure-form.md +11 -0
  54. package/payload/platform/plugins/estate-business/skills/business-growth/references/serhant-bizinbox-notes.md +13 -0
  55. package/payload/platform/plugins/estate-business/skills/business-growth/references/team-roles-commission.md +14 -0
  56. package/payload/platform/plugins/estate-business/skills/business-growth/references/va-2026-ops.md +43 -0
  57. package/payload/platform/plugins/estate-business/skills/business-growth/references/wingman-structure.md +13 -0
  58. package/payload/platform/plugins/estate-business/skills/business-operations/SKILL.md +32 -0
  59. package/payload/platform/plugins/estate-business/skills/business-operations/references/crm-systems.md +57 -0
  60. package/payload/platform/plugins/estate-business/skills/business-operations/references/hiring-guide.md +59 -0
  61. package/payload/platform/plugins/estate-business/skills/business-operations/references/impact-framework.md +47 -0
  62. package/payload/platform/plugins/estate-business/skills/business-operations/references/minutes-equal-money.md +55 -0
  63. package/payload/platform/plugins/estate-business/skills/business-operations/references/team-management.md +48 -0
  64. package/payload/platform/plugins/estate-business/skills/commission-calculator/SKILL.md +40 -0
  65. package/payload/platform/plugins/estate-business/skills/exp-partnership/SKILL.md +52 -0
  66. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/12-reasons.md +39 -0
  67. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/95-5-system.md +66 -0
  68. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/agent-attraction-scripts.md +90 -0
  69. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/business-partnership.md +92 -0
  70. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/exp-model-overview.md +66 -0
  71. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/model-comparison.md +66 -0
  72. package/payload/platform/plugins/estate-business/skills/exp-partnership/references/revenue-share-explained.md +57 -0
  73. package/payload/platform/plugins/estate-business/skills/month-end-close/SKILL.md +69 -0
  74. package/payload/platform/plugins/estate-business/skills/payment-batch-stager/SKILL.md +42 -0
  75. package/payload/platform/plugins/estate-business/skills/period-reconciler/SKILL.md +42 -0
  76. package/payload/platform/plugins/estate-business/skills/personal-branding/SKILL.md +117 -0
  77. package/payload/platform/plugins/estate-business/skills/personal-branding/references/attraction-agent-notes.md +31 -0
  78. package/payload/platform/plugins/estate-business/skills/personal-branding/references/attraction-agent.md +58 -0
  79. package/payload/platform/plugins/estate-business/skills/personal-branding/references/authenticity-boundaries.md +28 -0
  80. package/payload/platform/plugins/estate-business/skills/personal-branding/references/become-a-brand-leader-notes.md +19 -0
  81. package/payload/platform/plugins/estate-business/skills/personal-branding/references/blast-formula.md +42 -0
  82. package/payload/platform/plugins/estate-business/skills/personal-branding/references/brand-leader.md +48 -0
  83. package/payload/platform/plugins/estate-business/skills/personal-branding/references/brand-strategy-system.md +59 -0
  84. package/payload/platform/plugins/estate-business/skills/personal-branding/references/content-engine.md +49 -0
  85. package/payload/platform/plugins/estate-business/skills/personal-branding/references/firewave-blast-and-blogging.md +23 -0
  86. package/payload/platform/plugins/estate-business/skills/personal-branding/references/gary-v-content.md +52 -0
  87. package/payload/platform/plugins/estate-business/skills/personal-branding/references/gary-v-principles.md +20 -0
  88. package/payload/platform/plugins/estate-business/skills/personal-branding/references/oversubscribed-positioning.md +18 -0
  89. package/payload/platform/plugins/estate-business/skills/personal-branding/references/platforms.md +41 -0
  90. package/payload/platform/plugins/estate-business/skills/personal-branding/references/priestley-oversubscribed.md +54 -0
  91. package/payload/platform/plugins/estate-business/skills/personal-branding/references/storeys-style-examples.md +25 -0
  92. package/payload/platform/plugins/estate-business/skills/personal-branding/references/visual-identity.md +27 -0
  93. package/payload/platform/plugins/estate-coaching/.claude-plugin/plugin.json +8 -0
  94. package/payload/platform/plugins/estate-coaching/PLUGIN.md +55 -0
  95. package/payload/platform/plugins/estate-coaching/skills/agent-performance/SKILL.md +371 -0
  96. package/payload/platform/plugins/estate-coaching/skills/agent-performance/references/atomic-habits.md +52 -0
  97. package/payload/platform/plugins/estate-coaching/skills/agent-performance/references/daily-routine-scorecard.md +104 -0
  98. package/payload/platform/plugins/estate-coaching/skills/agent-performance/references/hp6-model.md +63 -0
  99. package/payload/platform/plugins/estate-coaching/skills/agent-performance/references/twelve-week-year.md +71 -0
  100. package/payload/platform/plugins/estate-coaching/skills/bespoke-coaching/SKILL.md +36 -0
  101. package/payload/platform/plugins/estate-coaching/skills/bespoke-coaching/references/coaching-boundaries.md +56 -0
  102. package/payload/platform/plugins/estate-coaching/skills/bespoke-coaching/references/feedback-framework.md +61 -0
  103. package/payload/platform/plugins/estate-coaching/skills/bespoke-coaching/references/performance-framework.md +109 -0
  104. package/payload/platform/plugins/estate-coaching/skills/coaching-toolkit/SKILL.md +421 -0
  105. package/payload/platform/plugins/estate-coaching/skills/coaching-toolkit/references/coaching-exercises.md +86 -0
  106. package/payload/platform/plugins/estate-coaching/skills/coaching-toolkit/references/goal-setting.md +78 -0
  107. package/payload/platform/plugins/estate-coaching/skills/coaching-toolkit/references/one-to-one-framework.md +92 -0
  108. package/payload/platform/plugins/estate-coaching/skills/coaching-toolkit/references/soi-workbook.md +103 -0
  109. package/payload/platform/plugins/estate-coaching/skills/serhant-training/SKILL.md +410 -0
  110. package/payload/platform/plugins/estate-coaching/skills/serhant-training/references/agent-training-guide.md +70 -0
  111. package/payload/platform/plugins/estate-coaching/skills/serhant-training/references/business-in-a-box.md +72 -0
  112. package/payload/platform/plugins/estate-coaching/skills/serhant-training/references/buyers-guide.md +53 -0
  113. package/payload/platform/plugins/estate-coaching/skills/serhant-training/references/codo-method.md +72 -0
  114. package/payload/platform/plugins/estate-coaching/skills/serhant-training/references/website-planning-guide.md +79 -0
  115. package/payload/platform/plugins/estate-onboarding/.claude-plugin/plugin.json +8 -0
  116. package/payload/platform/plugins/estate-onboarding/PLUGIN.md +31 -0
  117. package/payload/platform/plugins/estate-onboarding/skills/bootstrap/SKILL.md +26 -0
  118. package/payload/platform/plugins/estate-onboarding/skills/bootstrap/references/onboarding-flow.md +63 -0
  119. package/payload/platform/plugins/estate-sales/.claude-plugin/plugin.json +8 -0
  120. package/payload/platform/plugins/estate-sales/PLUGIN.md +53 -0
  121. package/payload/platform/plugins/estate-sales/skills/chase-progression/SKILL.md +107 -0
  122. package/payload/platform/plugins/estate-sales/skills/negotiation/SKILL.md +35 -0
  123. package/payload/platform/plugins/estate-sales/skills/negotiation/references/deal-saving.md +47 -0
  124. package/payload/platform/plugins/estate-sales/skills/negotiation/references/negotiation-deep-guide.md +64 -0
  125. package/payload/platform/plugins/estate-sales/skills/negotiation/references/negotiation-prep-principles.md +29 -0
  126. package/payload/platform/plugins/estate-sales/skills/negotiation/references/negotiation-techniques.md +42 -0
  127. package/payload/platform/plugins/estate-sales/skills/negotiation/references/offer-presentation.md +43 -0
  128. package/payload/platform/plugins/estate-sales/skills/risk-scorer/SKILL.md +42 -0
  129. package/payload/platform/plugins/estate-sales/skills/sales-closer/SKILL.md +24 -0
  130. package/payload/platform/plugins/estate-sales/skills/sales-closer/references/serhant-emotion-stages.md +36 -0
  131. package/payload/platform/plugins/estate-sales/skills/sales-discovery/SKILL.md +30 -0
  132. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/chris-voss-discovery.md +88 -0
  133. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/firewave-gost-journey.md +68 -0
  134. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/phil-jones-openers.md +78 -0
  135. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/pre-listing-checklist.md +77 -0
  136. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/serhant-improv.md +22 -0
  137. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/tom-ferry-discovery.md +103 -0
  138. package/payload/platform/plugins/estate-sales/skills/sales-discovery/references/vendor-motivation-competitor.md +52 -0
  139. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/SKILL.md +29 -0
  140. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/references/chris-voss-negotiation.md +70 -0
  141. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/references/phil-jones-price-words.md +40 -0
  142. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/references/serhant-negotiation-plus.md +55 -0
  143. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/references/tom-panos-commission-pricing.md +57 -0
  144. package/payload/platform/plugins/estate-sales/skills/sales-negotiation/references/tony-morris-questioning.md +54 -0
  145. package/payload/platform/plugins/estate-sales/skills/sales-progression/SKILL.md +27 -0
  146. package/payload/platform/plugins/estate-sales/skills/sales-progression/references/conveyancing-guide.md +54 -0
  147. package/payload/platform/plugins/estate-sales/skills/sales-progression/references/transaction-tracking.md +66 -0
  148. package/payload/platform/plugins/estate-teaching/.claude-plugin/plugin.json +8 -0
  149. package/payload/platform/plugins/estate-teaching/PLUGIN.md +31 -0
  150. package/payload/platform/plugins/estate-teaching/skills/content-directory/SKILL.md +39 -0
  151. package/payload/platform/plugins/estate-teaching/skills/content-directory/references/module-delivery.md +65 -0
  152. package/payload/platform/plugins/estate-teaching/skills/content-directory/references/progress-tracking.md +47 -0
  153. package/payload/platform/plugins/leads/.claude-plugin/plugin.json +8 -0
  154. package/payload/platform/plugins/leads/PLUGIN.md +62 -0
  155. package/payload/platform/plugins/leads/skills/chain-progression-tracker/SKILL.md +51 -0
  156. package/payload/platform/plugins/leads/skills/diary-builder/SKILL.md +38 -0
  157. package/payload/platform/plugins/leads/skills/enquiry-triage/SKILL.md +36 -0
  158. package/payload/platform/plugins/leads/skills/lead-nurturing/SKILL.md +137 -0
  159. package/payload/platform/plugins/leads/skills/lead-nurturing/references/buyer-search-letter.md +28 -0
  160. package/payload/platform/plugins/leads/skills/lead-nurturing/references/buyer-search-letters.md +37 -0
  161. package/payload/platform/plugins/leads/skills/lead-nurturing/references/database-reactivation.md +30 -0
  162. package/payload/platform/plugins/leads/skills/lead-nurturing/references/email-nurture-sequences.md +45 -0
  163. package/payload/platform/plugins/leads/skills/lead-nurturing/references/facebook-referrals.md +30 -0
  164. package/payload/platform/plugins/leads/skills/lead-nurturing/references/firewave-email-nurture-sequences.md +41 -0
  165. package/payload/platform/plugins/leads/skills/lead-nurturing/references/keller-33-touch.md +34 -0
  166. package/payload/platform/plugins/leads/skills/lead-nurturing/references/neighbour-letters.md +31 -0
  167. package/payload/platform/plugins/leads/skills/lead-nurturing/references/neighbour-notification-letter.md +20 -0
  168. package/payload/platform/plugins/leads/skills/lead-nurturing/references/ofi-follow-up-dialogue.md +22 -0
  169. package/payload/platform/plugins/leads/skills/lead-nurturing/references/ofi-follow-up.md +26 -0
  170. package/payload/platform/plugins/leads/skills/lead-nurturing/references/serhant-three-fs-plus.md +21 -0
  171. package/payload/platform/plugins/leads/skills/lead-nurturing/references/sharran-10x10x10.md +18 -0
  172. package/payload/platform/plugins/leads/skills/lead-nurturing/references/sms-templates.md +40 -0
  173. package/payload/platform/plugins/leads/skills/lead-nurturing/references/sphere-of-influence-notes.md +34 -0
  174. package/payload/platform/plugins/leads/skills/lead-nurturing/references/sphere-of-influence.md +60 -0
  175. package/payload/platform/plugins/leads/skills/lead-nurturing/references/tom-panos-sms-templates.md +59 -0
  176. package/payload/platform/plugins/leads/skills/morning-round/SKILL.md +72 -0
  177. package/payload/platform/plugins/leads/skills/prospecting/SKILL.md +33 -0
  178. package/payload/platform/plugins/leads/skills/prospecting/references/database-matching.md +30 -0
  179. package/payload/platform/plugins/leads/skills/prospecting/references/database-value.md +53 -0
  180. package/payload/platform/plugins/leads/skills/prospecting/references/prospecting-dialogues.md +24 -0
  181. package/payload/platform/plugins/leads/skills/prospecting/references/reactivation.md +34 -0
  182. package/payload/platform/plugins/listings/.claude-plugin/plugin.json +8 -0
  183. package/payload/platform/plugins/listings/PLUGIN.md +103 -0
  184. package/payload/platform/plugins/listings/skills/comparable-finder/SKILL.md +52 -0
  185. package/payload/platform/plugins/listings/skills/epc-checker/SKILL.md +38 -0
  186. package/payload/platform/plugins/listings/skills/home-preparation/SKILL.md +28 -0
  187. package/payload/platform/plugins/listings/skills/home-preparation/references/kerb-appeal.md +38 -0
  188. package/payload/platform/plugins/listings/skills/home-preparation/references/photo-day.md +59 -0
  189. package/payload/platform/plugins/listings/skills/home-preparation/references/situational-tips.md +50 -0
  190. package/payload/platform/plugins/listings/skills/home-preparation/references/staging-guide.md +52 -0
  191. package/payload/platform/plugins/listings/skills/listing-copy-writer/SKILL.md +55 -0
  192. package/payload/platform/plugins/listings/skills/listing-presentation/SKILL.md +286 -0
  193. package/payload/platform/plugins/listings/skills/listing-presentation/references/booking-script.md +51 -0
  194. package/payload/platform/plugins/listings/skills/listing-presentation/references/objection-scripts.md +193 -0
  195. package/payload/platform/plugins/listings/skills/listing-presentation/references/penhaul-presentation.md +123 -0
  196. package/payload/platform/plugins/listings/skills/listing-presentation/references/pre-listing-kit.md +139 -0
  197. package/payload/platform/plugins/listings/skills/listing-presentation/references/set-to-sell.md +55 -0
  198. package/payload/platform/plugins/listings/skills/listing-presentation/references/sharran-frameworks.md +107 -0
  199. package/payload/platform/plugins/listings/skills/local-market-stats/SKILL.md +33 -0
  200. package/payload/platform/plugins/listings/skills/new-instruction/SKILL.md +78 -0
  201. package/payload/platform/plugins/listings/skills/particulars-builder/SKILL.md +48 -0
  202. package/payload/platform/plugins/listings/skills/portal-launch-scheduler/SKILL.md +49 -0
  203. package/payload/platform/plugins/listings/skills/pricing-scenario-builder/SKILL.md +35 -0
  204. package/payload/platform/plugins/listings/skills/property-marketing/SKILL.md +337 -0
  205. package/payload/platform/plugins/listings/skills/property-marketing/references/auction-report-template.md +41 -0
  206. package/payload/platform/plugins/listings/skills/property-marketing/references/coming-soon-campaign.md +43 -0
  207. package/payload/platform/plugins/listings/skills/property-marketing/references/direct-mail-templates.md +121 -0
  208. package/payload/platform/plugins/listings/skills/property-marketing/references/eoi-form-template.md +62 -0
  209. package/payload/platform/plugins/listings/skills/property-marketing/references/monthly-scorecard.md +63 -0
  210. package/payload/platform/plugins/listings/skills/supplier-booker/SKILL.md +39 -0
  211. package/payload/platform/plugins/listings/skills/talk-track-composer/SKILL.md +36 -0
  212. package/payload/platform/plugins/listings/skills/terms-of-business-drafter/SKILL.md +54 -0
  213. package/payload/platform/plugins/listings/skills/valuation-prep/SKILL.md +69 -0
  214. package/payload/platform/plugins/loop/.claude-plugin/plugin.json +17 -0
  215. package/payload/platform/plugins/loop/PLUGIN.md +108 -0
  216. package/payload/platform/plugins/loop/mcp/dist/index.d.ts +2 -0
  217. package/payload/platform/plugins/loop/mcp/dist/index.d.ts.map +1 -0
  218. package/payload/platform/plugins/loop/mcp/dist/index.js +293 -0
  219. package/payload/platform/plugins/loop/mcp/dist/index.js.map +1 -0
  220. package/payload/platform/plugins/loop/mcp/dist/lib/crypto.d.ts +10 -0
  221. package/payload/platform/plugins/loop/mcp/dist/lib/crypto.d.ts.map +1 -0
  222. package/payload/platform/plugins/loop/mcp/dist/lib/crypto.js +88 -0
  223. package/payload/platform/plugins/loop/mcp/dist/lib/crypto.js.map +1 -0
  224. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.d.ts +82 -0
  225. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -0
  226. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.js +427 -0
  227. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -0
  228. package/payload/platform/plugins/loop/mcp/dist/lib/neo4j.d.ts +5 -0
  229. package/payload/platform/plugins/loop/mcp/dist/lib/neo4j.d.ts.map +1 -0
  230. package/payload/platform/plugins/loop/mcp/dist/lib/neo4j.js +40 -0
  231. package/payload/platform/plugins/loop/mcp/dist/lib/neo4j.js.map +1 -0
  232. package/payload/platform/plugins/loop/mcp/dist/tools/customer-preferences.d.ts +10 -0
  233. package/payload/platform/plugins/loop/mcp/dist/tools/customer-preferences.d.ts.map +1 -0
  234. package/payload/platform/plugins/loop/mcp/dist/tools/customer-preferences.js +24 -0
  235. package/payload/platform/plugins/loop/mcp/dist/tools/customer-preferences.js.map +1 -0
  236. package/payload/platform/plugins/loop/mcp/dist/tools/feedback.d.ts +16 -0
  237. package/payload/platform/plugins/loop/mcp/dist/tools/feedback.d.ts.map +1 -0
  238. package/payload/platform/plugins/loop/mcp/dist/tools/feedback.js +35 -0
  239. package/payload/platform/plugins/loop/mcp/dist/tools/feedback.js.map +1 -0
  240. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.d.ts +5 -0
  241. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  242. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.js +19 -0
  243. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.js.map +1 -0
  244. package/payload/platform/plugins/loop/mcp/dist/tools/key-list.d.ts +4 -0
  245. package/payload/platform/plugins/loop/mcp/dist/tools/key-list.d.ts.map +1 -0
  246. package/payload/platform/plugins/loop/mcp/dist/tools/key-list.js +14 -0
  247. package/payload/platform/plugins/loop/mcp/dist/tools/key-list.js.map +1 -0
  248. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.d.ts +9 -0
  249. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.d.ts.map +1 -0
  250. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.js +60 -0
  251. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.js.map +1 -0
  252. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts +13 -0
  253. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts.map +1 -0
  254. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.js +41 -0
  255. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.js.map +1 -0
  256. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts +9 -0
  257. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-batch.d.ts.map +1 -0
  258. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-batch.js +16 -0
  259. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-batch.js.map +1 -0
  260. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts +15 -0
  261. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-request.d.ts.map +1 -0
  262. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-request.js +11 -0
  263. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match-request.js.map +1 -0
  264. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match.d.ts +10 -0
  265. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match.d.ts.map +1 -0
  266. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match.js +39 -0
  267. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-match.js.map +1 -0
  268. package/payload/platform/plugins/loop/mcp/dist/tools/people-detail.d.ts +9 -0
  269. package/payload/platform/plugins/loop/mcp/dist/tools/people-detail.d.ts.map +1 -0
  270. package/payload/platform/plugins/loop/mcp/dist/tools/people-detail.js +125 -0
  271. package/payload/platform/plugins/loop/mcp/dist/tools/people-detail.js.map +1 -0
  272. package/payload/platform/plugins/loop/mcp/dist/tools/people-search.d.ts +18 -0
  273. package/payload/platform/plugins/loop/mcp/dist/tools/people-search.d.ts.map +1 -0
  274. package/payload/platform/plugins/loop/mcp/dist/tools/people-search.js +87 -0
  275. package/payload/platform/plugins/loop/mcp/dist/tools/people-search.js.map +1 -0
  276. package/payload/platform/plugins/loop/mcp/dist/tools/property-detail.d.ts +10 -0
  277. package/payload/platform/plugins/loop/mcp/dist/tools/property-detail.d.ts.map +1 -0
  278. package/payload/platform/plugins/loop/mcp/dist/tools/property-detail.js +82 -0
  279. package/payload/platform/plugins/loop/mcp/dist/tools/property-detail.js.map +1 -0
  280. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.d.ts +12 -0
  281. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -0
  282. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.js +32 -0
  283. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -0
  284. package/payload/platform/plugins/loop/mcp/dist/tools/property-request.d.ts +15 -0
  285. package/payload/platform/plugins/loop/mcp/dist/tools/property-request.d.ts.map +1 -0
  286. package/payload/platform/plugins/loop/mcp/dist/tools/property-request.js +11 -0
  287. package/payload/platform/plugins/loop/mcp/dist/tools/property-request.js.map +1 -0
  288. package/payload/platform/plugins/loop/mcp/dist/tools/property-search.d.ts +16 -0
  289. package/payload/platform/plugins/loop/mcp/dist/tools/property-search.d.ts.map +1 -0
  290. package/payload/platform/plugins/loop/mcp/dist/tools/property-search.js +41 -0
  291. package/payload/platform/plugins/loop/mcp/dist/tools/property-search.js.map +1 -0
  292. package/payload/platform/plugins/loop/mcp/dist/tools/supplier.d.ts +13 -0
  293. package/payload/platform/plugins/loop/mcp/dist/tools/supplier.d.ts.map +1 -0
  294. package/payload/platform/plugins/loop/mcp/dist/tools/supplier.js +49 -0
  295. package/payload/platform/plugins/loop/mcp/dist/tools/supplier.js.map +1 -0
  296. package/payload/platform/plugins/loop/mcp/dist/tools/team-availability.d.ts +7 -0
  297. package/payload/platform/plugins/loop/mcp/dist/tools/team-availability.d.ts.map +1 -0
  298. package/payload/platform/plugins/loop/mcp/dist/tools/team-availability.js +19 -0
  299. package/payload/platform/plugins/loop/mcp/dist/tools/team-availability.js.map +1 -0
  300. package/payload/platform/plugins/loop/mcp/dist/tools/team-info.d.ts +5 -0
  301. package/payload/platform/plugins/loop/mcp/dist/tools/team-info.d.ts.map +1 -0
  302. package/payload/platform/plugins/loop/mcp/dist/tools/team-info.js +32 -0
  303. package/payload/platform/plugins/loop/mcp/dist/tools/team-info.js.map +1 -0
  304. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-create.d.ts +14 -0
  305. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-create.d.ts.map +1 -0
  306. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-create.js +11 -0
  307. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-create.js.map +1 -0
  308. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-detail.d.ts +9 -0
  309. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-detail.d.ts.map +1 -0
  310. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-detail.js +85 -0
  311. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-detail.js.map +1 -0
  312. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-search.d.ts +13 -0
  313. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-search.d.ts.map +1 -0
  314. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-search.js +44 -0
  315. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-search.js.map +1 -0
  316. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-update.d.ts +14 -0
  317. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-update.d.ts.map +1 -0
  318. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-update.js +18 -0
  319. package/payload/platform/plugins/loop/mcp/dist/tools/viewing-update.js.map +1 -0
  320. package/payload/platform/plugins/loop/mcp/package-lock.json +2549 -0
  321. package/payload/platform/plugins/loop/mcp/package.json +21 -0
  322. package/payload/platform/plugins/loop/mcp/src/__tests__/loop-swagger.snapshot.json +26467 -0
  323. package/payload/platform/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +153 -0
  324. package/payload/platform/plugins/loop/mcp/src/index.ts +444 -0
  325. package/payload/platform/plugins/loop/mcp/src/lib/crypto.ts +105 -0
  326. package/payload/platform/plugins/loop/mcp/src/lib/loop-api.ts +604 -0
  327. package/payload/platform/plugins/loop/mcp/src/lib/neo4j.ts +51 -0
  328. package/payload/platform/plugins/loop/mcp/src/tools/customer-preferences.ts +66 -0
  329. package/payload/platform/plugins/loop/mcp/src/tools/feedback.ts +86 -0
  330. package/payload/platform/plugins/loop/mcp/src/tools/key-deregister.ts +27 -0
  331. package/payload/platform/plugins/loop/mcp/src/tools/key-list.ts +19 -0
  332. package/payload/platform/plugins/loop/mcp/src/tools/key-register.ts +95 -0
  333. package/payload/platform/plugins/loop/mcp/src/tools/marketing-enquiry.ts +113 -0
  334. package/payload/platform/plugins/loop/mcp/src/tools/marketing-match-batch.ts +53 -0
  335. package/payload/platform/plugins/loop/mcp/src/tools/marketing-match-request.ts +42 -0
  336. package/payload/platform/plugins/loop/mcp/src/tools/marketing-match.ts +84 -0
  337. package/payload/platform/plugins/loop/mcp/src/tools/people-detail.ts +245 -0
  338. package/payload/platform/plugins/loop/mcp/src/tools/people-search.ts +180 -0
  339. package/payload/platform/plugins/loop/mcp/src/tools/property-detail.ts +145 -0
  340. package/payload/platform/plugins/loop/mcp/src/tools/property-listed.ts +88 -0
  341. package/payload/platform/plugins/loop/mcp/src/tools/property-request.ts +42 -0
  342. package/payload/platform/plugins/loop/mcp/src/tools/property-search.ts +92 -0
  343. package/payload/platform/plugins/loop/mcp/src/tools/supplier.ts +129 -0
  344. package/payload/platform/plugins/loop/mcp/src/tools/team-availability.ts +52 -0
  345. package/payload/platform/plugins/loop/mcp/src/tools/team-info.ts +95 -0
  346. package/payload/platform/plugins/loop/mcp/src/tools/viewing-create.ts +41 -0
  347. package/payload/platform/plugins/loop/mcp/src/tools/viewing-detail.ts +171 -0
  348. package/payload/platform/plugins/loop/mcp/src/tools/viewing-search.ts +92 -0
  349. package/payload/platform/plugins/loop/mcp/src/tools/viewing-update.ts +53 -0
  350. package/payload/platform/plugins/loop/mcp/tsconfig.json +20 -0
  351. package/payload/platform/plugins/loop/mcp/vitest.config.ts +9 -0
  352. package/payload/platform/plugins/loop/skills/compliance-flag-checker/SKILL.md +53 -0
  353. package/payload/platform/plugins/loop/skills/priority-ranker/SKILL.md +40 -0
  354. package/payload/platform/plugins/loop/skills/tone-matched-drafter/SKILL.md +53 -0
  355. package/payload/platform/plugins/loop/skills/variance-narrator/SKILL.md +50 -0
  356. package/payload/platform/plugins/loop/skills/vendor-research/SKILL.md +54 -0
  357. package/payload/platform/plugins/teaching/.claude-plugin/plugin.json +8 -0
  358. package/payload/platform/plugins/teaching/PLUGIN.md +57 -0
  359. package/payload/platform/plugins/teaching/skills/interactive-tutor/SKILL.md +59 -0
  360. package/payload/platform/plugins/teaching/skills/interactive-tutor/references/assessment.md +70 -0
  361. package/payload/platform/plugins/teaching/skills/interactive-tutor/references/classroom-conduct.md +43 -0
  362. package/payload/platform/plugins/teaching/skills/interactive-tutor/references/teaching-modes.md +83 -0
  363. package/payload/platform/plugins/teaching/skills/lesson-planner/SKILL.md +48 -0
  364. package/payload/platform/plugins/teaching/skills/lesson-planner/references/context-gathering.md +41 -0
  365. package/payload/platform/plugins/teaching/skills/lesson-planner/references/plan-structure.md +94 -0
  366. package/payload/platform/plugins/teaching/skills/study-pack-builder/SKILL.md +52 -0
  367. package/payload/platform/plugins/teaching/skills/study-pack-builder/references/disaggregation.md +49 -0
  368. package/payload/platform/plugins/teaching/skills/study-pack-builder/references/materials.md +116 -0
  369. package/payload/platform/plugins/vendors/.claude-plugin/plugin.json +8 -0
  370. package/payload/platform/plugins/vendors/PLUGIN.md +34 -0
  371. package/payload/platform/plugins/vendors/skills/vendor-communication/SKILL.md +42 -0
  372. package/payload/platform/plugins/vendors/skills/vendor-communication/references/fee-protection-and-agenda.md +28 -0
  373. package/payload/platform/plugins/vendors/skills/vendor-communication/references/listing-scripts.md +44 -0
  374. package/payload/platform/plugins/vendors/skills/vendor-communication/references/negotiation-deep-guide.md +70 -0
  375. package/payload/platform/plugins/vendors/skills/vendor-communication/references/price-alignment-scripts.md +33 -0
  376. package/payload/platform/plugins/vendors/skills/vendor-communication/references/price-alignment.md +34 -0
  377. package/payload/platform/plugins/vendors/skills/vendor-communication/references/scenario-scripts.md +38 -0
  378. package/payload/platform/plugins/vendors/skills/vendor-communication/references/seller-engagement.md +51 -0
  379. package/payload/platform/plugins/vendors/skills/vendor-communication/references/valuation-booking.md +76 -0
  380. package/payload/platform/plugins/vendors/skills/vendor-communication/references/vendor-scripts.md +63 -0
  381. package/payload/platform/plugins/vendors/skills/vendor-communication/references/vendor-updates.md +41 -0
  382. package/payload/platform/plugins/vendors/skills/vendor-updates/SKILL.md +153 -0
  383. package/payload/platform/plugins/writer-craft/.claude-plugin/plugin.json +8 -0
  384. package/payload/platform/plugins/writer-craft/PLUGIN.md +87 -0
  385. package/payload/platform/plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +92 -0
  386. package/payload/platform/plugins/writer-craft/skills/citation-style/SKILL.md +94 -0
  387. package/payload/platform/plugins/writer-craft/skills/citation-style/references/book-and-chapter-models.md +77 -0
  388. package/payload/platform/plugins/writer-craft/skills/citation-style/references/citation-rules.md +103 -0
  389. package/payload/platform/plugins/writer-craft/skills/citation-style/references/journal-article-models.md +74 -0
  390. package/payload/platform/plugins/writer-craft/skills/citation-style/references/other-source-models.md +146 -0
  391. package/payload/platform/plugins/writer-craft/skills/citation-style/references/reference-list-rules.md +70 -0
  392. package/payload/platform/plugins/writer-craft/skills/editorial-practice/SKILL.md +108 -0
  393. package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/copyediting.md +73 -0
  394. package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/developmental-editing.md +85 -0
  395. package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/genre-specific-editing.md +78 -0
  396. package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/line-editing.md +55 -0
  397. package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/self-editing.md +89 -0
  398. package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/SKILL.md +114 -0
  399. package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/audience-analysis.md +73 -0
  400. package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/crafting-persuasive-story.md +76 -0
  401. package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/persuasion-case-studies.md +67 -0
  402. package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/transformation-framework.md +86 -0
  403. package/payload/platform/plugins/writer-craft/skills/point-of-view/SKILL.md +97 -0
  404. package/payload/platform/plugins/writer-craft/skills/point-of-view/references/indirect-narration.md +72 -0
  405. package/payload/platform/plugins/writer-craft/skills/point-of-view/references/pov-types-and-voice.md +91 -0
  406. package/payload/platform/plugins/writer-craft/skills/point-of-view/references/protagonist-filter.md +71 -0
  407. package/payload/platform/plugins/writer-craft/skills/point-of-view/references/tense-and-person.md +85 -0
  408. package/payload/platform/plugins/writer-craft/skills/prose-craft/SKILL.md +100 -0
  409. package/payload/platform/plugins/writer-craft/skills/prose-craft/references/punctuation-and-grammar.md +72 -0
  410. package/payload/platform/plugins/writer-craft/skills/prose-craft/references/repetition.md +71 -0
  411. package/payload/platform/plugins/writer-craft/skills/prose-craft/references/sound-and-rhythm.md +64 -0
  412. package/payload/platform/plugins/writer-craft/skills/prose-craft/references/word-economy.md +93 -0
  413. package/payload/platform/plugins/writer-craft/skills/reader-engagement/SKILL.md +100 -0
  414. package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/cause-effect-setup-payoff.md +79 -0
  415. package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/conflict-escalation.md +81 -0
  416. package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/hooking-readers.md +67 -0
  417. package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/neurochemistry-of-engagement.md +94 -0
  418. package/payload/platform/plugins/writer-craft/skills/review-manuscript/SKILL.md +111 -0
  419. package/payload/platform/plugins/writer-craft/skills/review-manuscript/references/review-manuscript-checklist.md +119 -0
  420. package/payload/platform/plugins/writer-craft/skills/review-prose/SKILL.md +99 -0
  421. package/payload/platform/plugins/writer-craft/skills/review-prose/references/prose-review-checklist.md +112 -0
  422. package/payload/platform/plugins/writer-craft/skills/review-scene/SKILL.md +99 -0
  423. package/payload/platform/plugins/writer-craft/skills/review-scene/references/scene-analysis-framework.md +95 -0
  424. package/payload/platform/plugins/writer-craft/skills/story-architecture/SKILL.md +106 -0
  425. package/payload/platform/plugins/writer-craft/skills/story-architecture/references/blueprinting-and-scene-cards.md +118 -0
  426. package/payload/platform/plugins/writer-craft/skills/story-architecture/references/inner-issue-and-protagonist-goal.md +66 -0
  427. package/payload/platform/plugins/writer-craft/skills/story-architecture/references/misbelief-desire-worldview.md +87 -0
  428. package/payload/platform/plugins/writer-craft/skills/story-architecture/references/origin-scenes-and-escalation.md +82 -0
  429. package/payload/platform/plugins/writer-craft/skills/story-blueprint/SKILL.md +133 -0
  430. package/payload/platform/plugins/writer-craft/skills/story-blueprint/references/blueprinting-exercises.md +118 -0
  431. package/payload/platform/plugins/writer-craft/skills/story-blueprint/references/blueprinting-process.md +128 -0
  432. package/payload/platform/services/claude-session-manager/dist/config.d.ts +6 -0
  433. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  434. package/payload/platform/services/claude-session-manager/dist/config.js +60 -1
  435. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  436. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +9 -0
  437. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  438. package/payload/platform/services/claude-session-manager/dist/http-server.js +34 -0
  439. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  440. package/payload/platform/services/claude-session-manager/dist/index.js +12 -0
  441. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  442. package/payload/platform/services/claude-session-manager/dist/public-tool-audit.d.ts +33 -0
  443. package/payload/platform/services/claude-session-manager/dist/public-tool-audit.d.ts.map +1 -0
  444. package/payload/platform/services/claude-session-manager/dist/public-tool-audit.js +149 -0
  445. package/payload/platform/services/claude-session-manager/dist/public-tool-audit.js.map +1 -0
  446. package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.d.ts +28 -0
  447. package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.d.ts.map +1 -0
  448. package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.js +77 -0
  449. package/payload/platform/services/claude-session-manager/dist/spawn-rate-limiter.js.map +1 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Anti-regression: every Loop V2 swagger write endpoint maps to a
3
+ * registered MCP tool whose source file references the path's stable
4
+ * prefix. Catches three drift modes pre-publish:
5
+ *
6
+ * 1. Loop adds a new POST/PUT/PATCH/DELETE → no mapping → test fails
7
+ * and names the orphan endpoint.
8
+ * 2. A tool's `server.tool("…")` registration is renamed or removed →
9
+ * the name lookup against `index.ts` source fails.
10
+ * 3. A tool's source file is refactored such that the swagger path
11
+ * family no longer appears literally → the path-fragment lookup
12
+ * against the source file fails.
13
+ *
14
+ * The vendored snapshot at `loop-swagger.snapshot.json` was fetched on
15
+ * 2026-04-28 from `https://api.loop.software/swagger/v2/swagger.json`
16
+ * (32 write endpoints across 8 tags). Refresh by re-downloading and
17
+ * extending ENDPOINT_MAP — the test is the single source of truth for
18
+ * what coverage means.
19
+ */
20
+
21
+ import { describe, expect, it } from 'vitest';
22
+ import { readFileSync } from 'node:fs';
23
+ import { resolve, dirname } from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+
26
+ const __dirname = dirname(fileURLToPath(import.meta.url));
27
+ const PLUGIN_SRC = resolve(__dirname, '..');
28
+ const SWAGGER = JSON.parse(
29
+ readFileSync(resolve(__dirname, 'loop-swagger.snapshot.json'), 'utf-8'),
30
+ ) as { paths: Record<string, Record<string, unknown>> };
31
+
32
+ const WRITE_METHODS = ['post', 'put', 'patch', 'delete'] as const;
33
+
34
+ interface EndpointMapping {
35
+ method: typeof WRITE_METHODS[number];
36
+ path: string;
37
+ tool: string;
38
+ source: string;
39
+ fragment: string;
40
+ }
41
+
42
+ // One entry per swagger write op. `fragment` is the literal substring
43
+ // (typically the stable prefix before any `${...}` template variable)
44
+ // that must appear in the source file — the path's family signature.
45
+ const ENDPOINT_MAP: EndpointMapping[] = [
46
+ // Customer (1)
47
+ { method: 'post', path: '/customer/preferences/{personCode}', tool: 'loop-customer-preferences', source: 'tools/customer-preferences.ts', fragment: '/customer/preferences/' },
48
+ // Feedback (2)
49
+ { method: 'post', path: '/feedback/residential/sales/viewings/{id}/feedback', tool: 'loop-feedback-submit', source: 'tools/feedback.ts', fragment: '/feedback/residential/' },
50
+ { method: 'post', path: '/feedback/residential/lettings/viewings/{id}/feedback', tool: 'loop-feedback-submit', source: 'tools/feedback.ts', fragment: '/feedback/residential/' },
51
+ // Marketing — match-batch PUT (2)
52
+ { method: 'put', path: '/marketing/matching/other-matches', tool: 'loop-marketing-match-batch', source: 'tools/marketing-match-batch.ts', fragment: '/matching/other-matches' },
53
+ { method: 'put', path: '/marketing/rentals/matching/other-matches', tool: 'loop-marketing-match-batch', source: 'tools/marketing-match-batch.ts', fragment: '/matching/other-matches' },
54
+ // Marketing — match-request POST × {sales,lettings} × {viewing,information,callback} (6)
55
+ { method: 'post', path: '/marketing/matching/{id}/viewing', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/matching/' },
56
+ { method: 'post', path: '/marketing/rentals/matching/{id}/viewing', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/marketing/rentals' },
57
+ { method: 'post', path: '/marketing/matching/{id}/information', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/matching/' },
58
+ { method: 'post', path: '/marketing/rentals/matching/{id}/information', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/marketing/rentals' },
59
+ { method: 'post', path: '/marketing/matching/{id}/callback', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/matching/' },
60
+ { method: 'post', path: '/marketing/rentals/matching/{id}/callback', tool: 'loop-marketing-match-request', source: 'tools/marketing-match-request.ts', fragment: '/marketing/rentals' },
61
+ // Marketing — enquiries (4)
62
+ { method: 'post', path: '/marketing/enquiries/seller', tool: 'loop-marketing-enquiry', source: 'tools/marketing-enquiry.ts', fragment: '/marketing/enquiries/seller' },
63
+ { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/answers/{key}', tool: 'loop-marketing-enquiry', source: 'tools/marketing-enquiry.ts', fragment: '/marketing/enquiries/auto-responder/' },
64
+ { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/details/{key}', tool: 'loop-marketing-enquiry', source: 'tools/marketing-enquiry.ts', fragment: '/marketing/enquiries/auto-responder/' },
65
+ { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/refer/{key}', tool: 'loop-marketing-enquiry', source: 'tools/marketing-enquiry.ts', fragment: '/marketing/enquiries/auto-responder/' },
66
+ // Property (6)
67
+ { method: 'post', path: '/property/residential/sales/{id}/viewing', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
68
+ { method: 'post', path: '/property/residential/sales/{id}/call-back', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
69
+ { method: 'post', path: '/property/residential/sales/{id}/information', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
70
+ { method: 'post', path: '/property/residential/lettings/{id}/viewing', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
71
+ { method: 'post', path: '/property/residential/lettings/{id}/call-back', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
72
+ { method: 'post', path: '/property/residential/lettings/{id}/information', tool: 'loop-property-request', source: 'tools/property-request.ts', fragment: '/property/residential/' },
73
+ // Supplier (3)
74
+ { method: 'post', path: '/supplier/maintenance/{code}/{jobid}/complete', tool: 'loop-supplier', source: 'tools/supplier.ts', fragment: '/supplier/maintenance/' },
75
+ { method: 'put', path: '/supplier/maintenance/{code}/{quoteid}/quote-for-job', tool: 'loop-supplier', source: 'tools/supplier.ts', fragment: '/quote-for-job' },
76
+ { method: 'post', path: '/supplier/board-contractor/{code}/{jobid}/complete', tool: 'loop-supplier', source: 'tools/supplier.ts', fragment: '/supplier/board-contractor/' },
77
+ // Viewing — sales (4)
78
+ { method: 'post', path: '/residential/sales/viewings', tool: 'loop-viewing-create', source: 'tools/viewing-create.ts', fragment: '/residential/' },
79
+ { method: 'post', path: '/residential/sales/viewings/{id}/buyer-feedback', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
80
+ { method: 'post', path: '/residential/sales/viewings/{id}/note', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
81
+ { method: 'post', path: '/residential/sales/viewings/{id}/seller-feedback', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
82
+ // RentalViewing — lettings (4)
83
+ { method: 'post', path: '/residential/lettings/viewings', tool: 'loop-viewing-create', source: 'tools/viewing-create.ts', fragment: '/residential/' },
84
+ { method: 'post', path: '/residential/lettings/viewings/{id}/renter-feedback', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
85
+ { method: 'post', path: '/residential/lettings/viewings/{id}/note', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
86
+ { method: 'post', path: '/residential/lettings/viewings/{id}/landlord-feedback', tool: 'loop-viewing-update', source: 'tools/viewing-update.ts', fragment: '/residential/' },
87
+ ];
88
+
89
+ // Lazily cache source files so multiple per-endpoint tests don't re-read.
90
+ const sourceCache = new Map<string, string>();
91
+ function readSource(rel: string): string {
92
+ let cached = sourceCache.get(rel);
93
+ if (cached) return cached;
94
+ cached = readFileSync(resolve(PLUGIN_SRC, rel), 'utf-8');
95
+ sourceCache.set(rel, cached);
96
+ return cached;
97
+ }
98
+
99
+ const indexSrc = readSource('index.ts');
100
+
101
+ describe('Loop swagger write coverage', () => {
102
+ // 1. Every write op in the snapshot has a mapping (catches new endpoints).
103
+ it('every swagger write op has a mapping in ENDPOINT_MAP', () => {
104
+ const swaggerOps: { method: string; path: string }[] = [];
105
+ for (const [path, item] of Object.entries(SWAGGER.paths)) {
106
+ for (const m of WRITE_METHODS) {
107
+ if (item[m]) swaggerOps.push({ method: m, path });
108
+ }
109
+ }
110
+ const mapped = new Set(ENDPOINT_MAP.map(e => `${e.method.toUpperCase()} ${e.path}`));
111
+ const orphans = swaggerOps.filter(o => !mapped.has(`${o.method.toUpperCase()} ${o.path}`));
112
+ if (orphans.length > 0) {
113
+ const lines = orphans.map(o => ` ${o.method.toUpperCase()} ${o.path}`).join('\n');
114
+ throw new Error(
115
+ `${orphans.length} swagger write endpoint(s) lack a mapping in ENDPOINT_MAP — Loop has added new write ops since the last snapshot:\n${lines}\n\nUpdate ENDPOINT_MAP to map each new endpoint to its tool, source, and path fragment.`,
116
+ );
117
+ }
118
+ expect(swaggerOps.length).toBe(ENDPOINT_MAP.length);
119
+ });
120
+
121
+ // 2. Every mapping's tool is registered in index.ts (catches deregistration).
122
+ // Match by quoted tool name in proximity to a `server.tool(` call —
123
+ // each registration is `server.tool(\n "<name>", ...)` or single-line.
124
+ // A tool name that appears only in a comment or unrelated string doesn't
125
+ // match because we require it to be the first non-whitespace argument
126
+ // of a server.tool( invocation within ~32 chars.
127
+ it.each(ENDPOINT_MAP)('$method $path → tool $tool registered in index.ts', ({ tool }) => {
128
+ const registrationRe = new RegExp(`server\\.tool\\(\\s*"${tool.replace(/[.*+?^${}()|[\\\]\\\\]/g, '\\\\$&')}"`);
129
+ const isRegistered = registrationRe.test(indexSrc);
130
+ expect(isRegistered, `tool "${tool}" not registered as server.tool("${tool}", ...) in src/index.ts`).toBe(true);
131
+ });
132
+
133
+ // 3. Every mapping's path fragment appears in the named source file
134
+ // (catches refactor that drops the path family).
135
+ it.each(ENDPOINT_MAP)('$method $path → fragment "$fragment" present in $source', ({ source, fragment }) => {
136
+ const src = readSource(source);
137
+ expect(
138
+ src.includes(fragment),
139
+ `expected fragment "${fragment}" in ${source} (path family for the swagger op)`,
140
+ ).toBe(true);
141
+ });
142
+
143
+ // 4. Reverse coverage: every distinct tool in the mapping appears
144
+ // registered (catches dead mappings).
145
+ it('every tool referenced by ENDPOINT_MAP is registered', () => {
146
+ const tools = new Set(ENDPOINT_MAP.map(e => e.tool));
147
+ const missing: string[] = [];
148
+ for (const tool of tools) {
149
+ if (!indexSrc.includes(`"${tool}"`)) missing.push(tool);
150
+ }
151
+ expect(missing).toEqual([]);
152
+ });
153
+ });
@@ -0,0 +1,444 @@
1
+ import { initStderrTee } from "../../../../../../platform/lib/mcp-stderr-tee/dist/index.js";
2
+ initStderrTee("loop");
3
+
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { keyRegister } from "./tools/key-register.js";
8
+ import { keyDeregister } from "./tools/key-deregister.js";
9
+ import { keyList } from "./tools/key-list.js";
10
+ import { propertySearch } from "./tools/property-search.js";
11
+ import { propertyDetail } from "./tools/property-detail.js";
12
+ import { propertyListed } from "./tools/property-listed.js";
13
+ import { propertyRequest } from "./tools/property-request.js";
14
+ import { peopleSearch } from "./tools/people-search.js";
15
+ import { peopleDetail } from "./tools/people-detail.js";
16
+ import { viewingSearch } from "./tools/viewing-search.js";
17
+ import { viewingDetail } from "./tools/viewing-detail.js";
18
+ import { viewingCreate } from "./tools/viewing-create.js";
19
+ import { viewingUpdate } from "./tools/viewing-update.js";
20
+ import { feedbackGet, feedbackSubmit } from "./tools/feedback.js";
21
+ import { teamInfo } from "./tools/team-info.js";
22
+ import { teamAvailability } from "./tools/team-availability.js";
23
+ import { marketingMatchDetail } from "./tools/marketing-match.js";
24
+ import { marketingMatchBatch } from "./tools/marketing-match-batch.js";
25
+ import { marketingMatchRequest } from "./tools/marketing-match-request.js";
26
+ import { marketingEnquiry } from "./tools/marketing-enquiry.js";
27
+ import { customerPreferences } from "./tools/customer-preferences.js";
28
+ import { supplier } from "./tools/supplier.js";
29
+ import { closeDriver } from "./lib/neo4j.js";
30
+
31
+ const accountId = process.env.ACCOUNT_ID;
32
+ if (!accountId) {
33
+ throw new Error("ACCOUNT_ID environment variable is required");
34
+ }
35
+
36
+ const server = new McpServer({
37
+ name: "maxy-loop",
38
+ version: "0.2.0",
39
+ });
40
+
41
+ const ALL_PERMISSIONS = z.enum([
42
+ "properties", "people", "viewings", "feedback", "team",
43
+ "marketing", "customer", "supplier",
44
+ ]);
45
+
46
+ console.error(`[loop] server started, account=${accountId}`);
47
+
48
+ // Helper: wrap a tool function in standard error handling
49
+ function toolHandler(fn: (p: Record<string, unknown>) => Promise<string>) {
50
+ return async (params: Record<string, unknown>) => {
51
+ try {
52
+ const text = await fn({ ...params, accountId });
53
+ return { content: [{ type: "text" as const, text }] };
54
+ } catch (err) {
55
+ return {
56
+ content: [{
57
+ type: "text" as const,
58
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
59
+ }],
60
+ isError: true,
61
+ };
62
+ }
63
+ };
64
+ }
65
+
66
+ // ─────────────────────────────────────────────────────────────────
67
+ // Key Management Tools (3)
68
+ // ─────────────────────────────────────────────────────────────────
69
+
70
+ server.tool(
71
+ "loop-key-register",
72
+ "Register a Loop CRM API key for a team. Validates the key against Loop's API, encrypts it, and stores it in the graph. Each key maps to one team.",
73
+ {
74
+ teamName: z.string().min(1).describe("Human-readable name for this team (e.g. 'Muvin Main Office')"),
75
+ apiKey: z.string().min(10).describe("Loop API key (X-Api-Key header value)"),
76
+ permissions: z
77
+ .array(ALL_PERMISSIONS)
78
+ .optional()
79
+ .describe("Endpoint groups this key can access (default: all 8 groups)"),
80
+ },
81
+ async (params) => {
82
+ try {
83
+ const result = await keyRegister({ ...params, accountId });
84
+ let text = `Team "${params.teamName}" registered successfully.`;
85
+ if (result.warning) text += `\n\nWarning: ${result.warning}`;
86
+ return { content: [{ type: "text" as const, text }] };
87
+ } catch (err) {
88
+ return {
89
+ content: [{
90
+ type: "text" as const,
91
+ text: `Failed to register key: ${err instanceof Error ? err.message : String(err)}`,
92
+ }],
93
+ isError: true,
94
+ };
95
+ }
96
+ }
97
+ );
98
+
99
+ server.tool(
100
+ "loop-key-deregister",
101
+ "Remove a registered Loop CRM team key. The key is permanently deleted from the graph.",
102
+ { teamName: z.string().min(1).describe("Name of the team to remove") },
103
+ async ({ teamName }) => {
104
+ try {
105
+ await keyDeregister({ teamName, accountId });
106
+ return { content: [{ type: "text" as const, text: `Team "${teamName}" deregistered.` }] };
107
+ } catch (err) {
108
+ return {
109
+ content: [{
110
+ type: "text" as const,
111
+ text: `Failed to deregister: ${err instanceof Error ? err.message : String(err)}`,
112
+ }],
113
+ isError: true,
114
+ };
115
+ }
116
+ }
117
+ );
118
+
119
+ server.tool(
120
+ "loop-key-list",
121
+ "List all registered Loop CRM teams for this account. Shows team names, addresses, and permissions. Never reveals API key values.",
122
+ {},
123
+ toolHandler(async (p) => keyList({ accountId: p.accountId as string }))
124
+ );
125
+
126
+ // ─────────────────────────────────────────────────────────────────
127
+ // People Tools (2) — permission: people
128
+ // ─────────────────────────────────────────────────────────────────
129
+
130
+ server.tool(
131
+ "loop-people-search",
132
+ "Search people in Loop CRM. Without a role, searches all contacts. With a role (buyers/sellers/renters/landlords), returns role-specific results with rich filters.",
133
+ {
134
+ role: z.enum(["buyers", "sellers", "renters", "landlords"]).optional().describe("Role to search (omit for all people)"),
135
+ searchTerm: z.string().optional().describe("Name or contact search"),
136
+ maxPrice: z.number().optional().describe("Max price (buyers) or max price filter (sellers)"),
137
+ minPrice: z.number().optional().describe("Min price (sellers)"),
138
+ minBeds: z.number().optional().describe("Min bedrooms (buyers/renters)"),
139
+ maxRent: z.number().optional().describe("Max rent (renters only)"),
140
+ searchAreas: z.string().optional().describe("Comma-separated outcodes e.g. 'SN7,OX12' (buyers/renters)"),
141
+ propertyTypes: z.string().optional().describe("Comma-separated property types (buyers/renters)"),
142
+ startDate: z.string().optional().describe("Start date filter (sellers/landlords)"),
143
+ endDate: z.string().optional().describe("End date filter (sellers/landlords)"),
144
+ teamName: z.string().optional().describe("Query a specific team only"),
145
+ limit: z.number().int().optional().describe("Max results (default 200)"),
146
+ },
147
+ toolHandler(async (p) => peopleSearch(p as Parameters<typeof peopleSearch>[0]))
148
+ );
149
+
150
+ server.tool(
151
+ "loop-people-detail",
152
+ "Get full details for a specific person by ID. Optionally specify a role (buyers/sellers/renters/landlords) for role-specific detail.",
153
+ {
154
+ personId: z.number().int().describe("Person ID"),
155
+ role: z.enum(["buyers", "sellers", "renters", "landlords"]).optional().describe("Role for role-specific detail view"),
156
+ teamName: z.string().optional().describe("Query a specific team only"),
157
+ },
158
+ toolHandler(async (p) => peopleDetail(p as Parameters<typeof peopleDetail>[0]))
159
+ );
160
+
161
+ // ─────────────────────────────────────────────────────────────────
162
+ // Property Tools (4) — permission: properties
163
+ // ─────────────────────────────────────────────────────────────────
164
+
165
+ server.tool(
166
+ "loop-property-search",
167
+ "Search properties in Loop CRM. Queries /property/residential/sales and /property/residential/lettings. Use department to narrow to one type.",
168
+ {
169
+ department: z.enum(["sales", "lettings", "both"]).optional().describe("Department (default: both)"),
170
+ searchTerm: z.string().optional().describe("Address or keyword search"),
171
+ minPrice: z.number().optional().describe("Minimum price"),
172
+ maxPrice: z.number().optional().describe("Maximum price"),
173
+ minBedrooms: z.number().int().optional().describe("Minimum bedrooms"),
174
+ maxBedrooms: z.number().int().optional().describe("Maximum bedrooms"),
175
+ propertyStatuses: z.string().optional().describe("Comma-separated statuses (e.g. 'forSale,underOffer')"),
176
+ propertyTypes: z.string().optional().describe("Comma-separated property types"),
177
+ teamName: z.string().optional().describe("Query a specific team only"),
178
+ limit: z.number().int().optional().describe("Max results (default 200)"),
179
+ },
180
+ toolHandler(async (p) => propertySearch(p as Parameters<typeof propertySearch>[0]))
181
+ );
182
+
183
+ server.tool(
184
+ "loop-property-detail",
185
+ "Get full details for a specific property by ID and department. Optionally include a preview hash for the public preview.",
186
+ {
187
+ propertyId: z.number().int().describe("Property ID"),
188
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
189
+ previewHash: z.number().int().optional().describe("Preview hash for public preview URL"),
190
+ teamName: z.string().optional().describe("Query a specific team only"),
191
+ },
192
+ toolHandler(async (p) => propertyDetail(p as Parameters<typeof propertyDetail>[0]))
193
+ );
194
+
195
+ server.tool(
196
+ "loop-property-listed",
197
+ "Get properties listed on a specific channel (Rightmove, Zoopla, OnTheMarket, website). Optionally include sold properties.",
198
+ {
199
+ channel: z.enum(["rightmove", "onTheMarket", "zoopla", "website"]).describe("Listing channel"),
200
+ department: z.enum(["sales", "lettings", "both"]).optional().describe("Department (default: both)"),
201
+ includeSold: z.boolean().optional().describe("Include sold gallery (default: false)"),
202
+ teamName: z.string().optional().describe("Query a specific team only"),
203
+ limit: z.number().int().optional().describe("Max results (default 200)"),
204
+ },
205
+ toolHandler(async (p) => propertyListed(p as Parameters<typeof propertyListed>[0]))
206
+ );
207
+
208
+ server.tool(
209
+ "loop-property-request",
210
+ "Submit a viewing, callback, or information request for a property. Requires a specific team.",
211
+ {
212
+ teamName: z.string().min(1).describe("Team to submit the request through"),
213
+ propertyId: z.number().int().describe("Property ID"),
214
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
215
+ action: z.enum(["viewing", "call-back", "information"]).describe("Request type"),
216
+ name: z.string().optional().describe("Requester name"),
217
+ email: z.string().optional().describe("Requester email"),
218
+ phone: z.string().optional().describe("Requester phone"),
219
+ message: z.string().optional().describe("Additional message"),
220
+ },
221
+ toolHandler(async (p) => propertyRequest(p as Parameters<typeof propertyRequest>[0]))
222
+ );
223
+
224
+ // ─────────────────────────────────────────────────────────────────
225
+ // Viewing Tools (4) — permission: viewings
226
+ // ─────────────────────────────────────────────────────────────────
227
+
228
+ server.tool(
229
+ "loop-viewing-search",
230
+ "Search viewings in Loop CRM. Queries /residential/sales/viewings and /residential/lettings/viewings.",
231
+ {
232
+ department: z.enum(["sales", "lettings", "both"]).optional().describe("Department (default: both)"),
233
+ searchTerm: z.string().optional().describe("Search viewings"),
234
+ appointmentStartDate: z.string().optional().describe("Filter from date (ISO format)"),
235
+ appointmentEndDate: z.string().optional().describe("Filter to date (ISO format)"),
236
+ status: z.string().optional().describe("Viewing status filter"),
237
+ teamName: z.string().optional().describe("Query a specific team only"),
238
+ limit: z.number().int().optional().describe("Max results (default 200)"),
239
+ },
240
+ toolHandler(async (p) => viewingSearch(p as Parameters<typeof viewingSearch>[0]))
241
+ );
242
+
243
+ server.tool(
244
+ "loop-viewing-detail",
245
+ "Get full details for a specific viewing by ID and department.",
246
+ {
247
+ viewingId: z.number().int().describe("Viewing ID"),
248
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
249
+ teamName: z.string().optional().describe("Query a specific team only"),
250
+ },
251
+ toolHandler(async (p) => viewingDetail(p as Parameters<typeof viewingDetail>[0]))
252
+ );
253
+
254
+ server.tool(
255
+ "loop-viewing-create",
256
+ "Create a new viewing in Loop CRM. Creates the viewing and associated buyer/renter record.",
257
+ {
258
+ teamName: z.string().min(1).describe("Team to create the viewing for"),
259
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
260
+ propertyId: z.number().int().describe("Property ID"),
261
+ date: z.string().describe("Viewing date (YYYY-MM-DD)"),
262
+ time: z.string().describe("Viewing time (HH:mm)"),
263
+ attendeeName: z.string().describe("Attendee full name"),
264
+ attendeeEmail: z.string().optional().describe("Attendee email"),
265
+ attendeePhone: z.string().optional().describe("Attendee phone"),
266
+ },
267
+ toolHandler(async (p) => viewingCreate(p as Parameters<typeof viewingCreate>[0]))
268
+ );
269
+
270
+ server.tool(
271
+ "loop-viewing-update",
272
+ "Add a note or record feedback for a viewing. For feedback, specify the party: buyer/seller (sales) or renter/landlord (lettings).",
273
+ {
274
+ teamName: z.string().min(1).describe("Team that owns the viewing"),
275
+ viewingId: z.number().int().describe("Viewing ID"),
276
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
277
+ action: z.enum(["note", "feedback"]).describe("note or feedback"),
278
+ text: z.string().describe("Note text or feedback content"),
279
+ feedbackParty: z.enum(["buyer", "seller", "renter", "landlord"]).optional().describe("Required for feedback: whose feedback"),
280
+ },
281
+ toolHandler(async (p) => viewingUpdate(p as Parameters<typeof viewingUpdate>[0]))
282
+ );
283
+
284
+ // ─────────────────────────────────────────────────────────────────
285
+ // Feedback Tools (2) — permission: feedback
286
+ // ─────────────────────────────────────────────────────────────────
287
+
288
+ server.tool(
289
+ "loop-feedback-get",
290
+ "Get feedback for a specific viewing. Reads from /feedback/residential/{department}/viewings/{id}.",
291
+ {
292
+ teamName: z.string().min(1).describe("Team that owns the viewing"),
293
+ viewingId: z.number().int().describe("Viewing ID"),
294
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
295
+ },
296
+ toolHandler(async (p) => feedbackGet(p as Parameters<typeof feedbackGet>[0]))
297
+ );
298
+
299
+ server.tool(
300
+ "loop-feedback-submit",
301
+ "Submit feedback for a viewing. Writes to /feedback/residential/{department}/viewings/{id}/feedback.",
302
+ {
303
+ teamName: z.string().min(1).describe("Team that owns the viewing"),
304
+ viewingId: z.number().int().describe("Viewing ID"),
305
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
306
+ feedback: z.string().describe("Feedback text"),
307
+ },
308
+ toolHandler(async (p) => feedbackSubmit(p as Parameters<typeof feedbackSubmit>[0]))
309
+ );
310
+
311
+ // ─────────────────────────────────────────────────────────────────
312
+ // Team Tools (2) — permission: team
313
+ // ─────────────────────────────────────────────────────────────────
314
+
315
+ server.tool(
316
+ "loop-team-info",
317
+ "Get team details from Loop CRM. Returns team name, address, phone, and email.",
318
+ {
319
+ teamName: z.string().optional().describe("Query a specific team only (omit for all teams)"),
320
+ },
321
+ toolHandler(async (p) => teamInfo(p as Parameters<typeof teamInfo>[0]))
322
+ );
323
+
324
+ server.tool(
325
+ "loop-team-availability",
326
+ "Get available time slots for a specific agent on a given date.",
327
+ {
328
+ agentId: z.string().describe("Agent GUID identifier"),
329
+ searchDate: z.string().describe("Date to check availability (YYYY-MM-DD)"),
330
+ teamName: z.string().optional().describe("Query a specific team only"),
331
+ },
332
+ toolHandler(async (p) => teamAvailability(p as Parameters<typeof teamAvailability>[0]))
333
+ );
334
+
335
+ // ─────────────────────────────────────────────────────────────────
336
+ // Marketing Tools (4) — permission: marketing
337
+ // ─────────────────────────────────────────────────────────────────
338
+
339
+ server.tool(
340
+ "loop-marketing-match",
341
+ "Get matching property detail and optionally the team profile for that match.",
342
+ {
343
+ propertyId: z.number().int().describe("Property ID"),
344
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
345
+ includeTeamProfile: z.boolean().optional().describe("Include the team profile for this match (default: false)"),
346
+ teamName: z.string().optional().describe("Query a specific team only"),
347
+ },
348
+ toolHandler(async (p) => marketingMatchDetail(p as Parameters<typeof marketingMatchDetail>[0]))
349
+ );
350
+
351
+ server.tool(
352
+ "loop-marketing-match-batch",
353
+ "Get batch matching results for multiple property IDs.",
354
+ {
355
+ propertyIds: z.array(z.number().int()).describe("Array of property IDs to match"),
356
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
357
+ teamName: z.string().optional().describe("Query a specific team only"),
358
+ },
359
+ toolHandler(async (p) => marketingMatchBatch(p as Parameters<typeof marketingMatchBatch>[0]))
360
+ );
361
+
362
+ server.tool(
363
+ "loop-marketing-match-request",
364
+ "Submit a viewing, information, or callback request for a matching property.",
365
+ {
366
+ teamName: z.string().min(1).describe("Team to submit the request through"),
367
+ propertyId: z.number().int().describe("Matching property ID"),
368
+ department: z.enum(["sales", "lettings"]).describe("sales or lettings"),
369
+ action: z.enum(["viewing", "information", "callback"]).describe("Request type"),
370
+ name: z.string().optional().describe("Requester name"),
371
+ email: z.string().optional().describe("Requester email"),
372
+ phone: z.string().optional().describe("Requester phone"),
373
+ message: z.string().optional().describe("Additional message"),
374
+ },
375
+ toolHandler(async (p) => marketingMatchRequest(p as Parameters<typeof marketingMatchRequest>[0]))
376
+ );
377
+
378
+ server.tool(
379
+ "loop-marketing-enquiry",
380
+ "Marketing enquiry operations: submit seller enquiries, manage auto-responder interactions.",
381
+ {
382
+ teamName: z.string().min(1).describe("Team to submit through"),
383
+ action: z.enum([
384
+ "seller-enquiry", "autoresponder-get", "autoresponder-answers",
385
+ "autoresponder-details", "autoresponder-refer",
386
+ ]).describe("Enquiry action"),
387
+ sellerEnquiryData: z.record(z.string(), z.unknown()).optional().describe("Seller enquiry request body (for seller-enquiry)"),
388
+ autoResponderId: z.number().int().optional().describe("Auto-responder enquiry ID"),
389
+ autoResponderKey: z.string().optional().describe("Auto-responder key (UUID)"),
390
+ answers: z.array(z.unknown()).optional().describe("Auto-responder answers array"),
391
+ details: z.record(z.string(), z.unknown()).optional().describe("Auto-responder details object"),
392
+ },
393
+ toolHandler(async (p) => marketingEnquiry(p as Parameters<typeof marketingEnquiry>[0]))
394
+ );
395
+
396
+ // ─────────────────────────────────────────────────────────────────
397
+ // Customer Tools (1) — permission: customer
398
+ // ─────────────────────────────────────────────────────────────────
399
+
400
+ server.tool(
401
+ "loop-customer-preferences",
402
+ "Read or write customer preferences for a person. Action 'read' returns current preferences, 'write' updates them.",
403
+ {
404
+ teamName: z.string().min(1).describe("Team for this operation"),
405
+ personCode: z.number().int().describe("Person code (ID)"),
406
+ action: z.enum(["read", "write"]).describe("read or write"),
407
+ preferences: z.record(z.string(), z.unknown()).optional().describe("Preferences object (required for write)"),
408
+ },
409
+ toolHandler(async (p) => customerPreferences(p as Parameters<typeof customerPreferences>[0]))
410
+ );
411
+
412
+ // ─────────────────────────────────────────────────────────────────
413
+ // Supplier Tools (1) — permission: supplier
414
+ // ─────────────────────────────────────────────────────────────────
415
+
416
+ server.tool(
417
+ "loop-supplier",
418
+ "Supplier operations: maintenance jobs, quotes, board contractor jobs. Supports listing, completing, and quoting.",
419
+ {
420
+ teamName: z.string().min(1).describe("Team for this operation"),
421
+ action: z.enum([
422
+ "maintenance-jobs", "maintenance-complete", "maintenance-quotes",
423
+ "maintenance-submit-quote", "board-jobs", "board-complete",
424
+ ]).describe("Supplier action"),
425
+ code: z.string().describe("Unique code assigned to the contractor"),
426
+ jobId: z.number().int().optional().describe("Job ID (required for all actions except maintenance-submit-quote)"),
427
+ quoteId: z.number().int().optional().describe("Quote ID (for maintenance-submit-quote)"),
428
+ quoteData: z.record(z.string(), z.unknown()).optional().describe("Quote data (for maintenance-submit-quote)"),
429
+ completionData: z.record(z.string(), z.unknown()).optional().describe("Completion data (for board-complete)"),
430
+ },
431
+ toolHandler(async (p) => supplier(p as Parameters<typeof supplier>[0]))
432
+ );
433
+
434
+ // ─────────────────────────────────────────────────────────────────
435
+ // Lifecycle
436
+ // ─────────────────────────────────────────────────────────────────
437
+
438
+ process.on("SIGINT", async () => {
439
+ await closeDriver();
440
+ process.exit(0);
441
+ });
442
+
443
+ const transport = new StdioServerTransport();
444
+ await server.connect(transport);