@rubytech/create-maxy-code 0.1.59 → 0.1.61

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 (284) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  3. package/payload/platform/plugins/brochures/skills/property-brochure/SKILL.md +26 -3
  4. package/payload/platform/plugins/brochures/skills/property-brochure/references/registers.md +1 -1
  5. package/payload/platform/plugins/docs/references/admin-session.md +2 -2
  6. package/payload/platform/plugins/docs/references/internals.md +1 -1
  7. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  8. package/payload/platform/plugins/epc/.claude-plugin/plugin.json +17 -0
  9. package/payload/platform/plugins/epc/PLUGIN.md +85 -0
  10. package/payload/platform/plugins/epc/mcp/dist/index.d.ts +2 -0
  11. package/payload/platform/plugins/epc/mcp/dist/index.d.ts.map +1 -0
  12. package/payload/platform/plugins/epc/mcp/dist/index.js +117 -0
  13. package/payload/platform/plugins/epc/mcp/dist/index.js.map +1 -0
  14. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.d.ts +3 -0
  15. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.d.ts.map +1 -0
  16. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.js +72 -0
  17. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.js.map +1 -0
  18. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.d.ts +60 -0
  19. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.d.ts.map +1 -0
  20. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.js +181 -0
  21. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.js.map +1 -0
  22. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.d.ts +3 -0
  23. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  24. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.js +49 -0
  25. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.js.map +1 -0
  26. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.d.ts +15 -0
  27. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.d.ts.map +1 -0
  28. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.js +130 -0
  29. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.js.map +1 -0
  30. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.d.ts +5 -0
  31. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.d.ts.map +1 -0
  32. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.js +38 -0
  33. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.js.map +1 -0
  34. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.d.ts +4 -0
  35. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  36. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.js +9 -0
  37. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.js.map +1 -0
  38. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.d.ts +4 -0
  39. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.d.ts.map +1 -0
  40. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.js +10 -0
  41. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.js.map +1 -0
  42. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.d.ts +5 -0
  43. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.d.ts.map +1 -0
  44. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.js +36 -0
  45. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.js.map +1 -0
  46. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.d.ts +10 -0
  47. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.d.ts.map +1 -0
  48. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.js +30 -0
  49. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.js.map +1 -0
  50. package/payload/platform/plugins/epc/mcp/package-lock.json +2566 -0
  51. package/payload/platform/plugins/epc/mcp/package.json +21 -0
  52. package/payload/platform/plugins/epc/mcp/src/__tests__/epc-api.test.ts +251 -0
  53. package/payload/platform/plugins/epc/mcp/src/__tests__/key-roundtrip.test.ts +113 -0
  54. package/payload/platform/plugins/epc/mcp/src/__tests__/lookup.test.ts +181 -0
  55. package/payload/platform/plugins/epc/mcp/src/__tests__/schema-parity.test.ts +54 -0
  56. package/payload/platform/plugins/epc/mcp/src/index.ts +156 -0
  57. package/payload/platform/plugins/epc/mcp/src/lib/crypto.ts +79 -0
  58. package/payload/platform/plugins/epc/mcp/src/lib/epc-api.ts +241 -0
  59. package/payload/platform/plugins/epc/mcp/src/lib/file-crypto.ts +55 -0
  60. package/payload/platform/plugins/epc/mcp/src/lib/key-store.ts +172 -0
  61. package/payload/platform/plugins/epc/mcp/src/lib/neo4j.ts +47 -0
  62. package/payload/platform/plugins/epc/mcp/src/tools/key-deregister.ts +9 -0
  63. package/payload/platform/plugins/epc/mcp/src/tools/key-list.ts +10 -0
  64. package/payload/platform/plugins/epc/mcp/src/tools/key-register.ts +44 -0
  65. package/payload/platform/plugins/epc/mcp/src/tools/lookup.ts +43 -0
  66. package/payload/platform/plugins/epc/mcp/tsconfig.json +20 -0
  67. package/payload/platform/plugins/epc/mcp/vitest.config.ts +8 -0
  68. package/payload/platform/plugins/loop/PLUGIN.md +13 -1
  69. package/payload/platform/plugins/loop/mcp/dist/index.js +8 -7
  70. package/payload/platform/plugins/loop/mcp/dist/index.js.map +1 -1
  71. package/payload/platform/plugins/loop/mcp/dist/lib/file-crypto.d.ts +3 -0
  72. package/payload/platform/plugins/loop/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  73. package/payload/platform/plugins/loop/mcp/dist/lib/file-crypto.js +48 -0
  74. package/payload/platform/plugins/loop/mcp/dist/lib/file-crypto.js.map +1 -0
  75. package/payload/platform/plugins/loop/mcp/dist/lib/key-store.d.ts +22 -0
  76. package/payload/platform/plugins/loop/mcp/dist/lib/key-store.d.ts.map +1 -0
  77. package/payload/platform/plugins/loop/mcp/dist/lib/key-store.js +165 -0
  78. package/payload/platform/plugins/loop/mcp/dist/lib/key-store.js.map +1 -0
  79. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.d.ts +1 -1
  80. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -1
  81. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.js +4 -39
  82. package/payload/platform/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -1
  83. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.d.ts.map +1 -1
  84. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.js +5 -15
  85. package/payload/platform/plugins/loop/mcp/dist/tools/key-deregister.js.map +1 -1
  86. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.d.ts.map +1 -1
  87. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.js +11 -28
  88. package/payload/platform/plugins/loop/mcp/dist/tools/key-register.js.map +1 -1
  89. package/payload/platform/plugins/loop/mcp/src/index.ts +10 -7
  90. package/payload/platform/plugins/loop/mcp/src/lib/file-crypto.ts +54 -0
  91. package/payload/platform/plugins/loop/mcp/src/lib/key-store.ts +221 -0
  92. package/payload/platform/plugins/loop/mcp/src/lib/loop-api.ts +5 -42
  93. package/payload/platform/plugins/loop/mcp/src/tools/key-deregister.ts +5 -20
  94. package/payload/platform/plugins/loop/mcp/src/tools/key-register.ts +14 -39
  95. package/payload/platform/plugins/preval/.claude-plugin/plugin.json +1 -1
  96. package/payload/platform/plugins/preval/PLUGIN.md +23 -17
  97. package/payload/platform/plugins/preval/skills/property-preval/SKILL.md +78 -38
  98. package/payload/platform/plugins/preval/skills/property-preval/references/render.py +288 -81
  99. package/payload/platform/plugins/preval/skills/property-preval/references/template-inputs.schema.json +125 -80
  100. package/payload/platform/plugins/preval/skills/property-preval/references/template.html +184 -124
  101. package/payload/platform/plugins/property-data/PLUGIN.md +14 -1
  102. package/payload/platform/plugins/property-data/mcp/dist/index.js +14 -11
  103. package/payload/platform/plugins/property-data/mcp/dist/index.js.map +1 -1
  104. package/payload/platform/plugins/property-data/mcp/dist/lib/file-crypto.d.ts +3 -0
  105. package/payload/platform/plugins/property-data/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  106. package/payload/platform/plugins/property-data/mcp/dist/lib/file-crypto.js +49 -0
  107. package/payload/platform/plugins/property-data/mcp/dist/lib/file-crypto.js.map +1 -0
  108. package/payload/platform/plugins/property-data/mcp/dist/lib/key-store.d.ts +15 -0
  109. package/payload/platform/plugins/property-data/mcp/dist/lib/key-store.d.ts.map +1 -0
  110. package/payload/platform/plugins/property-data/mcp/dist/lib/key-store.js +135 -0
  111. package/payload/platform/plugins/property-data/mcp/dist/lib/key-store.js.map +1 -0
  112. package/payload/platform/plugins/property-data/mcp/dist/lib/propertydata-api.d.ts +0 -1
  113. package/payload/platform/plugins/property-data/mcp/dist/lib/propertydata-api.d.ts.map +1 -1
  114. package/payload/platform/plugins/property-data/mcp/dist/lib/propertydata-api.js +11 -22
  115. package/payload/platform/plugins/property-data/mcp/dist/lib/propertydata-api.js.map +1 -1
  116. package/payload/platform/plugins/property-data/mcp/dist/tools/endpoints.d.ts.map +1 -1
  117. package/payload/platform/plugins/property-data/mcp/dist/tools/endpoints.js +2 -2
  118. package/payload/platform/plugins/property-data/mcp/dist/tools/endpoints.js.map +1 -1
  119. package/payload/platform/plugins/property-data/mcp/dist/tools/key-deregister.d.ts.map +1 -1
  120. package/payload/platform/plugins/property-data/mcp/dist/tools/key-deregister.js +5 -15
  121. package/payload/platform/plugins/property-data/mcp/dist/tools/key-deregister.js.map +1 -1
  122. package/payload/platform/plugins/property-data/mcp/dist/tools/key-register.d.ts.map +1 -1
  123. package/payload/platform/plugins/property-data/mcp/dist/tools/key-register.js +30 -23
  124. package/payload/platform/plugins/property-data/mcp/dist/tools/key-register.js.map +1 -1
  125. package/payload/platform/plugins/property-data/mcp/src/index.ts +16 -11
  126. package/payload/platform/plugins/property-data/mcp/src/lib/file-crypto.ts +55 -0
  127. package/payload/platform/plugins/property-data/mcp/src/lib/key-store.ts +181 -0
  128. package/payload/platform/plugins/property-data/mcp/src/lib/propertydata-api.ts +9 -24
  129. package/payload/platform/plugins/property-data/mcp/src/tools/endpoints.ts +2 -2
  130. package/payload/platform/plugins/property-data/mcp/src/tools/key-deregister.ts +5 -17
  131. package/payload/platform/plugins/property-data/mcp/src/tools/key-register.ts +29 -33
  132. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  133. package/payload/platform/services/claude-session-manager/dist/http-server.js +6 -1
  134. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  135. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +7 -0
  136. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  137. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -0
  138. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  139. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  140. package/payload/platform/templates/agents/admin/SOUL.md +1 -1
  141. package/payload/premium-plugins/real-agent/BUNDLE.md +4 -2
  142. package/payload/premium-plugins/real-agent/agents/valuer.md +1 -1
  143. package/payload/premium-plugins/real-agent/plugins/.claude-plugin/marketplace.json +5 -0
  144. package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/SKILL.md +26 -3
  145. package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/references/registers.md +1 -1
  146. package/payload/premium-plugins/real-agent/plugins/epc/.claude-plugin/plugin.json +17 -0
  147. package/payload/premium-plugins/real-agent/plugins/epc/PLUGIN.md +85 -0
  148. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.d.ts +2 -0
  149. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.d.ts.map +1 -0
  150. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.js +117 -0
  151. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.js.map +1 -0
  152. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.d.ts +3 -0
  153. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.d.ts.map +1 -0
  154. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.js +72 -0
  155. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.js.map +1 -0
  156. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.d.ts +60 -0
  157. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.d.ts.map +1 -0
  158. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.js +181 -0
  159. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.js.map +1 -0
  160. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.d.ts +3 -0
  161. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  162. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.js +49 -0
  163. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.js.map +1 -0
  164. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.d.ts +15 -0
  165. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.d.ts.map +1 -0
  166. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.js +130 -0
  167. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.js.map +1 -0
  168. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.d.ts +5 -0
  169. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.d.ts.map +1 -0
  170. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.js +38 -0
  171. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.js.map +1 -0
  172. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.d.ts +4 -0
  173. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  174. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.js +9 -0
  175. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.js.map +1 -0
  176. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.d.ts +4 -0
  177. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.d.ts.map +1 -0
  178. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.js +10 -0
  179. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.js.map +1 -0
  180. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.d.ts +5 -0
  181. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.d.ts.map +1 -0
  182. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.js +36 -0
  183. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.js.map +1 -0
  184. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.d.ts +10 -0
  185. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.d.ts.map +1 -0
  186. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.js +30 -0
  187. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.js.map +1 -0
  188. package/payload/premium-plugins/real-agent/plugins/epc/mcp/package-lock.json +2566 -0
  189. package/payload/premium-plugins/real-agent/plugins/epc/mcp/package.json +21 -0
  190. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/epc-api.test.ts +251 -0
  191. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/key-roundtrip.test.ts +113 -0
  192. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/lookup.test.ts +181 -0
  193. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/schema-parity.test.ts +54 -0
  194. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/index.ts +156 -0
  195. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/crypto.ts +79 -0
  196. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/epc-api.ts +241 -0
  197. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/file-crypto.ts +55 -0
  198. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/key-store.ts +172 -0
  199. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/neo4j.ts +47 -0
  200. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-deregister.ts +9 -0
  201. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-list.ts +10 -0
  202. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-register.ts +44 -0
  203. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/lookup.ts +43 -0
  204. package/payload/premium-plugins/real-agent/plugins/epc/mcp/tsconfig.json +20 -0
  205. package/payload/premium-plugins/real-agent/plugins/epc/mcp/vitest.config.ts +8 -0
  206. package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +13 -1
  207. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js +8 -7
  208. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js.map +1 -1
  209. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/file-crypto.d.ts +3 -0
  210. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  211. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/file-crypto.js +48 -0
  212. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/file-crypto.js.map +1 -0
  213. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/key-store.d.ts +22 -0
  214. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/key-store.d.ts.map +1 -0
  215. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/key-store.js +165 -0
  216. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/key-store.js.map +1 -0
  217. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.d.ts +1 -1
  218. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.d.ts.map +1 -1
  219. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.js +4 -39
  220. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/lib/loop-api.js.map +1 -1
  221. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-deregister.d.ts.map +1 -1
  222. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-deregister.js +5 -15
  223. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-deregister.js.map +1 -1
  224. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-register.d.ts.map +1 -1
  225. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-register.js +11 -28
  226. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/key-register.js.map +1 -1
  227. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/index.ts +10 -7
  228. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/file-crypto.ts +54 -0
  229. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/key-store.ts +221 -0
  230. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/lib/loop-api.ts +5 -42
  231. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/key-deregister.ts +5 -20
  232. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/key-register.ts +14 -39
  233. package/payload/premium-plugins/real-agent/plugins/preval/.claude-plugin/plugin.json +1 -1
  234. package/payload/premium-plugins/real-agent/plugins/preval/PLUGIN.md +23 -17
  235. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/SKILL.md +78 -38
  236. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/render.py +288 -81
  237. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template-inputs.schema.json +125 -80
  238. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template.html +184 -124
  239. package/payload/premium-plugins/real-agent/plugins/property-data/PLUGIN.md +14 -1
  240. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/index.js +14 -11
  241. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/index.js.map +1 -1
  242. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/file-crypto.d.ts +3 -0
  243. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  244. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/file-crypto.js +49 -0
  245. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/file-crypto.js.map +1 -0
  246. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/key-store.d.ts +15 -0
  247. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/key-store.d.ts.map +1 -0
  248. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/key-store.js +135 -0
  249. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/key-store.js.map +1 -0
  250. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/propertydata-api.d.ts +0 -1
  251. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/propertydata-api.d.ts.map +1 -1
  252. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/propertydata-api.js +11 -22
  253. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/lib/propertydata-api.js.map +1 -1
  254. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/endpoints.d.ts.map +1 -1
  255. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/endpoints.js +2 -2
  256. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/endpoints.js.map +1 -1
  257. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-deregister.d.ts.map +1 -1
  258. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-deregister.js +5 -15
  259. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-deregister.js.map +1 -1
  260. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-register.d.ts.map +1 -1
  261. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-register.js +30 -23
  262. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/dist/tools/key-register.js.map +1 -1
  263. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/index.ts +16 -11
  264. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/lib/file-crypto.ts +55 -0
  265. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/lib/key-store.ts +181 -0
  266. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/lib/propertydata-api.ts +9 -24
  267. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/tools/endpoints.ts +2 -2
  268. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/tools/key-deregister.ts +5 -17
  269. package/payload/premium-plugins/real-agent/plugins/property-data/mcp/src/tools/key-register.ts +29 -33
  270. package/payload/server/public/assets/{admin-nqCBFiSl.js → admin-DhN3G8W7.js} +51 -51
  271. package/payload/server/public/assets/{data-BGUAGVkV.js → data-B2ZVXOcE.js} +1 -1
  272. package/payload/server/public/assets/{graph-MvYxZOBF.js → graph-BAMGPHrK.js} +1 -1
  273. package/payload/server/public/assets/{graph-labels-D865qb3K.js → graph-labels-D9eBbvxo.js} +1 -1
  274. package/payload/server/public/assets/{page-C2b1nlOc.js → page-CV27Al6Z.js} +1 -1
  275. package/payload/server/public/assets/{page--hOVRrgN.js → page-DjdVMWCz.js} +1 -1
  276. package/payload/server/public/data.html +3 -3
  277. package/payload/server/public/graph.html +3 -3
  278. package/payload/server/public/index.html +4 -4
  279. package/payload/server/server.js +7 -19
  280. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts +0 -51
  281. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts.map +0 -1
  282. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js +0 -196
  283. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js.map +0 -1
  284. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/package.json +0 -7
