@lerianstudio/matcher-mcp 1.0.0-beta.5

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 (220) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +281 -0
  3. package/dist/auth/request-token.js +117 -0
  4. package/dist/auth/request-token.js.map +1 -0
  5. package/dist/config.js +95 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/matcher/client.js +168 -0
  8. package/dist/matcher/client.js.map +1 -0
  9. package/dist/observability/otel.js +223 -0
  10. package/dist/observability/otel.js.map +1 -0
  11. package/dist/server.js +245 -0
  12. package/dist/server.js.map +1 -0
  13. package/dist/spec/curated-operations.js +145 -0
  14. package/dist/spec/curated-operations.js.map +1 -0
  15. package/dist/spec/index.js +155 -0
  16. package/dist/spec/index.js.map +1 -0
  17. package/dist/spec/load.js +76 -0
  18. package/dist/spec/load.js.map +1 -0
  19. package/dist/spec/openapi.yaml +15217 -0
  20. package/dist/tools/context/create.js +100 -0
  21. package/dist/tools/context/create.js.map +1 -0
  22. package/dist/tools/context/get.js +31 -0
  23. package/dist/tools/context/get.js.map +1 -0
  24. package/dist/tools/context/index.js +21 -0
  25. package/dist/tools/context/index.js.map +1 -0
  26. package/dist/tools/context/list.js +78 -0
  27. package/dist/tools/context/list.js.map +1 -0
  28. package/dist/tools/context/shared.js +61 -0
  29. package/dist/tools/context/shared.js.map +1 -0
  30. package/dist/tools/context/update.js +105 -0
  31. package/dist/tools/context/update.js.map +1 -0
  32. package/dist/tools/dashboard/aggregates.js +34 -0
  33. package/dist/tools/dashboard/aggregates.js.map +1 -0
  34. package/dist/tools/dashboard/cash-impact.js +36 -0
  35. package/dist/tools/dashboard/cash-impact.js.map +1 -0
  36. package/dist/tools/dashboard/index.js +27 -0
  37. package/dist/tools/dashboard/index.js.map +1 -0
  38. package/dist/tools/dashboard/match-rate.js +35 -0
  39. package/dist/tools/dashboard/match-rate.js.map +1 -0
  40. package/dist/tools/dashboard/metrics.js +36 -0
  41. package/dist/tools/dashboard/metrics.js.map +1 -0
  42. package/dist/tools/dashboard/shared.js +122 -0
  43. package/dist/tools/dashboard/shared.js.map +1 -0
  44. package/dist/tools/dashboard/sla.js +33 -0
  45. package/dist/tools/dashboard/sla.js.map +1 -0
  46. package/dist/tools/dashboard/source-breakdown.js +36 -0
  47. package/dist/tools/dashboard/source-breakdown.js.map +1 -0
  48. package/dist/tools/dashboard/volume.js +35 -0
  49. package/dist/tools/dashboard/volume.js.map +1 -0
  50. package/dist/tools/dispute/close.js +49 -0
  51. package/dist/tools/dispute/close.js.map +1 -0
  52. package/dist/tools/dispute/get.js +27 -0
  53. package/dist/tools/dispute/get.js.map +1 -0
  54. package/dist/tools/dispute/index.js +17 -0
  55. package/dist/tools/dispute/index.js.map +1 -0
  56. package/dist/tools/dispute/list.js +137 -0
  57. package/dist/tools/dispute/list.js.map +1 -0
  58. package/dist/tools/dispute/shared.js +66 -0
  59. package/dist/tools/dispute/shared.js.map +1 -0
  60. package/dist/tools/dispute/submit-evidence.js +58 -0
  61. package/dist/tools/dispute/submit-evidence.js.map +1 -0
  62. package/dist/tools/exception/add-comment.js +50 -0
  63. package/dist/tools/exception/add-comment.js.map +1 -0
  64. package/dist/tools/exception/adjust-entry.js +71 -0
  65. package/dist/tools/exception/adjust-entry.js.map +1 -0
  66. package/dist/tools/exception/bulk-assign.js +47 -0
  67. package/dist/tools/exception/bulk-assign.js.map +1 -0
  68. package/dist/tools/exception/bulk-dispatch.js +69 -0
  69. package/dist/tools/exception/bulk-dispatch.js.map +1 -0
  70. package/dist/tools/exception/bulk-resolve.js +64 -0
  71. package/dist/tools/exception/bulk-resolve.js.map +1 -0
  72. package/dist/tools/exception/delete-comment.js +40 -0
  73. package/dist/tools/exception/delete-comment.js.map +1 -0
  74. package/dist/tools/exception/dispatch.js +64 -0
  75. package/dist/tools/exception/dispatch.js.map +1 -0
  76. package/dist/tools/exception/force-match.js +55 -0
  77. package/dist/tools/exception/force-match.js.map +1 -0
  78. package/dist/tools/exception/get.js +34 -0
  79. package/dist/tools/exception/get.js.map +1 -0
  80. package/dist/tools/exception/history.js +61 -0
  81. package/dist/tools/exception/history.js.map +1 -0
  82. package/dist/tools/exception/index.js +39 -0
  83. package/dist/tools/exception/index.js.map +1 -0
  84. package/dist/tools/exception/list-comments.js +35 -0
  85. package/dist/tools/exception/list-comments.js.map +1 -0
  86. package/dist/tools/exception/list.js +169 -0
  87. package/dist/tools/exception/list.js.map +1 -0
  88. package/dist/tools/exception/open-dispute.js +53 -0
  89. package/dist/tools/exception/open-dispute.js.map +1 -0
  90. package/dist/tools/exception/shared.js +98 -0
  91. package/dist/tools/exception/shared.js.map +1 -0
  92. package/dist/tools/fee-rule/create.js +106 -0
  93. package/dist/tools/fee-rule/create.js.map +1 -0
  94. package/dist/tools/fee-rule/delete.js +27 -0
  95. package/dist/tools/fee-rule/delete.js.map +1 -0
  96. package/dist/tools/fee-rule/get.js +27 -0
  97. package/dist/tools/fee-rule/get.js.map +1 -0
  98. package/dist/tools/fee-rule/index.js +21 -0
  99. package/dist/tools/fee-rule/index.js.map +1 -0
  100. package/dist/tools/fee-rule/list.js +64 -0
  101. package/dist/tools/fee-rule/list.js.map +1 -0
  102. package/dist/tools/fee-rule/shared.js +67 -0
  103. package/dist/tools/fee-rule/shared.js.map +1 -0
  104. package/dist/tools/fee-rule/update.js +70 -0
  105. package/dist/tools/fee-rule/update.js.map +1 -0
  106. package/dist/tools/fee-schedule/create.js +105 -0
  107. package/dist/tools/fee-schedule/create.js.map +1 -0
  108. package/dist/tools/fee-schedule/delete.js +32 -0
  109. package/dist/tools/fee-schedule/delete.js.map +1 -0
  110. package/dist/tools/fee-schedule/get.js +30 -0
  111. package/dist/tools/fee-schedule/get.js.map +1 -0
  112. package/dist/tools/fee-schedule/index.js +22 -0
  113. package/dist/tools/fee-schedule/index.js.map +1 -0
  114. package/dist/tools/fee-schedule/list.js +56 -0
  115. package/dist/tools/fee-schedule/list.js.map +1 -0
  116. package/dist/tools/fee-schedule/shared.js +58 -0
  117. package/dist/tools/fee-schedule/shared.js.map +1 -0
  118. package/dist/tools/fee-schedule/simulate.js +54 -0
  119. package/dist/tools/fee-schedule/simulate.js.map +1 -0
  120. package/dist/tools/fee-schedule/update.js +69 -0
  121. package/dist/tools/fee-schedule/update.js.map +1 -0
  122. package/dist/tools/generic/describe-operation.js +157 -0
  123. package/dist/tools/generic/describe-operation.js.map +1 -0
  124. package/dist/tools/generic/invoke.js +343 -0
  125. package/dist/tools/generic/invoke.js.map +1 -0
  126. package/dist/tools/generic/list-operations.js +73 -0
  127. package/dist/tools/generic/list-operations.js.map +1 -0
  128. package/dist/tools/ingestion/index.js +29 -0
  129. package/dist/tools/ingestion/index.js.map +1 -0
  130. package/dist/tools/ingestion/job-errors-list.js +45 -0
  131. package/dist/tools/ingestion/job-errors-list.js.map +1 -0
  132. package/dist/tools/ingestion/job-get.js +42 -0
  133. package/dist/tools/ingestion/job-get.js.map +1 -0
  134. package/dist/tools/ingestion/job-transactions-list.js +93 -0
  135. package/dist/tools/ingestion/job-transactions-list.js.map +1 -0
  136. package/dist/tools/ingestion/jobs-list.js +89 -0
  137. package/dist/tools/ingestion/jobs-list.js.map +1 -0
  138. package/dist/tools/ingestion/shared.js +84 -0
  139. package/dist/tools/ingestion/shared.js.map +1 -0
  140. package/dist/tools/ingestion/transaction-ignore.js +51 -0
  141. package/dist/tools/ingestion/transaction-ignore.js.map +1 -0
  142. package/dist/tools/ingestion/transactions-search.js +143 -0
  143. package/dist/tools/ingestion/transactions-search.js.map +1 -0
  144. package/dist/tools/match-rule/create.js +64 -0
  145. package/dist/tools/match-rule/create.js.map +1 -0
  146. package/dist/tools/match-rule/delete.js +35 -0
  147. package/dist/tools/match-rule/delete.js.map +1 -0
  148. package/dist/tools/match-rule/get.js +32 -0
  149. package/dist/tools/match-rule/get.js.map +1 -0
  150. package/dist/tools/match-rule/index.js +22 -0
  151. package/dist/tools/match-rule/index.js.map +1 -0
  152. package/dist/tools/match-rule/list.js +72 -0
  153. package/dist/tools/match-rule/list.js.map +1 -0
  154. package/dist/tools/match-rule/reorder.js +45 -0
  155. package/dist/tools/match-rule/reorder.js.map +1 -0
  156. package/dist/tools/match-rule/shared.js +71 -0
  157. package/dist/tools/match-rule/shared.js.map +1 -0
  158. package/dist/tools/match-rule/update.js +68 -0
  159. package/dist/tools/match-rule/update.js.map +1 -0
  160. package/dist/tools/matching/get.js +48 -0
  161. package/dist/tools/matching/get.js.map +1 -0
  162. package/dist/tools/matching/groups.js +98 -0
  163. package/dist/tools/matching/groups.js.map +1 -0
  164. package/dist/tools/matching/index.js +19 -0
  165. package/dist/tools/matching/index.js.map +1 -0
  166. package/dist/tools/matching/list.js +76 -0
  167. package/dist/tools/matching/list.js.map +1 -0
  168. package/dist/tools/matching/shared.js +77 -0
  169. package/dist/tools/matching/shared.js.map +1 -0
  170. package/dist/tools/matching/start.js +55 -0
  171. package/dist/tools/matching/start.js.map +1 -0
  172. package/dist/tools/report/count-exceptions.js +45 -0
  173. package/dist/tools/report/count-exceptions.js.map +1 -0
  174. package/dist/tools/report/count-matched.js +46 -0
  175. package/dist/tools/report/count-matched.js.map +1 -0
  176. package/dist/tools/report/count-transactions.js +44 -0
  177. package/dist/tools/report/count-transactions.js.map +1 -0
  178. package/dist/tools/report/count-unmatched.js +44 -0
  179. package/dist/tools/report/count-unmatched.js.map +1 -0
  180. package/dist/tools/report/export-exceptions.js +46 -0
  181. package/dist/tools/report/export-exceptions.js.map +1 -0
  182. package/dist/tools/report/export-matched.js +47 -0
  183. package/dist/tools/report/export-matched.js.map +1 -0
  184. package/dist/tools/report/export-summary.js +47 -0
  185. package/dist/tools/report/export-summary.js.map +1 -0
  186. package/dist/tools/report/export-unmatched.js +47 -0
  187. package/dist/tools/report/export-unmatched.js.map +1 -0
  188. package/dist/tools/report/export-variance.js +46 -0
  189. package/dist/tools/report/export-variance.js.map +1 -0
  190. package/dist/tools/report/index.js +40 -0
  191. package/dist/tools/report/index.js.map +1 -0
  192. package/dist/tools/report/matched.js +44 -0
  193. package/dist/tools/report/matched.js.map +1 -0
  194. package/dist/tools/report/shared.js +176 -0
  195. package/dist/tools/report/shared.js.map +1 -0
  196. package/dist/tools/report/summary.js +41 -0
  197. package/dist/tools/report/summary.js.map +1 -0
  198. package/dist/tools/report/unmatched.js +44 -0
  199. package/dist/tools/report/unmatched.js.map +1 -0
  200. package/dist/tools/report/variance.js +44 -0
  201. package/dist/tools/report/variance.js.map +1 -0
  202. package/dist/tools/source/create.js +84 -0
  203. package/dist/tools/source/create.js.map +1 -0
  204. package/dist/tools/source/get.js +41 -0
  205. package/dist/tools/source/get.js.map +1 -0
  206. package/dist/tools/source/index.js +22 -0
  207. package/dist/tools/source/index.js.map +1 -0
  208. package/dist/tools/source/list.js +72 -0
  209. package/dist/tools/source/list.js.map +1 -0
  210. package/dist/tools/source/shared.js +63 -0
  211. package/dist/tools/source/shared.js.map +1 -0
  212. package/dist/tools/source/update.js +73 -0
  213. package/dist/tools/source/update.js.map +1 -0
  214. package/dist/tools/tool-error.js +74 -0
  215. package/dist/tools/tool-error.js.map +1 -0
  216. package/dist/transport/body-limit.js +82 -0
  217. package/dist/transport/body-limit.js.map +1 -0
  218. package/dist/transport/http.js +154 -0
  219. package/dist/transport/http.js.map +1 -0
  220. package/package.json +68 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmatched.js","sourceRoot":"","sources":["../../../src/tools/report/unmatched.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,8EAA8E;AAC9E,iFAAiF;AACjF,6EAA6E;AAC7E,qEAAqE;AACrE,+EAA+E;AAC/E,iDAAiD;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AAEpB,yFAAyF;AACzF,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,uEAAuE;QACrE,iDAAiD,CACpD;IACH,GAAG,sBAAsB;IACzB,GAAG,0BAA0B;CAC9B,CAAA;AAOD;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,iEAAiE;YACjE,qEAAqE;YACrE,oEAAoE;YACpE,yEAAyE;YACzE,sEAAsE;YACtE,4DAA4D;QAC9D,WAAW,EAAE,yBAAyB;KACvC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAA;QAC7C,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC;YACpD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,44 @@