@@ -0,0 +1,221 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { getSession } from "./neo4j.js";
5
+ import { encrypt, decrypt } from "./crypto.js";
6
+ import { encryptWith, decryptWith } from "./file-crypto.js";
7
+
8
+ // Storage abstraction so the same MCP body runs against Neo4j (platform mode)
9
+ // or an on-disk encrypted file (standalone mode, spawned by Claude Code from
10
+ // ~/.claude/). Selected by LOOP_STANDALONE at boot. Loop is multi-team — each
11
+ // account can register many team keys — so the file backend stores a nested
12
+ // map and the store API mirrors that.
13
+
14
+ export interface LoopStoredKey {
15
+ teamName: string;
16
+ apiKey: string;
17
+ teamAddress: string;
18
+ agentId: string;
19
+ permissions: string[];
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+
24
+ export type LoopKeyInput = Omit<LoopStoredKey, "createdAt" | "updatedAt">;
25
+
26
+ export interface LoopKeyStore {
27
+ readonly backendName: string;
28
+ readonly backendPath: string;
29
+ loadAll(accountId: string): Promise<LoopStoredKey[]>;
30
+ save(accountId: string, key: LoopKeyInput): Promise<{ created: boolean }>;
31
+ delete(accountId: string, teamName: string): Promise<boolean>;
32
+ }
33
+
34
+ class Neo4jLoopKeyStore implements LoopKeyStore {
35
+ readonly backendName = "neo4j";
36
+ readonly backendPath = process.env.NEO4J_URI ?? "<unset>";
37
+
38
+ async loadAll(accountId: string): Promise<LoopStoredKey[]> {
39
+ const session = getSession();
40
+ try {
41
+ const result = await session.run(
42
+ `MATCH (k:LoopTeamKey {accountId: $accountId})
43
+ RETURN k.teamName AS teamName, k.encryptedKey AS encryptedKey,
44
+ k.teamAddress AS teamAddress, k.agentId AS agentId,
45
+ k.permissions AS permissions, k.createdAt AS createdAt,
46
+ k.updatedAt AS updatedAt
47
+ ORDER BY k.teamName`,
48
+ { accountId },
49
+ );
50
+ return result.records.map((r) => ({
51
+ teamName: r.get("teamName"),
52
+ apiKey: decrypt(r.get("encryptedKey")),
53
+ teamAddress: r.get("teamAddress") ?? "",
54
+ agentId: r.get("agentId") ?? "",
55
+ permissions: r.get("permissions") ?? [],
56
+ createdAt: r.get("createdAt") ?? "",
57
+ updatedAt: r.get("updatedAt") ?? "",
58
+ }));
59
+ } finally {
60
+ await session.close();
61
+ }
62
+ }
63
+
64
+ async save(accountId: string, key: LoopKeyInput): Promise<{ created: boolean }> {
65
+ const encryptedKey = encrypt(key.apiKey);
66
+ const now = new Date().toISOString();
67
+ const session = getSession();
68
+ try {
69
+ await session.run(
70
+ `CREATE INDEX loop_team_key_account_team IF NOT EXISTS
71
+ FOR (k:LoopTeamKey) ON (k.accountId, k.teamName)`,
72
+ {},
73
+ );
74
+ const result = await session.run(
75
+ `MERGE (k:LoopTeamKey {teamName: $teamName, accountId: $accountId})
76
+ ON CREATE SET
77
+ k.encryptedKey = $encryptedKey,
78
+ k.teamAddress = $teamAddress,
79
+ k.agentId = $agentId,
80
+ k.permissions = $permissions,
81
+ k.createdAt = $now,
82
+ k.updatedAt = $now
83
+ RETURN k.createdAt = $now AS created`,
84
+ {
85
+ teamName: key.teamName,
86
+ encryptedKey,
87
+ teamAddress: key.teamAddress,
88
+ agentId: key.agentId,
89
+ accountId,
90
+ permissions: key.permissions,
91
+ now,
92
+ },
93
+ );
94
+ return { created: Boolean(result.records[0]?.get("created")) };
95
+ } finally {
96
+ await session.close();
97
+ }
98
+ }
99
+
100
+ async delete(accountId: string, teamName: string): Promise<boolean> {
101
+ const session = getSession();
102
+ try {
103
+ const result = await session.run(
104
+ `MATCH (k:LoopTeamKey {teamName: $teamName, accountId: $accountId})
105
+ DELETE k
106
+ RETURN count(k) AS deleted`,
107
+ { teamName, accountId },
108
+ );
109
+ const deleted = result.records[0]?.get("deleted")?.toNumber?.() ?? 0;
110
+ return deleted > 0;
111
+ } finally {
112
+ await session.close();
113
+ }
114
+ }
115
+ }
116
+
117
+ const LABEL = "loop";
118
+
119
+ interface FileEnvelope {
120
+ [accountId: string]: {
121
+ [teamName: string]: {
122
+ encryptedKey: string;
123
+ teamAddress: string;
124
+ agentId: string;
125
+ permissions: string[];
126
+ createdAt: string;
127
+ updatedAt: string;
128
+ };
129
+ };
130
+ }
131
+
132
+ class FileLoopKeyStore implements LoopKeyStore {
133
+ readonly backendName = "file";
134
+ readonly backendPath: string;
135
+ private readonly storePath: string;
136
+ private readonly keyFilePath: string;
137
+
138
+ constructor() {
139
+ const home = homedir();
140
+ this.storePath = resolve(home, ".claude/.realagent-loop-key.enc");
141
+ this.keyFilePath = resolve(home, ".claude/.realagent-loop-encryption-key");
142
+ this.backendPath = this.storePath;
143
+ }
144
+
145
+ private ensureDir(): void {
146
+ mkdirSync(dirname(this.storePath), { recursive: true });
147
+ }
148
+
149
+ private readAll(): FileEnvelope {
150
+ if (!existsSync(this.storePath)) return {};
151
+ const blob = readFileSync(this.storePath, "utf-8").trim();
152
+ if (!blob) return {};
153
+ const json = decryptWith(blob, this.keyFilePath, LABEL);
154
+ return JSON.parse(json) as FileEnvelope;
155
+ }
156
+
157
+ private writeAll(env: FileEnvelope): void {
158
+ this.ensureDir();
159
+ const blob = encryptWith(JSON.stringify(env), this.keyFilePath, LABEL);
160
+ writeFileSync(this.storePath, blob, { mode: 0o600 });
161
+ }
162
+
163
+ async loadAll(accountId: string): Promise<LoopStoredKey[]> {
164
+ const env = this.readAll();
165
+ const account = env[accountId];
166
+ if (!account) return [];
167
+ return Object.entries(account)
168
+ .map(([teamName, entry]) => ({
169
+ teamName,
170
+ apiKey: decryptWith(entry.encryptedKey, this.keyFilePath, LABEL),
171
+ teamAddress: entry.teamAddress,
172
+ agentId: entry.agentId,
173
+ permissions: entry.permissions,
174
+ createdAt: entry.createdAt,
175
+ updatedAt: entry.updatedAt,
176
+ }))
177
+ .sort((a, b) => a.teamName.localeCompare(b.teamName));
178
+ }
179
+
180
+ async save(accountId: string, key: LoopKeyInput): Promise<{ created: boolean }> {
181
+ const env = this.readAll();
182
+ const account = env[accountId] ?? {};
183
+ const prev = account[key.teamName];
184
+ const now = new Date().toISOString();
185
+ const encryptedKey = encryptWith(key.apiKey, this.keyFilePath, LABEL);
186
+ account[key.teamName] = {
187
+ encryptedKey,
188
+ teamAddress: key.teamAddress,
189
+ agentId: key.agentId,
190
+ permissions: key.permissions,
191
+ createdAt: prev?.createdAt ?? now,
192
+ updatedAt: now,
193
+ };
194
+ env[accountId] = account;
195
+ this.writeAll(env);
196
+ return { created: !prev };
197
+ }
198
+
199
+ async delete(accountId: string, teamName: string): Promise<boolean> {
200
+ const env = this.readAll();
201
+ const account = env[accountId];
202
+ if (!account || !(teamName in account)) return false;
203
+ delete account[teamName];
204
+ if (Object.keys(account).length === 0) delete env[accountId];
205
+ else env[accountId] = account;
206
+ this.writeAll(env);
207
+ return true;
208
+ }
209
+ }
210
+
211
+ let cached: LoopKeyStore | null = null;
212
+
213
+ export function selectLoopKeyStore(): LoopKeyStore {
214
+ if (cached) return cached;
215
+ cached = process.env.LOOP_STANDALONE === "1" ? new FileLoopKeyStore() : new Neo4jLoopKeyStore();
216
+ return cached;
217
+ }
218
+
219
+ export function _resetKeyStoreForTests(): void {
220
+ cached = null;
221
+ }
@@ -1,5 +1,4 @@
1
- import { getSession } from "./neo4j.js";
2
- import { decrypt } from "./crypto.js";
1
+ import { selectLoopKeyStore } from "./key-store.js";
3
2
 
4
3
  // ─────────────────────────────────────────────────────────────────
5
4
  // Loop API V2 HTTP Client + Cross-Team Aggregation Engine
@@ -90,7 +89,7 @@ function unwrapEnvelope<T>(parsed: unknown, toolName: string, teamName: string):
90
89
 
91
90
  export interface LoopTeamKey {
92
91
  teamName: string;
93
- encryptedKey: string;
92
+ apiKey: string;
94
93
  teamAddress: string;
95
94
  agentId: string;
96
95
  permissions: string[];
@@ -120,29 +119,7 @@ export interface AggregationResult<T> {
120
119
  * Load all LoopTeamKey nodes for an account from Neo4j.
121
120
  */
122
121
  export async function loadTeamKeys(accountId: string): Promise<LoopTeamKey[]> {
123
- const session = getSession();
124
- try {
125
- const result = await session.run(
126
- `MATCH (k:LoopTeamKey {accountId: $accountId})
127
- RETURN k.teamName AS teamName, k.encryptedKey AS encryptedKey,
128
- k.teamAddress AS teamAddress, k.agentId AS agentId,
129
- k.permissions AS permissions, k.createdAt AS createdAt,
130
- k.updatedAt AS updatedAt
131
- ORDER BY k.teamName`,
132
- { accountId }
133
- );
134
- return result.records.map((r) => ({
135
- teamName: r.get("teamName"),
136
- encryptedKey: r.get("encryptedKey"),
137
- teamAddress: r.get("teamAddress") ?? "",
138
- agentId: r.get("agentId") ?? "",
139
- permissions: r.get("permissions") ?? [],
140
- createdAt: r.get("createdAt") ?? "",
141
- updatedAt: r.get("updatedAt") ?? "",
142
- }));
143
- } finally {
144
- await session.close();
145
- }
122
+ return selectLoopKeyStore().loadAll(accountId);
146
123
  }
147
124
 
148
125
  // Truncate to 200 chars, escape newlines, single line — `[loop] <tool> team=<t>
@@ -396,20 +373,7 @@ export async function aggregateAcrossTeams<T>(
396
373
  continue;
397
374
  }
398
375
 
399
- let apiKey: string;
400
- try {
401
- apiKey = decrypt(key.encryptedKey);
402
- } catch (err) {
403
- const msg = err instanceof Error ? err.message : String(err);
404
- console.error(`[loop] decrypt failed for team=${key.teamName}: ${msg}`);
405
- failures.push({
406
- teamName: key.teamName,
407
- reason: "Decryption failed",
408
- kind: "other",
409
- });
410
- continue;
411
- }
412
-
376
+ const apiKey = key.apiKey;
413
377
  const teamName = key.teamName;
414
378
  tasks.push({
415
379
  key,
@@ -512,8 +476,7 @@ export async function withTeamKey<T>(
512
476
  );
513
477
  }
514
478
 
515
- const apiKey = decrypt(key.encryptedKey);
516
- return execute(apiKey);
479
+ return execute(key.apiKey);
517
480
  }
518
481
 
519
482
  /**
@@ -1,27 +1,12 @@
1
- import { getSession } from "../lib/neo4j.js";
1
+ import { selectLoopKeyStore } from "../lib/key-store.js";
2
2
 
3
3
  export async function keyDeregister(params: {
4
4
  teamName: string;
5
5
  accountId: string;
6
6
  }): Promise<void> {
7
- const { teamName, accountId } = params;
8
-
9
- const session = getSession();
10
- try {
11
- const result = await session.run(
12
- `MATCH (k:LoopTeamKey {teamName: $teamName, accountId: $accountId})
13
- DELETE k
14
- RETURN count(k) AS deleted`,
15
- { teamName, accountId }
16
- );
17
-
18
- const deleted = result.records[0]?.get("deleted")?.toNumber?.() ?? 0;
19
- if (deleted === 0) {
20
- throw new Error(`Team "${teamName}" not found.`);
21
- }
22
- } finally {
23
- await session.close();
7
+ const deleted = await selectLoopKeyStore().delete(params.accountId, params.teamName);
8
+ if (!deleted) {
9
+ throw new Error(`Team "${params.teamName}" not found.`);
24
10
  }
25
-
26
- console.error(`[loop] key-deregister team=${teamName} account=${accountId}`);
11
+ console.error(`[loop] key-deregister team=${params.teamName} account=${params.accountId}`);
27
12
  }
@@ -1,5 +1,4 @@
1
- import { getSession } from "../lib/neo4j.js";
2
- import { encrypt } from "../lib/crypto.js";
1
+ import { selectLoopKeyStore } from "../lib/key-store.js";
3
2
  import { loopGet, LoopApiError } from "../lib/loop-api.js";
4
3
 
5
4
  interface LoopTeamResponse {
@@ -21,7 +20,7 @@ export async function keyRegister(params: {
21
20
  const { teamName, apiKey, accountId } = params;
22
21
  const permissions = params.permissions ?? ALL_PERMISSIONS;
23
22
 
24
- // Validate key against Loop API — try /team endpoint
23
+ // Validate key against Loop API — try /team endpoint.
25
24
  let teamAddress = "";
26
25
  let agentId = "";
27
26
  let warning: string | undefined;
@@ -31,11 +30,10 @@ export async function keyRegister(params: {
31
30
  apiKey,
32
31
  "/team",
33
32
  "loop-key-register",
34
- teamName
33
+ teamName,
35
34
  );
36
35
 
37
36
  if (Array.isArray(teamData) && teamData.length === 0) {
38
- // 204 was converted to empty array — ambiguous scope
39
37
  warning =
40
38
  "Key accepted but Loop returned no team data (204). " +
41
39
  "Verify the key's scope in Loop's dashboard.";
@@ -47,49 +45,26 @@ export async function keyRegister(params: {
47
45
  if (err instanceof LoopApiError && err.status === 401) {
48
46
  throw new Error(
49
47
  `Loop API rejected the key for team "${teamName}" (HTTP 401). ` +
50
- `Check that the key is valid and not expired.`
48
+ `Check that the key is valid and not expired.`,
51
49
  );
52
50
  }
53
- // Network/timeout errors — still reject, key is unverified
54
51
  throw new Error(
55
- `Could not validate key against Loop API: ${err instanceof Error ? err.message : String(err)}`
52
+ `Could not validate key against Loop API: ${err instanceof Error ? err.message : String(err)}`,
56
53
  );
57
54
  }
58
55
 
59
- // Encrypt and store
60
- const encryptedKey = encrypt(apiKey);
61
- const now = new Date().toISOString();
56
+ const { created } = await selectLoopKeyStore().save(accountId, {
57
+ teamName,
58
+ apiKey,
59
+ teamAddress,
60
+ agentId,
61
+ permissions,
62
+ });
62
63
 
63
- const writeSession = getSession();
64
- try {
65
- await writeSession.run(
66
- `CREATE INDEX loop_team_key_account_team IF NOT EXISTS
67
- FOR (k:LoopTeamKey) ON (k.accountId, k.teamName)`,
68
- {}
69
- );
70
-
71
- const mergeResult = await writeSession.run(
72
- `MERGE (k:LoopTeamKey {teamName: $teamName, accountId: $accountId})
73
- ON CREATE SET
74
- k.encryptedKey = $encryptedKey,
75
- k.teamAddress = $teamAddress,
76
- k.agentId = $agentId,
77
- k.permissions = $permissions,
78
- k.createdAt = $now,
79
- k.updatedAt = $now
80
- RETURN k.createdAt = $now AS created`,
81
- { teamName, encryptedKey, teamAddress, agentId, accountId, permissions, now }
82
- );
83
-
84
- const created = mergeResult.records[0]?.get("created");
85
- if (!created) {
86
- throw new Error(`Team "${teamName}" is already registered for this account.`);
87
- }
88
- } finally {
89
- await writeSession.close();
64
+ if (!created) {
65
+ throw new Error(`Team "${teamName}" is already registered for this account.`);
90
66
  }
91
67
 
92
68
  console.error(`[loop] key-register team=${teamName} account=${accountId}`);
93
-
94
69
  return { warning };
95
70
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "real-agent-preval",
3
- "description": "Single-address pre-valuation pack. One full UK address in, 4-page A4 PDF out (cover · valuation · area · demand+trend+sources). Consumes the real-agent-property-data MCP — no MCP tools of its own.",
3
+ "description": "Single-address pre-valuation pitch pack. One full UK address in, 5-page A4 PDF out (cover · subject valuation · market context · comparables · recently with <brand>). Consumes the real-agent-property-data and real-agent-epc MCPs — no MCP tools of its own.",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: real-agent-preval
3
- description: "Single-address pre-valuation pack. One full UK address in, 4-page A4 PDF out (cover · valuation · area · demand+trend+sources). Consumes the real-agent-property-data MCP — no MCP tools of its own."
3
+ description: "Single-address pre-valuation pitch pack. One full UK address in, 5-page A4 PDF out (cover · subject valuation · market context · comparables · recently with <brand>). Consumes the real-agent-property-data and real-agent-epc MCPs — no MCP tools of its own."
4
4
  skills:
5
5
  - skills/property-preval/SKILL.md
6
6
  always: false
@@ -9,31 +9,37 @@ metadata: {"platform":{"optional":true,"embed":["admin"]}}
9
9
 
10
10
  # Real Agent — Preval
11
11
 
12
- Consumer sub-plugin. No MCP server, no new tools — only a skill (`property-preval`) that takes one full UK address and renders a 4-page A4 pre-valuation PDF by fanning out to 12 of the 22 `real-agent-property-data` MCP tools.
12
+ Consumer sub-plugin. No MCP server, no new tools — only a skill (`property-preval`) that takes one full UK address plus operator-supplied brand tokens, subject details, agent listings, and a market summary, and renders a **5-page A4 pre-valuation PDF** designed as a sales pitch for the listing.
13
13
 
14
14
  ## What it produces
15
15
 
16
- A 4-page A4 PDF and the source HTML, written to `~/Documents/Maxy/preval/<postcode>-<YYYY-MM-DD>/`:
16
+ A 5-page A4 PDF and the source HTML, written to `~/Documents/Maxy/preval/<postcode-compact>-<YYYY-MM-DD>/`:
17
17
 
18
- - **Page 1 — Cover.** Full address, postcode chip, generated-date meta strip.
19
- - **Page 2 — Valuation.** Average asking £, asking £/sqft, sold avg £, sold £/sqft. Nearest-sold comps table. Live asking comps grid.
20
- - **Page 3 — Area.** Crime rating, flood risk, council-tax band D cost, local area population. Crime breakdown panel + council-tax band table. Headline read.
21
- - **Page 4 — Demand, trend, sources.** Demand rating, days-on-market, turnover, for-sale count. 7y growth sparkline (£ and £/sqft). Rental-side mini-panel. Property-types doughnut. Sources/methodology block.
18
+ - **Page 1 — Cover.** Full-bleed subject hero photo with gradient scrim. Light brand wordmark top-left. Address in display-italic. Postcode chip. Four-up meta strip (Report · Address · Postcode · Generated). Lifted from the `property-market-report` cover contract.
19
+ - **Page 2 — Subject valuation.** KPI strip (floor area, bedrooms, EPC rating, type). Valuation panel: headline range = `subject.sqft × sold £/sqft 70pc band` (Land Registry), asking £/sqft cross-check, operator adjustments (`subject.adjustments[]`) shown as a stacked list with colour-coded percentages, final indicative range pill. Falls back to "Estimate requires floor area" benchmark-only mode when sqft is missing.
20
+ - **Page 3 — Market context.** Market summary headline + paragraphs with drop cap. KPI strip (avg asking £, avg sold £, £/sqft). Price-growth panel (YoY) + demand pill with gauge. 7-year sparkline of £ and £/sqft. Same patterns as `property-market-report`.
21
+ - **Page 4 — Comparables.** Full-width Land Registry sold-transactions table (≤ 6 rows), stacked above a full-width live-asking-comparables tile grid ( 4 tiles). Never side-by-side corrects the cramped two-column layout in v1 preval.
22
+ - **Page 5 — Recently with `<brand>` + sources.** Operator-curated `agent_listings[]` rendered as a clickable tile grid with status badges (Sold / Under offer / For sale), then the sources & methodology block. The vendor reads this page and knows who to call.
22
23
 
23
- ## How the address routes
24
+ ## Tool dependencies
24
25
 
25
- The skill derives the postcode from the address (UK-postcode regex on the final token group), then fans out to **12** `real-agent-property-data` MCP tools in fixed call order:
26
+ The skill fans out to **13** MCP calls in parallel:
26
27
 
27
- `prices`, `prices-per-sqf`, `sold-prices`, `sold-prices-per-sqf`, `growth`, `growth-psf`, `demand`, `demand-rent`, `property-types`, `crime`, `flood-risk`, `council-tax`.
28
+ - **12 PropertyData** tools (postcode-keyed): `prices`, `prices-per-sqf`, `sold-prices`, `sold-prices-per-sqf`, `growth`, `growth-psf`, `demand`, `demand-rent`, `property-types`, `crime`, `flood-risk`, `council-tax`.
29
+ - **1 EPC** tool (address-keyed): `epc-lookup-by-address` for subject floor area + energy rating + lodgement date.
28
30
 
29
- Two tools from the same MCP are **deliberately not called** by this skill:
31
+ `property-data-address-match-uprn` and `property-data-planning-applications` are deliberately not called (CRM hygiene and dev-precedent reports, respectively).
30
32
 
31
- - `property-data-address-match-uprn` — UPRN is CRM hygiene, not preval data.
32
- - `property-data-planning-applications` — planning is a separate dev-precedent report.
33
+ ## Failure handling
33
34
 
34
- Operators can still reach those tools via the underlying `property-data` skill or directly through the MCP.
35
+ - **PropertyData key-not-registered** abort with the `property-data-key-register` prompt. Market data is essential; the pack cannot render without it.
36
+ - **EPC unavailable** — any of `key-not-registered`, `http-404 (no record)`, `http-429`, `http-500`, network failure, or EPC MCP not configured — is treated as a single class. The skill prompts the operator with the specific cause and offers two paths:
37
+ 1. Paste `subject.sqft` directly → pack renders with EPC KPI cells showing "—" + cause sub-label, but the valuation maths still runs on the pasted sqft.
38
+ 2. Decline → valuation panel switches to "Estimate requires floor area" benchmark-only mode; the pack still renders all five pages.
39
+ - **PropertyData http-429** → back off and retry only the throttled tool (4 calls per 10 seconds upstream throttle).
35
40
 
36
- ## Dependencies
41
+ ## External dependencies
37
42
 
38
- - Requires `real-agent-property-data` enabled on the account with a registered PropertyData API key (`property-data-key-register`). Without a key every fan-out call returns `Error: key-not-registered` and the renderer raises before producing a PDF.
39
- - Requires Google Chrome at `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` (mac, headless `--print-to-pdf`). Same Chrome dependency as `property-market-report`.
43
+ - Requires `real-agent-property-data` enabled on the account with a registered PropertyData API key (`property-data-key-register`).
44
+ - Requires `real-agent-epc` enabled with a registered EPC API key (`epc-key-register`) failure modes above; not strictly blocking thanks to the paste-sqft fallback.
45
+ - Requires Google Chrome at `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` for headless `--print-to-pdf`. Same Chrome dependency as `property-market-report`.