1
+ // `report_variance` — curated GET /v1/reports/contexts/{contextId}/variance.
2
+ //
3
+ // Cursor-paginated variance report rows for a context within a date window.
4
+ // date_from/date_to/source_id filter and cursor/limit/sort_order page; matcher's
5
+ // { items, nextCursor } envelope is returned as-is — no auto-pagination (the
6
+ // model re-calls with the returned cursor). The report is implicitly
7
+ // tenant-scoped by the relayed token; contextId is a routing path param, not a
8
+ // tenant field, and no tenant field is accepted.
9
+ import { z } from 'zod';
10
+ import { buildReportPaginatedQuery, contextReportPath, dispatchReport, reportFilterInputShape, reportPaginationInputShape, } from './shared.js';
11
+ /** Zod raw shape for `report_variance` input — contextId path + filter + pagination. */
12
+ export const varianceReportInputShape = {
13
+ contextId: z
14
+ .string()
15
+ .min(1)
16
+ .describe('The reconciliation context id (UUID) whose variance rows to report. ' +
17
+ 'Routing path param, not a tenant field.'),
18
+ ...reportFilterInputShape,
19
+ ...reportPaginationInputShape,
20
+ };
21
+ /**
22
+ * Register `report_variance`. Passes the date filter + pagination through and
23
+ * returns matcher's pagination envelope verbatim; a matcher error surfaces via
24
+ * the shared toToolError.
25
+ */
26
+ export function registerReportVarianceTool(server) {
27
+ server.registerTool('report_variance', {
28
+ description: 'List variance report rows for a context within a date window ' +
29
+ '(cursor-paginated). Pass contextId (UUID path param — not a tenant ' +
30
+ 'field), and optionally date_from/date_to (YYYY-MM-DD, window ≤ 90 ' +
31
+ 'days), source_id, limit (1–200), sort_order (asc|desc), and/or cursor. ' +
32
+ 'The response carries { items, nextCursor } — paginate by re-calling ' +
33
+ 'with the returned nextCursor. No tenant field is accepted.',
34
+ inputSchema: varianceReportInputShape,
35
+ }, async (args, extra) => {
36
+ const query = buildReportPaginatedQuery(args);
37
+ return dispatchReport({
38
+ method: 'GET',
39
+ path: contextReportPath(args.contextId, 'variance'),
40
+ ...(query !== undefined ? { query } : {}),
41
+ }, extra);
42
+ });
43
+ }
44
+ //# sourceMappingURL=variance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variance.js","sourceRoot":"","sources":["../../../src/tools/report/variance.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,4EAA4E;AAC5E,iFAAiF;AACjF,6EAA6E;AAC7E,qEAAqE;AACrE,+EAA+E;AAC/E,iDAAiD;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AAEpB,wFAAwF;AACxF,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,sEAAsE;QACpE,yCAAyC,CAC5C;IACH,GAAG,sBAAsB;IACzB,GAAG,0BAA0B;CAC9B,CAAA;AAOD;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,+DAA+D;YAC/D,qEAAqE;YACrE,oEAAoE;YACpE,yEAAyE;YACzE,sEAAsE;YACtE,4DAA4D;QAC9D,WAAW,EAAE,wBAAwB;KACtC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAA;QAC7C,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;YACnD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,84 @@
1
+ // `source_create` — curated POST /v1/contexts/{contextId}/sources.
2
+ //
3
+ // A typed, richly-described front door to creating a reconciliation source under
4
+ // a context, mirroring the spec's CreateSourceRequest. The Zod schema models the
5
+ // required scalars (name, type, side) and the documented enums; `config` is left
6
+ // as an open record (per the established pattern) — its shape depends on the
7
+ // source type and matcher validates it server-side, so we do not duplicate that
8
+ // validation here.
9
+ //
10
+ // `contextId` is a routing path param (matcher validates it belongs to the
11
+ // relayed token's tenant), NOT a tenant field. No tenant field exists in the
12
+ // schema: the source is created for the tenant of the relayed bearer token.
13
+ import { z } from 'zod';
14
+ import { contextSourcesPath, dispatchSource } from './shared.js';
15
+ /** Origin of the source data (LEDGER ledger / BANK feed / GATEWAY / CUSTOM / FETCHER pull). */
16
+ export const SOURCE_TYPES = [
17
+ 'LEDGER',
18
+ 'BANK',
19
+ 'GATEWAY',
20
+ 'CUSTOM',
21
+ 'FETCHER',
22
+ ];
23
+ /** Matching side this source feeds — the two sides reconciled against each other. */
24
+ export const SOURCE_SIDES = ['LEFT', 'RIGHT'];
25
+ /** Zod raw shape for `source_create` input — mirrors CreateSourceRequest. */
26
+ export const createSourceInputShape = {
27
+ contextId: z
28
+ .string()
29
+ .min(1)
30
+ .describe('The reconciliation context id (UUID) the source belongs to. This is a ' +
31
+ 'routing path param, not a tenant field — matcher validates the context ' +
32
+ "belongs to your token's tenant."),
33
+ name: z
34
+ .string()
35
+ .min(1)
36
+ .max(50)
37
+ .describe('Human-readable name of the source (1–50 chars).'),
38
+ type: z
39
+ .enum(SOURCE_TYPES)
40
+ .describe('Origin of the source data. LEDGER is the internal ledger; BANK is a bank ' +
41
+ 'statement feed; GATEWAY is a payment gateway; CUSTOM is a bespoke feed; ' +
42
+ 'FETCHER is a discovery-engine pull.'),
43
+ side: z
44
+ .enum(SOURCE_SIDES)
45
+ .describe('Matching side this source feeds. LEFT and RIGHT are the two sides ' +
46
+ 'reconciled against each other.'),
47
+ config: z
48
+ .record(z.string(), z.unknown())
49
+ .describe('Source-specific configuration object (connection and parsing settings). ' +
50
+ 'Its shape depends on the source type and is validated server-side.'),
51
+ };
52
+ /**
53
+ * Build the POST body from the input minus the path-only `contextId`. Only the
54
+ * CreateSourceRequest fields are sent; contextId travels in the path.
55
+ */
56
+ export function buildCreateBody(args) {
57
+ const { contextId: _contextId, ...body } = args;
58
+ void _contextId;
59
+ return body;
60
+ }
61
+ /**
62
+ * Register `source_create`. Substitutes contextId into the path, builds the body
63
+ * from the remaining typed input, and dispatches a POST through the shared
64
+ * fail-closed relay; a matcher error surfaces via the shared toToolError (same
65
+ * shape as matcher_invoke).
66
+ */
67
+ export function registerSourceCreateTool(server) {
68
+ server.registerTool('source_create', {
69
+ description: 'Create a reconciliation source under a context. Provide contextId ' +
70
+ '(UUID path param — not a tenant field), name (1–50), type ' +
71
+ '(LEDGER|BANK|GATEWAY|CUSTOM|FETCHER), side (LEFT|RIGHT), and a ' +
72
+ 'type-specific config object (validated server-side). The source is ' +
73
+ "created for your token's tenant (no tenant field is accepted). Returns " +
74
+ 'the created source, or a structured RFC 9457 error.',
75
+ inputSchema: createSourceInputShape,
76
+ }, async (args, extra) => {
77
+ return dispatchSource({
78
+ method: 'POST',
79
+ path: contextSourcesPath(args.contextId),
80
+ body: buildCreateBody(args),
81
+ }, extra);
82
+ });
83
+ }
84
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/tools/source/create.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,EAAE;AACF,iFAAiF;AACjF,iFAAiF;AACjF,iFAAiF;AACjF,6EAA6E;AAC7E,gFAAgF;AAChF,mBAAmB;AACnB,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAE5E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,MAAM;IACN,SAAS;IACT,QAAQ;IACR,SAAS;CACD,CAAA;AAEV,qFAAqF;AACrF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,CAAU,CAAA;AAEtD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,wEAAwE;QACtE,yEAAyE;QACzE,iCAAiC,CACpC;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,QAAQ,CAAC,iDAAiD,CAAC;IAC9D,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,CACP,2EAA2E;QACzE,0EAA0E;QAC1E,qCAAqC,CACxC;IACH,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,CACP,oEAAoE;QAClE,gCAAgC,CACnC;IACH,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/B,QAAQ,CACP,0EAA0E;QACxE,oEAAoE,CACvE;CACJ,CAAA;AAOD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAuB;IAEvB,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IAC/C,KAAK,UAAU,CAAA;IACf,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,WAAW,EACT,oEAAoE;YACpE,4DAA4D;YAC5D,iEAAiE;YACjE,qEAAqE;YACrE,yEAAyE;YACzE,qDAAqD;QACvD,WAAW,EAAE,sBAAsB;KACpC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC;YACxC,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC;SAC5B,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ // `source_get` — curated GET /v1/contexts/{contextId}/sources/{sourceId}.
2
+ //
3
+ // Reads one reconciliation source by id. Unlike fee-rules, sources keep the
4
+ // contextId on single-resource ops, so both contextId and sourceId are path
5
+ // params. The source must belong to a context owned by the relayed token's
6
+ // tenant — matcher enforces that; no tenant field is accepted.
7
+ import { z } from 'zod';
8
+ import { contextSourcePath, dispatchSource } from './shared.js';
9
+ /** Zod raw shape for `source_get` input — both path ids. */
10
+ export const getSourceInputShape = {
11
+ contextId: z
12
+ .string()
13
+ .min(1)
14
+ .describe('The reconciliation context id (UUID). Routing path param, not a tenant ' +
15
+ 'field.'),
16
+ sourceId: z
17
+ .string()
18
+ .min(1)
19
+ .describe('The reconciliation source id (UUID) to fetch.'),
20
+ };
21
+ /**
22
+ * Register `source_get`. Substitutes both ids into the path and dispatches a GET
23
+ * through the shared fail-closed relay; a matcher error (e.g. 404) surfaces via
24
+ * the shared toToolError.
25
+ */
26
+ export function registerSourceGetTool(server) {
27
+ server.registerTool('source_get', {
28
+ description: 'Fetch a single reconciliation source by id (UUID) within a context. ' +
29
+ 'Provide both contextId and sourceId (path params; contextId is not a ' +
30
+ "tenant field). The source must belong to your token's tenant. Returns " +
31
+ 'the source, or a structured RFC 9457 error (e.g. 404 if it does not ' +
32
+ 'exist or is not yours).',
33
+ inputSchema: getSourceInputShape,
34
+ }, async (args, extra) => {
35
+ return dispatchSource({
36
+ method: 'GET',
37
+ path: contextSourcePath(args.contextId, args.sourceId),
38
+ }, extra);
39
+ });
40
+ }
41
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/tools/source/get.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,+DAA+D;AAE/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE/D,4DAA4D;AAC5D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,yEAAyE;QACvE,QAAQ,CACX;IACH,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,+CAA+C,CAAC;CAC7D,CAAA;AAKD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;QACE,WAAW,EACT,sEAAsE;YACtE,uEAAuE;YACvE,wEAAwE;YACxE,sEAAsE;YACtE,yBAAyB;QAC3B,WAAW,EAAE,mBAAmB;KACjC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC;SACvD,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ // Curated source vertical (task 2.2.2): four typed tools over the matcher
2
+ // reconciliation-source resource — create, list, get, update. (Delete was
3
+ // removed when the matcher app replaced source hard-delete with the
4
+ // archive/restore lifecycle; archive/restore remain reachable via the generic
5
+ // invoke surface.) Sources are fully context-nested on every op (single-resource
6
+ // ops keep contextId, unlike fee-rules). Each tool shares the fail-closed relay
7
+ // (shared.ts) and the RFC 9457 → tool-error mapping (toToolError) with the
8
+ // generic surface, so curated and generic map matcher errors identically. No
9
+ // tool carries a tenant field; contextId is a routing path param matcher
10
+ // validates against the relayed token's tenant.
11
+ import { registerSourceCreateTool } from './create.js';
12
+ import { registerSourceGetTool } from './get.js';
13
+ import { registerSourceListTool } from './list.js';
14
+ import { registerSourceUpdateTool } from './update.js';
15
+ /** Register all four curated source tools on the given server. */
16
+ export function registerSourceTools(server) {
17
+ registerSourceCreateTool(server);
18
+ registerSourceListTool(server);
19
+ registerSourceGetTool(server);
20
+ registerSourceUpdateTool(server);
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/source/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,oEAAoE;AACpE,8EAA8E;AAC9E,iFAAiF;AACjF,gFAAgF;AAChF,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,gDAAgD;AAIhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEtD,kEAAkE;AAClE,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,wBAAwB,CAAC,MAAM,CAAC,CAAA;IAChC,sBAAsB,CAAC,MAAM,CAAC,CAAA;IAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAA;IAC7B,wBAAwB,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC"}
@@ -0,0 +1,72 @@
1
+ // `source_list` — curated GET /v1/contexts/{contextId}/sources.
2
+ //
3
+ // Cursor-paginated list of a context's reconciliation sources, optionally
4
+ // filtered by type. limit/cursor/type pass through untouched and matcher's
5
+ // { items, nextCursor, ... } envelope is returned as-is — no auto-pagination
6
+ // (the model re-calls with the returned cursor). The list is implicitly
7
+ // tenant-scoped by the relayed token; contextId is a routing path param, not a
8
+ // tenant field, and no tenant field is accepted.
9
+ import { z } from 'zod';
10
+ import { SOURCE_TYPES } from './create.js';
11
+ import { contextSourcesPath, dispatchSource } from './shared.js';
12
+ /** Zod raw shape for `source_list` input — contextId path + type filter + pagination. */
13
+ export const listSourcesInputShape = {
14
+ contextId: z
15
+ .string()
16
+ .min(1)
17
+ .describe('The reconciliation context id (UUID) whose sources to list. Routing path ' +
18
+ 'param, not a tenant field.'),
19
+ type: z
20
+ .enum(SOURCE_TYPES)
21
+ .optional()
22
+ .describe('Optional filter by source type (LEDGER|BANK|GATEWAY|CUSTOM|FETCHER).'),
23
+ limit: z
24
+ .number()
25
+ .int()
26
+ .min(1)
27
+ .max(200)
28
+ .optional()
29
+ .describe('Maximum number of records to return (1–200, default 20).'),
30
+ cursor: z
31
+ .string()
32
+ .optional()
33
+ .describe('Opaque pagination cursor from a prior response. Re-call with the ' +
34
+ 'returned nextCursor to page forward (there is no auto-pagination).'),
35
+ };
36
+ /** Build the list query, omitting unset knobs so the request stays minimal. */
37
+ function buildListQuery(args) {
38
+ const query = {};
39
+ if (args.type !== undefined) {
40
+ query.type = args.type;
41
+ }
42
+ if (args.limit !== undefined) {
43
+ query.limit = args.limit;
44
+ }
45
+ if (args.cursor !== undefined) {
46
+ query.cursor = args.cursor;
47
+ }
48
+ return Object.keys(query).length > 0 ? query : undefined;
49
+ }
50
+ /**
51
+ * Register `source_list`. Passes type/limit/cursor through and returns matcher's
52
+ * pagination envelope verbatim; a matcher error surfaces via the shared
53
+ * toToolError.
54
+ */
55
+ export function registerSourceListTool(server) {
56
+ server.registerTool('source_list', {
57
+ description: "List the reconciliation sources under a context (cursor-paginated). " +
58
+ 'Pass contextId (UUID path param — not a tenant field), and optionally ' +
59
+ 'type (LEDGER|BANK|GATEWAY|CUSTOM|FETCHER), limit (1–200), and/or cursor. ' +
60
+ 'The response carries { items, nextCursor } — paginate by re-calling ' +
61
+ 'with the returned nextCursor. No tenant field is accepted.',
62
+ inputSchema: listSourcesInputShape,
63
+ }, async (args, extra) => {
64
+ const query = buildListQuery(args);
65
+ return dispatchSource({
66
+ method: 'GET',
67
+ path: contextSourcesPath(args.contextId),
68
+ ...(query !== undefined ? { query } : {}),
69
+ }, extra);
70
+ });
71
+ }
72
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/tools/source/list.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,+EAA+E;AAC/E,iDAAiD;AAEjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,yFAAyF;AACzF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,2EAA2E;QACzE,4BAA4B,CAC/B;IACH,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,EAAE;SACV,QAAQ,CACP,sEAAsE,CACvE;IACH,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,0DAA0D,CAAC;IACvE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,mEAAmE;QACjE,oEAAoE,CACvE;CACJ,CAAA;AAKD,+EAA+E;AAC/E,SAAS,cAAc,CAAC,IAAsB;IAC5C,MAAM,KAAK,GAAoC,EAAE,CAAA;IACjD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IACxB,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC7B,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IAC1B,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,WAAW,EACT,sEAAsE;YACtE,wEAAwE;YACxE,2EAA2E;YAC3E,sEAAsE;YACtE,4DAA4D;QAC9D,WAAW,EAAE,qBAAqB;KACnC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAA;QAClC,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC;YACxC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,63 @@
1
+ // Shared plumbing for the curated source tools.
2
+ //
3
+ // Sources are the data feeds bound to a reconciliation context. Unlike
4
+ // fee-rules' hybrid path, EVERY source op is fully context-nested: create/list
5
+ // hang off /v1/contexts/{contextId}/sources and get/update/delete off
6
+ // /v1/contexts/{contextId}/sources/{sourceId} — single-resource ops KEEP the
7
+ // contextId. This module factors the two path helpers plus the relay boilerplate
8
+ // exactly once so the five tools stay thin (just "build the request") and so
9
+ // curated + generic surfaces map matcher errors through the identical path
10
+ // (toToolError).
11
+ //
12
+ // Security: the bearer token is never read here directly — requireMatcherClient
13
+ // owns the request-scoped relay and fails closed when no usable credential was
14
+ // provided. The contextId path param is a routing identifier (matcher validates
15
+ // the context belongs to the relayed token's tenant); no tenant identifier is
16
+ // ever read or injected, and no source tool input carries one.
17
+ import { requireMatcherClient, UnauthenticatedError, } from '../../auth/request-token.js';
18
+ import { isMatcherApiError, toToolError } from '../tool-error.js';
19
+ /** Build the context-scoped sources collection path (create/list). */
20
+ export function contextSourcesPath(contextId) {
21
+ return `/v1/contexts/${encodeURIComponent(contextId)}/sources`;
22
+ }
23
+ /** Build the single-source sub-path (get/update/delete) — both ids encoded. */
24
+ export function contextSourcePath(contextId, sourceId) {
25
+ return `${contextSourcesPath(contextId)}/${encodeURIComponent(sourceId)}`;
26
+ }
27
+ /**
28
+ * Run one assembled request through the relay: fail-closed auth, dispatch, and
29
+ * RFC 9457 → tool-error mapping — identical to the generic invoke's dispatch so
30
+ * both surfaces produce the same structured error shape.
31
+ *
32
+ * `clientFor` is injectable for tests; production callers pass nothing and get
33
+ * the request-scoped, fail-closed `requireMatcherClient`.
34
+ */
35
+ export async function dispatchSource(request, extra, clientFor = requireMatcherClient) {
36
+ let client;
37
+ try {
38
+ // Fail-closed on a missing / empty / non-Bearer credential.
39
+ client = clientFor(extra);
40
+ }
41
+ catch (err) {
42
+ if (err instanceof UnauthenticatedError) {
43
+ return { isError: true, content: [{ type: 'text', text: err.message }] };
44
+ }
45
+ throw err;
46
+ }
47
+ try {
48
+ const result = await client.request(request);
49
+ // Success → matcher's JSON returned as-is (incl. pagination envelopes and
50
+ // 204 → null for delete).
51
+ return { content: [{ type: 'text', text: JSON.stringify(result ?? null) }] };
52
+ }
53
+ catch (err) {
54
+ // RFC 9457 problem → the SHARED structured tool error (status/title/detail/
55
+ // code preserved). A non-matcher error is a bug, not a matcher failure, and
56
+ // is re-thrown rather than masqueraded as one.
57
+ if (isMatcherApiError(err)) {
58
+ return toToolError(err);
59
+ }
60
+ throw err;
61
+ }
62
+ }
63
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/tools/source/shared.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,uEAAuE;AACvE,+EAA+E;AAC/E,sEAAsE;AACtE,6EAA6E;AAC7E,iFAAiF;AACjF,6EAA6E;AAC7E,2EAA2E;AAC3E,iBAAiB;AACjB,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,+DAA+D;AAI/D,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAErB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEjE,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,gBAAgB,kBAAkB,CAAC,SAAS,CAAC,UAAU,CAAA;AAChE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,QAAgB;IACnE,OAAO,GAAG,kBAAkB,CAAC,SAAS,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAA;AAC3E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAuB,EACvB,KAAmC,EACnC,YAAyD,oBAAoB;IAE7E,IAAI,MAAqB,CAAA;IACzB,IAAI,CAAC;QACH,4DAA4D;QAC5D,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,oBAAoB,EAAE,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,CAAA;QAC1E,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAC5C,0EAA0E;QAC1E,0BAA0B;QAC1B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC,EAAE,CAAA;IAC9E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4EAA4E;QAC5E,4EAA4E;QAC5E,+CAA+C;QAC/C,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,WAAW,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,73 @@
1
+ // `source_update` — curated PATCH /v1/contexts/{contextId}/sources/{sourceId}.
2
+ //
3
+ // Partial update of a source's fields, mirroring the spec's UpdateSourceRequest.
4
+ // All body fields are optional (PATCH semantics): name, type, side, config.
5
+ // Both contextId and sourceId are path params (sources keep contextId on
6
+ // single-resource ops); no tenant field is accepted (matcher scopes to the
7
+ // relayed token's tenant).
8
+ import { z } from 'zod';
9
+ import { SOURCE_SIDES, SOURCE_TYPES } from './create.js';
10
+ import { contextSourcePath, dispatchSource } from './shared.js';
11
+ /** Zod raw shape for `source_update` input — path ids + optional body fields. */
12
+ export const updateSourceInputShape = {
13
+ contextId: z
14
+ .string()
15
+ .min(1)
16
+ .describe('The reconciliation context id (UUID). Routing path param, not a tenant ' +
17
+ 'field.'),
18
+ sourceId: z
19
+ .string()
20
+ .min(1)
21
+ .describe('The reconciliation source id (UUID) to update.'),
22
+ name: z
23
+ .string()
24
+ .min(1)
25
+ .max(50)
26
+ .optional()
27
+ .describe('New human-readable name of the source (1–50 chars).'),
28
+ type: z
29
+ .enum(SOURCE_TYPES)
30
+ .optional()
31
+ .describe('Origin of the source data (LEDGER|BANK|GATEWAY|CUSTOM|FETCHER).'),
32
+ side: z
33
+ .enum(SOURCE_SIDES)
34
+ .optional()
35
+ .describe('Matching side this source feeds (LEFT|RIGHT).'),
36
+ config: z
37
+ .record(z.string(), z.unknown())
38
+ .optional()
39
+ .describe('Source-specific configuration object (validated server-side).'),
40
+ };
41
+ /**
42
+ * Build the PATCH body from the input minus the path-only ids. Only the fields
43
+ * the caller actually set are sent — matcher treats absent fields as unchanged.
44
+ */
45
+ export function buildUpdateBody(args) {
46
+ const { contextId: _contextId, sourceId: _sourceId, ...body } = args;
47
+ void _contextId;
48
+ void _sourceId;
49
+ return body;
50
+ }
51
+ /**
52
+ * Register `source_update`. Splits the path ids from the body fields, dispatches
53
+ * a PATCH through the shared fail-closed relay; a matcher error surfaces via the
54
+ * shared toToolError.
55
+ */
56
+ export function registerSourceUpdateTool(server) {
57
+ server.registerTool('source_update', {
58
+ description: 'Update fields of a reconciliation source by id (UUID) within a context. ' +
59
+ 'Provide contextId and sourceId (path params; contextId is not a tenant ' +
60
+ 'field). All body fields are optional (PATCH): name (1–50), type ' +
61
+ '(LEDGER|BANK|GATEWAY|CUSTOM|FETCHER), side (LEFT|RIGHT), config. No ' +
62
+ 'tenant field is accepted. Returns the updated source, or a structured ' +
63
+ 'RFC 9457 error.',
64
+ inputSchema: updateSourceInputShape,
65
+ }, async (args, extra) => {
66
+ return dispatchSource({
67
+ method: 'PATCH',
68
+ path: contextSourcePath(args.contextId, args.sourceId),
69
+ body: buildUpdateBody(args),
70
+ }, extra);
71
+ });
72
+ }
73
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/tools/source/update.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,2BAA2B;AAE3B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE/D,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,yEAAyE;QACvE,QAAQ,CACX;IACH,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,QAAQ,EAAE;SACV,QAAQ,CAAC,qDAAqD,CAAC;IAClE,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,EAAE;SACV,QAAQ,CACP,iEAAiE,CAClE;IACH,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,YAAY,CAAC;SAClB,QAAQ,EAAE;SACV,QAAQ,CAAC,+CAA+C,CAAC;IAC5D,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/B,QAAQ,EAAE;SACV,QAAQ,CACP,+DAA+D,CAChE;CACJ,CAAA;AAOD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAuB;IAEvB,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IACpE,KAAK,UAAU,CAAA;IACf,KAAK,SAAS,CAAA;IACd,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,WAAW,EACT,0EAA0E;YAC1E,yEAAyE;YACzE,kEAAkE;YAClE,sEAAsE;YACtE,wEAAwE;YACxE,iBAAiB;QACnB,WAAW,EAAE,sBAAsB;KACpC,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,OAAO,cAAc,CACnB;YACE,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC;YACtD,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC;SAC5B,EACD,KAAK,CACN,CAAA;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,74 @@
1
+ // Shared error-mapping helper: RFC 9457 problem → structured MCP tool error.
2
+ //
3
+ // Both the generic `matcher_invoke` (task 1.2.3) and every curated tool (task
4
+ // 1.3.2 onward) call `toToolError` so that a matcher failure renders the SAME
5
+ // structured shape regardless of which surface produced it. Keeping this in one
6
+ // place is what makes "curated and generic map errors identically" true rather
7
+ // than aspirational.
8
+ //
9
+ // The mapping deliberately PRESERVES the problem's status/title/detail/code
10
+ // rather than collapsing them into one human string — the model (and any UI on
11
+ // top of it) can branch on `status`/`code` and surface `detail` verbatim. The
12
+ // matcher's bearer token is never part of a MatcherApiError (see matcher/client.ts),
13
+ // so nothing token-bearing can leak through here.
14
+ import { MatcherApiError } from '../matcher/client.js';
15
+ /** Guidance shown alongside `retryable: true` — the request can be retried as-is. */
16
+ const GUIDANCE_RETRYABLE = 'matcher backend was unavailable/timed out; retry';
17
+ /** Guidance shown alongside `retryable: false` — the request itself must change. */
18
+ const GUIDANCE_NOT_RETRYABLE = 'fix the request';
19
+ /**
20
+ * Classify a matcher HTTP status into a retryability hint. Backend
21
+ * unavailability/timeouts (any 5xx — including the synthetic 504 for transport
22
+ * failures — and 429 Too Many Requests) are retryable as-is; every other
23
+ * client-side 4xx is not (the caller must change the request first).
24
+ */
25
+ function retryabilityFor(status) {
26
+ const retryable = status >= 500 || status === 429;
27
+ return {
28
+ retryable,
29
+ guidance: retryable ? GUIDANCE_RETRYABLE : GUIDANCE_NOT_RETRYABLE,
30
+ };
31
+ }
32
+ /**
33
+ * Map a `MatcherApiError` to an MCP tool error result (`isError: true`). The
34
+ * result carries two content blocks:
35
+ * 1. A one-line human summary (`status — title`) for quick reading.
36
+ * 2. The full structured problem as JSON, so a caller can branch on
37
+ * `status`/`code` and read `detail`/`errors` without re-parsing a sentence.
38
+ *
39
+ * The problem is preserved, never swallowed into a generic "request failed".
40
+ */
41
+ export function toToolError(err) {
42
+ const { status, problem } = err;
43
+ const { retryable, guidance } = retryabilityFor(status);
44
+ const payload = {
45
+ status,
46
+ ...(problem.title !== undefined ? { title: problem.title } : {}),
47
+ ...(problem.detail !== undefined ? { detail: problem.detail } : {}),
48
+ ...(problem.code !== undefined ? { code: problem.code } : {}),
49
+ ...(problem.type !== undefined ? { type: problem.type } : {}),
50
+ ...(problem.instance !== undefined ? { instance: problem.instance } : {}),
51
+ ...(problem.errors !== undefined ? { errors: problem.errors } : {}),
52
+ retryable,
53
+ guidance,
54
+ };
55
+ const summary = problem.title !== undefined
56
+ ? `matcher error ${status}: ${problem.title}`
57
+ : `matcher error ${status}`;
58
+ return {
59
+ isError: true,
60
+ content: [
61
+ { type: 'text', text: summary },
62
+ { type: 'text', text: JSON.stringify(payload) },
63
+ ],
64
+ };
65
+ }
66
+ /**
67
+ * Narrow an unknown thrown value to `MatcherApiError`. Lets callers route only
68
+ * matcher API failures through `toToolError` and re-throw anything else (a bug
69
+ * is not a matcher error and should not masquerade as one).
70
+ */
71
+ export function isMatcherApiError(err) {
72
+ return err instanceof MatcherApiError;
73
+ }
74
+ //# sourceMappingURL=tool-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/tools/tool-error.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,qBAAqB;AACrB,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,qFAAqF;AACrF,kDAAkD;AAIlD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AA4BtD,qFAAqF;AACrF,MAAM,kBAAkB,GACtB,kDAAkD,CAAA;AACpD,oFAAoF;AACpF,MAAM,sBAAsB,GAAG,iBAAiB,CAAA;AAEhD;;;;;GAKG;AACH,SAAS,eAAe,CAAC,MAAc;IAIrC,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI,MAAM,KAAK,GAAG,CAAA;IACjD,OAAO;QACL,SAAS;QACT,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,sBAAsB;KAClE,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,GAAoB;IAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAA;IAC/B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,OAAO,GAAqB;QAChC,MAAM;QACN,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,SAAS;QACT,QAAQ;KACT,CAAA;IAED,MAAM,OAAO,GACX,OAAO,CAAC,KAAK,KAAK,SAAS;QACzB,CAAC,CAAC,iBAAiB,MAAM,KAAK,OAAO,CAAC,KAAK,EAAE;QAC7C,CAAC,CAAC,iBAAiB,MAAM,EAAE,CAAA;IAE/B,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;YAC/B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;SAChD;KACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC5C,OAAO,GAAG,YAAY,eAAe,CAAA;AACvC,CAAC"}
@@ -0,0 +1,82 @@
1
+ // Inbound body-size guard + server-timeout constants for the HTTP transport.
2
+ //
3
+ // `transport/http.ts` currently reads the request body unbounded (every chunk
4
+ // appended to an array). That is a trivial memory-exhaustion / DoS vector for a
5
+ // network-reachable, credential-bearing relay. This module supplies a small,
6
+ // pure, testable guard the body reader uses to bound the accumulated size, plus
7
+ // the Node HTTP server timeout values integration applies in `createServer`.
8
+ //
9
+ // Token-relay rule: nothing here touches the `Authorization` header or any
10
+ // request content beyond byte counts. `BodyTooLargeError` carries only sizes —
11
+ // never any portion of the body or any token.
12
+ /** Default cap when a config value is not threaded in (matches Config default). */
13
+ export const DEFAULT_MAX_BODY_BYTES = 1_048_576; // 1 MiB
14
+ /**
15
+ * How long the server waits for a complete request (ms) before aborting it.
16
+ * Set on the Node HTTP server in integration (`createServer`). Generous enough
17
+ * for a slow large-but-legal request, short enough to shed a stuck connection.
18
+ */
19
+ export const REQUEST_TIMEOUT_MS = 60_000;
20
+ /**
21
+ * How long the server waits for the complete request headers (ms). Must be less
22
+ * than `REQUEST_TIMEOUT_MS` (Node enforces this) so a Slowloris-style trickle of
23
+ * headers is dropped well before the overall request budget. Set on the Node
24
+ * HTTP server in integration.
25
+ */
26
+ export const HEADERS_TIMEOUT_MS = 30_000;
27
+ /**
28
+ * Thrown when accumulated inbound body bytes exceed the configured limit. Reads
29
+ * as HTTP 413 (Payload Too Large) to an operator. Carries only the limit and the
30
+ * observed size — never any body content and never a token.
31
+ */
32
+ export class BodyTooLargeError extends Error {
33
+ /** HTTP status an operator should see — 413 Payload Too Large. */
34
+ status = 413;
35
+ /** The configured maximum, in bytes. */
36
+ limitBytes;
37
+ /** Bytes accumulated when the limit was first exceeded. */
38
+ observedBytes;
39
+ constructor(limitBytes, observedBytes) {
40
+ super(`request body exceeds the ${limitBytes}-byte limit ` +
41
+ `(received at least ${observedBytes} bytes)`);
42
+ this.name = 'BodyTooLargeError';
43
+ this.limitBytes = limitBytes;
44
+ this.observedBytes = observedBytes;
45
+ }
46
+ }
47
+ /**
48
+ * A running tally of inbound body bytes that fails closed once the limit is
49
+ * exceeded. The body reader calls `add(chunk.length)` for each chunk and stops
50
+ * (the guard throws) before appending an over-limit chunk to memory, so the
51
+ * relay never buffers more than `maxBodyBytes` (+ one chunk) of an attacker's
52
+ * payload.
53
+ *
54
+ * Pure and allocation-free beyond a single number — trivially unit-testable
55
+ * without a socket.
56
+ */
57
+ export class BodySizeGuard {
58
+ maxBodyBytes;
59
+ total = 0;
60
+ constructor(maxBodyBytes = DEFAULT_MAX_BODY_BYTES) {
61
+ if (!Number.isInteger(maxBodyBytes) || maxBodyBytes < 1) {
62
+ throw new Error(`invalid maxBodyBytes: ${maxBodyBytes} (expected a positive integer)`);
63
+ }
64
+ this.maxBodyBytes = maxBodyBytes;
65
+ }
66
+ /** Bytes accumulated so far. */
67
+ get bytes() {
68
+ return this.total;
69
+ }
70
+ /**
71
+ * Account for `chunkLength` more bytes. Throws `BodyTooLargeError` the moment
72
+ * the running total would exceed the limit — the caller must NOT have appended
73
+ * the chunk to its buffer yet, so the over-limit bytes are never retained.
74
+ */
75
+ add(chunkLength) {
76
+ this.total += chunkLength;
77
+ if (this.total > this.maxBodyBytes) {
78
+ throw new BodyTooLargeError(this.maxBodyBytes, this.total);
79
+ }
80
+ }
81
+ }
82
+ //# sourceMappingURL=body-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body-limit.js","sourceRoot":"","sources":["../../src/transport/body-limit.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,EAAE;AACF,2EAA2E;AAC3E,+EAA+E;AAC/E,8CAA8C;AAE9C,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAA,CAAC,QAAQ;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAA;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAA;AAExC;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,kEAAkE;IACzD,MAAM,GAAG,GAAG,CAAA;IACrB,wCAAwC;IAC/B,UAAU,CAAQ;IAC3B,2DAA2D;IAClD,aAAa,CAAQ;IAE9B,YAAY,UAAkB,EAAE,aAAqB;QACnD,KAAK,CACH,4BAA4B,UAAU,cAAc;YAClD,sBAAsB,aAAa,SAAS,CAC/C,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAA;IACpC,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IACP,YAAY,CAAQ;IAC7B,KAAK,GAAG,CAAC,CAAA;IAEjB,YAAY,eAAuB,sBAAsB;QACvD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CACb,yBAAyB,YAAY,gCAAgC,CACtE,CAAA;QACH,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;IAClC,CAAC;IAED,gCAAgC;IAChC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,WAAmB;QACrB,IAAI,CAAC,KAAK,IAAI,WAAW,CAAA;QACzB,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YACnC,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;CACF"}