boondmanager-mcp-server 2.15.2 → 2.17.0

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 (272) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE +16 -2
  3. package/README.md +60 -15
  4. package/dist/config/access-policy.d.ts.map +1 -1
  5. package/dist/config/access-policy.js +6 -18
  6. package/dist/config/access-policy.js.map +1 -1
  7. package/dist/config/dictionary-overrides.d.ts.map +1 -1
  8. package/dist/config/dictionary-overrides.js +2 -12
  9. package/dist/config/dictionary-overrides.js.map +1 -1
  10. package/dist/config/env.d.ts +63 -0
  11. package/dist/config/env.d.ts.map +1 -0
  12. package/dist/config/env.js +116 -0
  13. package/dist/config/env.js.map +1 -0
  14. package/dist/config/profiles.d.ts +22 -8
  15. package/dist/config/profiles.d.ts.map +1 -1
  16. package/dist/config/profiles.js +48 -4
  17. package/dist/config/profiles.js.map +1 -1
  18. package/dist/constants.d.ts +13 -2
  19. package/dist/constants.d.ts.map +1 -1
  20. package/dist/constants.js +25 -1
  21. package/dist/constants.js.map +1 -1
  22. package/dist/icons.d.ts.map +1 -1
  23. package/dist/icons.js +6 -1
  24. package/dist/icons.js.map +1 -1
  25. package/dist/index.js +33 -15
  26. package/dist/index.js.map +1 -1
  27. package/dist/instructions.d.ts +1 -1
  28. package/dist/instructions.d.ts.map +1 -1
  29. package/dist/instructions.js +6 -6
  30. package/dist/instructions.js.map +1 -1
  31. package/dist/prompts/index.d.ts +19 -1
  32. package/dist/prompts/index.d.ts.map +1 -1
  33. package/dist/prompts/index.js +495 -25
  34. package/dist/prompts/index.js.map +1 -1
  35. package/dist/prompts/periods.d.ts +45 -0
  36. package/dist/prompts/periods.d.ts.map +1 -0
  37. package/dist/prompts/periods.js +154 -0
  38. package/dist/prompts/periods.js.map +1 -0
  39. package/dist/resources/index.d.ts +14 -0
  40. package/dist/resources/index.d.ts.map +1 -1
  41. package/dist/resources/index.js +285 -0
  42. package/dist/resources/index.js.map +1 -1
  43. package/dist/resources/templates.d.ts +1 -1
  44. package/dist/resources/templates.d.ts.map +1 -1
  45. package/dist/resources/templates.js +2 -0
  46. package/dist/resources/templates.js.map +1 -1
  47. package/dist/schema-dialect.js.map +1 -1
  48. package/dist/schemas/filter-aliases.d.ts +1 -1
  49. package/dist/schemas/filter-aliases.d.ts.map +1 -1
  50. package/dist/schemas/filter-aliases.js +101 -2
  51. package/dist/schemas/filter-aliases.js.map +1 -1
  52. package/dist/schemas/index.d.ts +987 -161
  53. package/dist/schemas/index.d.ts.map +1 -1
  54. package/dist/schemas/index.js +853 -323
  55. package/dist/schemas/index.js.map +1 -1
  56. package/dist/server.d.ts.map +1 -1
  57. package/dist/server.js +10 -3
  58. package/dist/server.js.map +1 -1
  59. package/dist/services/boond-client.d.ts +29 -213
  60. package/dist/services/boond-client.d.ts.map +1 -1
  61. package/dist/services/boond-client.js +29 -1168
  62. package/dist/services/boond-client.js.map +1 -1
  63. package/dist/services/dictionary.d.ts +27 -3
  64. package/dist/services/dictionary.d.ts.map +1 -1
  65. package/dist/services/dictionary.js +60 -23
  66. package/dist/services/dictionary.js.map +1 -1
  67. package/dist/services/document-text.d.ts +32 -0
  68. package/dist/services/document-text.d.ts.map +1 -0
  69. package/dist/services/document-text.js +120 -0
  70. package/dist/services/document-text.js.map +1 -0
  71. package/dist/services/format/detail.d.ts +16 -0
  72. package/dist/services/format/detail.d.ts.map +1 -0
  73. package/dist/services/format/detail.js +36 -0
  74. package/dist/services/format/detail.js.map +1 -0
  75. package/dist/services/format/html.d.ts +19 -0
  76. package/dist/services/format/html.d.ts.map +1 -0
  77. package/dist/services/format/html.js +87 -0
  78. package/dist/services/format/html.js.map +1 -0
  79. package/dist/services/format/list.d.ts +3 -0
  80. package/dist/services/format/list.d.ts.map +1 -0
  81. package/dist/services/format/list.js +44 -0
  82. package/dist/services/format/list.js.map +1 -0
  83. package/dist/services/format/summary.d.ts +16 -0
  84. package/dist/services/format/summary.d.ts.map +1 -0
  85. package/dist/services/format/summary.js +172 -0
  86. package/dist/services/format/summary.js.map +1 -0
  87. package/dist/services/format/tab.d.ts +9 -0
  88. package/dist/services/format/tab.d.ts.map +1 -0
  89. package/dist/services/format/tab.js +32 -0
  90. package/dist/services/format/tab.js.map +1 -0
  91. package/dist/services/http/auth.d.ts +44 -0
  92. package/dist/services/http/auth.d.ts.map +1 -0
  93. package/dist/services/http/auth.js +146 -0
  94. package/dist/services/http/auth.js.map +1 -0
  95. package/dist/services/http/download.d.ts +60 -0
  96. package/dist/services/http/download.d.ts.map +1 -0
  97. package/dist/services/http/download.js +160 -0
  98. package/dist/services/http/download.js.map +1 -0
  99. package/dist/services/http/errors.d.ts +45 -0
  100. package/dist/services/http/errors.d.ts.map +1 -0
  101. package/dist/services/http/errors.js +188 -0
  102. package/dist/services/http/errors.js.map +1 -0
  103. package/dist/services/http/rate-limit.d.ts +40 -0
  104. package/dist/services/http/rate-limit.d.ts.map +1 -0
  105. package/dist/services/http/rate-limit.js +92 -0
  106. package/dist/services/http/rate-limit.js.map +1 -0
  107. package/dist/services/http/retry.d.ts +46 -0
  108. package/dist/services/http/retry.d.ts.map +1 -0
  109. package/dist/services/http/retry.js +101 -0
  110. package/dist/services/http/retry.js.map +1 -0
  111. package/dist/services/http/transport.d.ts +73 -0
  112. package/dist/services/http/transport.d.ts.map +1 -0
  113. package/dist/services/http/transport.js +272 -0
  114. package/dist/services/http/transport.js.map +1 -0
  115. package/dist/services/logger.d.ts +53 -2
  116. package/dist/services/logger.d.ts.map +1 -1
  117. package/dist/services/logger.js +82 -21
  118. package/dist/services/logger.js.map +1 -1
  119. package/dist/services/oauth.d.ts +12 -0
  120. package/dist/services/oauth.d.ts.map +1 -1
  121. package/dist/services/oauth.js +21 -8
  122. package/dist/services/oauth.js.map +1 -1
  123. package/dist/services/rate-limiter.d.ts +9 -2
  124. package/dist/services/rate-limiter.d.ts.map +1 -1
  125. package/dist/services/rate-limiter.js +18 -6
  126. package/dist/services/rate-limiter.js.map +1 -1
  127. package/dist/services/request-context.d.ts +47 -0
  128. package/dist/services/request-context.d.ts.map +1 -0
  129. package/dist/services/request-context.js +112 -0
  130. package/dist/services/request-context.js.map +1 -0
  131. package/dist/services/search.d.ts +26 -0
  132. package/dist/services/search.d.ts.map +1 -0
  133. package/dist/services/search.js +98 -0
  134. package/dist/services/search.js.map +1 -0
  135. package/dist/services/update-checker.d.ts.map +1 -1
  136. package/dist/services/update-checker.js +15 -8
  137. package/dist/services/update-checker.js.map +1 -1
  138. package/dist/tools/absences.d.ts +9 -0
  139. package/dist/tools/absences.d.ts.map +1 -1
  140. package/dist/tools/absences.js +65 -20
  141. package/dist/tools/absences.js.map +1 -1
  142. package/dist/tools/actions.d.ts.map +1 -1
  143. package/dist/tools/actions.js +11 -27
  144. package/dist/tools/actions.js.map +1 -1
  145. package/dist/tools/advantages.d.ts +2 -0
  146. package/dist/tools/advantages.d.ts.map +1 -1
  147. package/dist/tools/advantages.js +66 -13
  148. package/dist/tools/advantages.js.map +1 -1
  149. package/dist/tools/alerts.d.ts +18 -0
  150. package/dist/tools/alerts.d.ts.map +1 -0
  151. package/dist/tools/alerts.js +82 -0
  152. package/dist/tools/alerts.js.map +1 -0
  153. package/dist/tools/application.d.ts +2 -1
  154. package/dist/tools/application.d.ts.map +1 -1
  155. package/dist/tools/application.js +9 -3
  156. package/dist/tools/application.js.map +1 -1
  157. package/dist/tools/contacts.d.ts.map +1 -1
  158. package/dist/tools/contacts.js +3 -7
  159. package/dist/tools/contacts.js.map +1 -1
  160. package/dist/tools/contracts.d.ts +22 -0
  161. package/dist/tools/contracts.d.ts.map +1 -1
  162. package/dist/tools/contracts.js +185 -52
  163. package/dist/tools/contracts.js.map +1 -1
  164. package/dist/tools/crud-factory.d.ts +33 -2
  165. package/dist/tools/crud-factory.d.ts.map +1 -1
  166. package/dist/tools/crud-factory.js +42 -15
  167. package/dist/tools/crud-factory.js.map +1 -1
  168. package/dist/tools/deliveries.d.ts +3 -0
  169. package/dist/tools/deliveries.d.ts.map +1 -1
  170. package/dist/tools/deliveries.js +50 -94
  171. package/dist/tools/deliveries.js.map +1 -1
  172. package/dist/tools/description-builders.d.ts +0 -1
  173. package/dist/tools/description-builders.d.ts.map +1 -1
  174. package/dist/tools/description-builders.js +46 -3
  175. package/dist/tools/description-builders.js.map +1 -1
  176. package/dist/tools/documents.d.ts.map +1 -1
  177. package/dist/tools/documents.js +73 -21
  178. package/dist/tools/documents.js.map +1 -1
  179. package/dist/tools/expenses.d.ts.map +1 -1
  180. package/dist/tools/expenses.js +2 -11
  181. package/dist/tools/expenses.js.map +1 -1
  182. package/dist/tools/find.d.ts +61 -0
  183. package/dist/tools/find.d.ts.map +1 -0
  184. package/dist/tools/find.js +221 -0
  185. package/dist/tools/find.js.map +1 -0
  186. package/dist/tools/flags.d.ts +5 -0
  187. package/dist/tools/flags.d.ts.map +1 -1
  188. package/dist/tools/flags.js +116 -1
  189. package/dist/tools/flags.js.map +1 -1
  190. package/dist/tools/forms.d.ts +5 -0
  191. package/dist/tools/forms.d.ts.map +1 -0
  192. package/dist/tools/forms.js +64 -0
  193. package/dist/tools/forms.js.map +1 -0
  194. package/dist/tools/groupments.d.ts +5 -0
  195. package/dist/tools/groupments.d.ts.map +1 -0
  196. package/dist/tools/groupments.js +68 -0
  197. package/dist/tools/groupments.js.map +1 -0
  198. package/dist/tools/inactivities.d.ts +5 -0
  199. package/dist/tools/inactivities.d.ts.map +1 -0
  200. package/dist/tools/inactivities.js +59 -0
  201. package/dist/tools/inactivities.js.map +1 -0
  202. package/dist/tools/index.d.ts +4 -0
  203. package/dist/tools/index.d.ts.map +1 -1
  204. package/dist/tools/index.js +4 -0
  205. package/dist/tools/index.js.map +1 -1
  206. package/dist/tools/invoices.d.ts.map +1 -1
  207. package/dist/tools/invoices.js +27 -2
  208. package/dist/tools/invoices.js.map +1 -1
  209. package/dist/tools/linked-entity-filters.d.ts +48 -0
  210. package/dist/tools/linked-entity-filters.d.ts.map +1 -0
  211. package/dist/tools/linked-entity-filters.js +64 -0
  212. package/dist/tools/linked-entity-filters.js.map +1 -0
  213. package/dist/tools/notifications.d.ts.map +1 -1
  214. package/dist/tools/notifications.js +0 -5
  215. package/dist/tools/notifications.js.map +1 -1
  216. package/dist/tools/orders.d.ts.map +1 -1
  217. package/dist/tools/orders.js +33 -4
  218. package/dist/tools/orders.js.map +1 -1
  219. package/dist/tools/parameter-disclosure.js.map +1 -1
  220. package/dist/tools/payments.d.ts +3 -0
  221. package/dist/tools/payments.d.ts.map +1 -1
  222. package/dist/tools/payments.js +43 -109
  223. package/dist/tools/payments.js.map +1 -1
  224. package/dist/tools/positionings.d.ts.map +1 -1
  225. package/dist/tools/positionings.js +9 -39
  226. package/dist/tools/positionings.js.map +1 -1
  227. package/dist/tools/projects.d.ts.map +1 -1
  228. package/dist/tools/projects.js +5 -12
  229. package/dist/tools/projects.js.map +1 -1
  230. package/dist/tools/provider-invoices.d.ts +2 -0
  231. package/dist/tools/provider-invoices.d.ts.map +1 -1
  232. package/dist/tools/provider-invoices.js +30 -111
  233. package/dist/tools/provider-invoices.js.map +1 -1
  234. package/dist/tools/purchases.d.ts +2 -0
  235. package/dist/tools/purchases.d.ts.map +1 -1
  236. package/dist/tools/purchases.js +50 -104
  237. package/dist/tools/purchases.js.map +1 -1
  238. package/dist/tools/registration-decorators.d.ts +28 -0
  239. package/dist/tools/registration-decorators.d.ts.map +1 -1
  240. package/dist/tools/registration-decorators.js +107 -0
  241. package/dist/tools/registration-decorators.js.map +1 -1
  242. package/dist/tools/resources.d.ts.map +1 -1
  243. package/dist/tools/resources.js +27 -5
  244. package/dist/tools/resources.js.map +1 -1
  245. package/dist/tools/rights.d.ts +101 -0
  246. package/dist/tools/rights.d.ts.map +1 -0
  247. package/dist/tools/rights.js +75 -0
  248. package/dist/tools/rights.js.map +1 -0
  249. package/dist/tools/tab-tools.js +3 -3
  250. package/dist/tools/tab-tools.js.map +1 -1
  251. package/dist/tools/timesheets.d.ts +29 -0
  252. package/dist/tools/timesheets.d.ts.map +1 -1
  253. package/dist/tools/timesheets.js +196 -126
  254. package/dist/tools/timesheets.js.map +1 -1
  255. package/dist/tools/todolists.d.ts +2 -0
  256. package/dist/tools/todolists.d.ts.map +1 -1
  257. package/dist/tools/todolists.js +64 -1
  258. package/dist/tools/todolists.js.map +1 -1
  259. package/dist/tools/validations.d.ts +9 -0
  260. package/dist/tools/validations.d.ts.map +1 -1
  261. package/dist/tools/validations.js +86 -9
  262. package/dist/tools/validations.js.map +1 -1
  263. package/dist/tools/workflows.js +1 -1
  264. package/dist/tools/workflows.js.map +1 -1
  265. package/dist/transports/http.d.ts +116 -0
  266. package/dist/transports/http.d.ts.map +1 -1
  267. package/dist/transports/http.js +422 -92
  268. package/dist/transports/http.js.map +1 -1
  269. package/dist/types.d.ts +3 -3
  270. package/dist/types.d.ts.map +1 -1
  271. package/manifest.json +7 -7
  272. package/package.json +14 -10
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Retry policy for the BoondManager client: configuration, the retryability
3
+ * decision, `Retry-After` parsing and full-jitter backoff. Pure functions,
4
+ * exported for unit testing; `send()` in `transport.ts` is the only consumer.
5
+ */
6
+ import { readPositiveInt } from "../../config/env.js";
7
+ import { DEFAULT_HTTP_MAX_RETRIES, DEFAULT_HTTP_RETRY_BASE_MS, DEFAULT_HTTP_RETRY_MAX_MS } from "../../constants.js";
8
+ /** Resolve retry configuration from env, with safe fallbacks. Exported for tests. */
9
+ export function resolveRetryConfig() {
10
+ return {
11
+ maxRetries: readPositiveInt("BOOND_HTTP_MAX_RETRIES", DEFAULT_HTTP_MAX_RETRIES, process.env, { allowZero: true }),
12
+ baseDelayMs: readPositiveInt("BOOND_HTTP_RETRY_BASE_MS", DEFAULT_HTTP_RETRY_BASE_MS),
13
+ maxDelayMs: readPositiveInt("BOOND_HTTP_RETRY_MAX_MS", DEFAULT_HTTP_RETRY_MAX_MS),
14
+ };
15
+ }
16
+ /**
17
+ * Decide whether a failed attempt is worth retrying.
18
+ *
19
+ * Retry policy is intentionally conservative for non-idempotent verbs to avoid
20
+ * silently duplicating writes when the server's response was lost or delayed:
21
+ * - 429 (Too Many Requests) is always retried — the server explicitly
22
+ * rejected the request before processing it, so it is safe regardless of
23
+ * verb.
24
+ * - For GET only, 5xx responses, network failures, and timeouts are retried
25
+ * because GET is idempotent.
26
+ * - 4xx responses (other than 429) are never retried — the client must change
27
+ * the request before another attempt makes sense.
28
+ *
29
+ * Exported for unit testing.
30
+ */
31
+ export function isRetryable(method, status, isNetworkOrTimeout) {
32
+ if (status === 429)
33
+ return true;
34
+ if (method !== "GET")
35
+ return false;
36
+ if (isNetworkOrTimeout)
37
+ return true;
38
+ if (status !== undefined && status >= 500 && status < 600)
39
+ return true;
40
+ return false;
41
+ }
42
+ /**
43
+ * Parse a `Retry-After` header value into milliseconds.
44
+ *
45
+ * Accepts either a non-negative number of seconds or an HTTP-date. Returns
46
+ * null when the value is absent or unparseable. Negative computed delays are
47
+ * clamped to 0. Exported for unit testing.
48
+ */
49
+ export function parseRetryAfter(value, now = Date.now()) {
50
+ if (!value)
51
+ return null;
52
+ const trimmed = value.trim();
53
+ if (trimmed === "")
54
+ return null;
55
+ const seconds = Number(trimmed);
56
+ if (Number.isFinite(seconds)) {
57
+ // Numeric form is authoritative once we recognise it as a number — falling
58
+ // through to Date.parse on a negative/odd numeric would silently produce
59
+ // weird timestamps (e.g. Date.parse("-1") → year -1).
60
+ return seconds >= 0 ? Math.floor(seconds * 1000) : null;
61
+ }
62
+ const date = Date.parse(trimmed);
63
+ if (!Number.isNaN(date))
64
+ return Math.max(0, date - now);
65
+ return null;
66
+ }
67
+ /**
68
+ * Compute the next backoff delay using full jitter:
69
+ * delay = random(0, min(maxMs, baseMs * 2^attempt))
70
+ *
71
+ * Full jitter (vs. exponential-only) reduces thundering-herd risk when many
72
+ * clients retry in lockstep. Exported for unit testing.
73
+ */
74
+ export function computeBackoffMs(attempt, baseMs, maxMs, random = Math.random) {
75
+ const exp = baseMs * 2 ** attempt;
76
+ const capped = Math.min(maxMs, exp);
77
+ return Math.floor(random() * capped);
78
+ }
79
+ /**
80
+ * Backoff sleep. With a `signal`, the wait ends the moment it fires — a
81
+ * cancelled caller does not sit out a 5 s `Retry-After` (issue #231); the
82
+ * rejection carries the signal's reason.
83
+ */
84
+ export function sleep(ms, signal) {
85
+ if (signal?.aborted)
86
+ return Promise.reject(signal.reason ?? new Error("aborted"));
87
+ if (ms <= 0)
88
+ return Promise.resolve();
89
+ return new Promise((resolve, reject) => {
90
+ const timer = setTimeout(() => {
91
+ signal?.removeEventListener("abort", onAbort);
92
+ resolve();
93
+ }, ms);
94
+ function onAbort() {
95
+ clearTimeout(timer);
96
+ reject(signal.reason ?? new Error("aborted"));
97
+ }
98
+ signal?.addEventListener("abort", onAbort, { once: true });
99
+ });
100
+ }
101
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/services/http/retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { JsonApiResponse } from "../../types.js";
2
+ export type QueryValue = string | number | Array<string | number> | undefined;
3
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+ /**
5
+ * Resolve the per-request HTTP timeout in milliseconds.
6
+ *
7
+ * Reads BOOND_HTTP_TIMEOUT_MS at call time so tests / runtime overrides take
8
+ * effect without restarting the process. Falls back to the default for
9
+ * unset, non-numeric, or non-positive values.
10
+ *
11
+ * Exported for unit testing.
12
+ */
13
+ export declare function resolveTimeoutMs(): number;
14
+ /**
15
+ * Defense-in-depth against path traversal / query injection through entity
16
+ * ids interpolated into API paths at ~40 call sites. Even though the id
17
+ * schemas are now numeric-only, a future tool could forget to validate, so we
18
+ * assert here that the path is well-formed: it must start with `/`, carry no
19
+ * query (`?`) or fragment (`#`) — those arrive via `queryParams`, never the
20
+ * path — and contain no traversal (`..`) or percent/backslash escapes. Built
21
+ * paths only ever combine static segments with numeric ids and hyphenated tab
22
+ * names, so this rejects nothing legitimate. Exported for unit testing.
23
+ */
24
+ export declare function assertSafeApiPath(path: string): void;
25
+ /**
26
+ * Validates `path` and resolves it against `baseUrl`, returning the
27
+ * constructed URL. Throws if the path is unsafe (see `assertSafeApiPath`) or
28
+ * if the resolved URL escapes the configured API base origin/path. Exported
29
+ * for unit testing.
30
+ */
31
+ export declare function resolveApiUrl(baseUrl: string, path: string): URL;
32
+ /** What a helper hands to `send()`: everything but the plumbing. */
33
+ export interface SendOptions {
34
+ method?: HttpMethod;
35
+ query?: Record<string, QueryValue>;
36
+ /**
37
+ * Request body, reused verbatim on every attempt: a JSON string or a
38
+ * `FormData` (both are re-serialised by `fetch` per call, so a retry never
39
+ * sends a consumed stream).
40
+ */
41
+ body?: string | FormData;
42
+ /** Extra headers (`Accept`, `Content-Type`); the auth header is added per attempt. */
43
+ headers?: Record<string, string>;
44
+ /**
45
+ * Cancellation signal. Defaults to the current MCP request's `extra.signal`
46
+ * (`currentRequestSignal()`, issue #231) — pass one only to override it.
47
+ */
48
+ signal?: AbortSignal;
49
+ }
50
+ /**
51
+ * Send one request through the whole pipeline — URL guard, per-identity rate
52
+ * limiter, auth header resolved per attempt (so an OAuth refresh between two
53
+ * tries is picked up), per-attempt timeout, retry policy with `Retry-After`,
54
+ * error mapping — and return the first **2xx** `Response`, body unread.
55
+ *
56
+ * Throws `BoondApiError` on a non-2xx that the policy does not retry (or once
57
+ * retries are exhausted), the standard timeout `Error` when an attempt never
58
+ * completed, `RequestCancelledError` (name `AbortError`) when the client
59
+ * cancelled — checked before each attempt, while waiting for a rate-limit
60
+ * token, during the fetch itself and during a backoff, and never retried —
61
+ * and the raw network error otherwise.
62
+ */
63
+ export declare function send(path: string, options?: SendOptions): Promise<Response>;
64
+ /** JSON:API request. `body` is serialised for POST / PUT / PATCH only. */
65
+ export declare function apiRequest(path: string, method?: HttpMethod, body?: unknown, queryParams?: Record<string, QueryValue>): Promise<JsonApiResponse>;
66
+ /**
67
+ * POST a multipart/form-data payload to the BoondManager API (document
68
+ * upload). Form values are simple string fields — the file itself travels by
69
+ * reference via the `fileUrl` field (Boond downloads it server-side), so the
70
+ * MCP server never buffers file bytes.
71
+ */
72
+ export declare function apiUploadForm(path: string, fields: Record<string, string>): Promise<JsonApiResponse>;
73
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/services/http/transport.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAQtD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;AAC9E,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAWD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CASpD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAUhE;AAoBD,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiIrF;AAkBD,0EAA0E;AAC1E,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,UAAkB,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,CAU1B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAQ1G"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * The single HTTP send path towards BoondManager (issue #239).
3
+ *
4
+ * Before the split, `apiRequest`, `apiDownload` and `apiUploadForm` each
5
+ * carried their own copy of "resolve URL, acquire a rate-limit token, resolve
6
+ * the auth header, fetch with a timeout, map the error" — and they had
7
+ * diverged: the upload helper did not translate timeouts (a bare
8
+ * `TimeoutError` with no endpoint), retried nothing, and neither the download
9
+ * nor the upload helper honoured a 429. `send()` is now the only place that
10
+ * calls `fetch`, so the timeout, the rate limiter, the retry policy and the
11
+ * error shaping are the same for JSON, binary and multipart requests. The
12
+ * three helpers are thin wrappers that only decide what to do with the body.
13
+ */
14
+ import { readPositiveInt } from "../../config/env.js";
15
+ import { DEFAULT_HTTP_TIMEOUT_MS } from "../../constants.js";
16
+ import { getConfig } from "./auth.js";
17
+ import { BoondApiError, formatApiError, nonJsonResponseError, timeoutError } from "./errors.js";
18
+ import { getRateLimiter } from "./rate-limit.js";
19
+ import { computeBackoffMs, isRetryable, parseRetryAfter, resolveRetryConfig, sleep } from "./retry.js";
20
+ import { currentRequestContext, RequestCancelledError, throwIfCancelled } from "../request-context.js";
21
+ import { logger } from "../logger.js";
22
+ /**
23
+ * Resolve the per-request HTTP timeout in milliseconds.
24
+ *
25
+ * Reads BOOND_HTTP_TIMEOUT_MS at call time so tests / runtime overrides take
26
+ * effect without restarting the process. Falls back to the default for
27
+ * unset, non-numeric, or non-positive values.
28
+ *
29
+ * Exported for unit testing.
30
+ */
31
+ export function resolveTimeoutMs() {
32
+ return readPositiveInt("BOOND_HTTP_TIMEOUT_MS", DEFAULT_HTTP_TIMEOUT_MS);
33
+ }
34
+ /** True when an error from fetch() came from an AbortSignal firing. */
35
+ function isAbortError(err) {
36
+ if (!(err instanceof Error))
37
+ return false;
38
+ // AbortSignal.timeout() rejects with a DOMException whose name is "TimeoutError";
39
+ // generic aborts surface as "AbortError". Both indicate the request never
40
+ // completed end-to-end and should be reported as a timeout.
41
+ return err.name === "TimeoutError" || err.name === "AbortError";
42
+ }
43
+ /**
44
+ * Defense-in-depth against path traversal / query injection through entity
45
+ * ids interpolated into API paths at ~40 call sites. Even though the id
46
+ * schemas are now numeric-only, a future tool could forget to validate, so we
47
+ * assert here that the path is well-formed: it must start with `/`, carry no
48
+ * query (`?`) or fragment (`#`) — those arrive via `queryParams`, never the
49
+ * path — and contain no traversal (`..`) or percent/backslash escapes. Built
50
+ * paths only ever combine static segments with numeric ids and hyphenated tab
51
+ * names, so this rejects nothing legitimate. Exported for unit testing.
52
+ */
53
+ export function assertSafeApiPath(path) {
54
+ if (!path.startsWith("/")) {
55
+ throw new Error(`Invalid API path (must start with "/"): ${path}`);
56
+ }
57
+ // `?`/`#` would inject a query/fragment; `%`/`\` could encode a traversal;
58
+ // `..` is a literal traversal segment.
59
+ if (/[?#%\\]/.test(path) || path.includes("..")) {
60
+ throw new Error(`Unsafe API path rejected: ${path}`);
61
+ }
62
+ }
63
+ /**
64
+ * Validates `path` and resolves it against `baseUrl`, returning the
65
+ * constructed URL. Throws if the path is unsafe (see `assertSafeApiPath`) or
66
+ * if the resolved URL escapes the configured API base origin/path. Exported
67
+ * for unit testing.
68
+ */
69
+ export function resolveApiUrl(baseUrl, path) {
70
+ assertSafeApiPath(path);
71
+ const url = new URL(`${baseUrl}${path}`);
72
+ // Belt-and-braces: confirm the constructed URL did not escape the API base
73
+ // origin/path despite the textual guard above.
74
+ const base = new URL(baseUrl);
75
+ if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
76
+ throw new Error(`API path escaped the configured base URL: ${path}`);
77
+ }
78
+ return url;
79
+ }
80
+ /** Append query parameters; arrays use BoondManager's repeated bracket notation (`key[]=v1&key[]=v2`). */
81
+ function applyQuery(url, queryParams) {
82
+ if (!queryParams)
83
+ return;
84
+ for (const [key, value] of Object.entries(queryParams)) {
85
+ if (value === undefined || value === null)
86
+ continue;
87
+ if (Array.isArray(value)) {
88
+ const bracketKey = key.endsWith("[]") ? key : `${key}[]`;
89
+ for (const v of value) {
90
+ if (v !== undefined && v !== null && v !== "") {
91
+ url.searchParams.append(bracketKey, String(v));
92
+ }
93
+ }
94
+ }
95
+ else {
96
+ url.searchParams.set(key, String(value));
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Send one request through the whole pipeline — URL guard, per-identity rate
102
+ * limiter, auth header resolved per attempt (so an OAuth refresh between two
103
+ * tries is picked up), per-attempt timeout, retry policy with `Retry-After`,
104
+ * error mapping — and return the first **2xx** `Response`, body unread.
105
+ *
106
+ * Throws `BoondApiError` on a non-2xx that the policy does not retry (or once
107
+ * retries are exhausted), the standard timeout `Error` when an attempt never
108
+ * completed, `RequestCancelledError` (name `AbortError`) when the client
109
+ * cancelled — checked before each attempt, while waiting for a rate-limit
110
+ * token, during the fetch itself and during a backoff, and never retried —
111
+ * and the raw network error otherwise.
112
+ */
113
+ export async function send(path, options = {}) {
114
+ const method = options.method ?? "GET";
115
+ const { baseUrl, auth } = getConfig();
116
+ const url = resolveApiUrl(baseUrl, path);
117
+ applyQuery(url, options.query);
118
+ const timeoutMs = resolveTimeoutMs();
119
+ const retry = resolveRetryConfig();
120
+ const totalAttempts = retry.maxRetries + 1;
121
+ const limiter = getRateLimiter();
122
+ const context = currentRequestContext();
123
+ const signal = options.signal ?? context?.signal;
124
+ const corrId = context?.corrId;
125
+ // Correlation towards BoondManager (#236): the request id every log line
126
+ // carries, and the client's W3C trace context when it sent one. Header
127
+ // propagation only — no tracing SDK.
128
+ const correlationHeaders = {
129
+ ...(corrId ? { "X-Request-Id": corrId } : {}),
130
+ ...(context?.traceparent ? { traceparent: context.traceparent } : {}),
131
+ };
132
+ let lastError;
133
+ for (let attempt = 0; attempt < totalAttempts; attempt++) {
134
+ throwIfCancelled(signal, method, path);
135
+ // Acquire a token before each attempt so retries also count toward the
136
+ // rate budget — this is what actually protects us from feedback loops
137
+ // (transient 5xx → retry → transient 5xx → …) saturating the API.
138
+ if (limiter) {
139
+ try {
140
+ await limiter.acquire(signal);
141
+ }
142
+ catch (err) {
143
+ throwIfCancelled(signal, method, path);
144
+ throw err;
145
+ }
146
+ }
147
+ const authHeader = await auth();
148
+ const headers = {
149
+ [authHeader.name]: authHeader.value,
150
+ ...correlationHeaders,
151
+ ...options.headers,
152
+ };
153
+ const attemptStartedAt = Date.now();
154
+ const fetchOptions = {
155
+ method,
156
+ headers,
157
+ // Each attempt gets its own timeout signal — once a signal has fired it
158
+ // can't be reused for the next try. The request's cancellation signal
159
+ // is composed in so `notifications/cancelled` aborts the socket too.
160
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
161
+ };
162
+ if (options.body !== undefined)
163
+ fetchOptions.body = options.body;
164
+ let response;
165
+ let networkError;
166
+ try {
167
+ response = await fetch(url.toString(), fetchOptions);
168
+ }
169
+ catch (err) {
170
+ // A cancellation is not a network failure: no retry, and the error
171
+ // says "cancelled", not "timed out".
172
+ if (signal?.aborted)
173
+ throw new RequestCancelledError(method, path, err);
174
+ networkError = isAbortError(err)
175
+ ? timeoutError(timeoutMs, method, path, err)
176
+ : err instanceof Error
177
+ ? err
178
+ : new Error(String(err));
179
+ }
180
+ // The query string is deliberately left out of every log line: `keywords`
181
+ // and the perimeter filters are end-user data.
182
+ const logFields = { corrId, method, path, attempt: attempt + 1, durationMs: Date.now() - attemptStartedAt };
183
+ if (response && response.ok) {
184
+ logger.debug({ ...logFields, status: response.status }, "boondmanager request");
185
+ return response;
186
+ }
187
+ let attemptError;
188
+ let isNetworkOrTimeout = false;
189
+ if (response) {
190
+ const errorText = await response.text().catch(() => "");
191
+ attemptError = new BoondApiError(formatApiError(response.status, response.statusText, method, path, errorText), response.status, method, path);
192
+ }
193
+ else {
194
+ attemptError = networkError;
195
+ isNetworkOrTimeout = true;
196
+ }
197
+ const hasMoreAttempts = attempt < totalAttempts - 1;
198
+ const timedOut = isNetworkOrTimeout && isAbortError(networkError.cause);
199
+ const failure = response ? { status: response.status } : { reason: timedOut ? "timeout" : "network" };
200
+ if (!hasMoreAttempts || !isRetryable(method, response?.status, isNetworkOrTimeout)) {
201
+ // A final 429 or timeout is worth a warning on its own; any other final
202
+ // failure reaches the tool log as `ok: false` and the tool's error text.
203
+ if (response?.status === 429 || timedOut) {
204
+ logger.warn({ ...logFields, ...failure, totalAttempts }, "boondmanager request failed");
205
+ }
206
+ else {
207
+ logger.debug({ ...logFields, ...failure, totalAttempts }, "boondmanager request failed");
208
+ }
209
+ throw attemptError;
210
+ }
211
+ // Only inspect Retry-After once we've actually decided to retry — keeps
212
+ // the fast path off the headers object and matches tests that build
213
+ // minimal Response stubs.
214
+ const retryAfterMs = response ? parseRetryAfter(response.headers?.get("retry-after") ?? null) : null;
215
+ const backoff = retryAfterMs !== null
216
+ ? Math.min(retry.maxDelayMs, retryAfterMs)
217
+ : computeBackoffMs(attempt, retry.baseDelayMs, retry.maxDelayMs);
218
+ logger.warn({ ...logFields, ...failure, totalAttempts, backoffMs: backoff }, "boondmanager request retried");
219
+ try {
220
+ await sleep(backoff, signal);
221
+ }
222
+ catch (err) {
223
+ throw new RequestCancelledError(method, path, err);
224
+ }
225
+ lastError = attemptError;
226
+ }
227
+ // Defensive — the loop always returns or throws. If somehow exhausted:
228
+ throw lastError ?? new Error("BoondManager API request exhausted retries with no recorded error.");
229
+ }
230
+ /**
231
+ * Read a 2xx body as JSON:API. A `204` / empty body (DELETE) is an empty page;
232
+ * a body that is not JSON becomes an error that names the endpoint instead of
233
+ * a bare `SyntaxError`.
234
+ */
235
+ async function readJsonApi(response, method, path) {
236
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
237
+ return { data: [] };
238
+ }
239
+ try {
240
+ return (await response.json());
241
+ }
242
+ catch (err) {
243
+ throw nonJsonResponseError(response.status, method, path, err);
244
+ }
245
+ }
246
+ /** JSON:API request. `body` is serialised for POST / PUT / PATCH only. */
247
+ export async function apiRequest(path, method = "GET", body, queryParams) {
248
+ const serialized = body && (method === "POST" || method === "PUT" || method === "PATCH") ? JSON.stringify(body) : undefined;
249
+ const response = await send(path, {
250
+ method,
251
+ ...(queryParams !== undefined ? { query: queryParams } : {}),
252
+ ...(serialized !== undefined ? { body: serialized } : {}),
253
+ headers: { Accept: "application/json", "Content-Type": "application/json" },
254
+ });
255
+ return readJsonApi(response, method, path);
256
+ }
257
+ /**
258
+ * POST a multipart/form-data payload to the BoondManager API (document
259
+ * upload). Form values are simple string fields — the file itself travels by
260
+ * reference via the `fileUrl` field (Boond downloads it server-side), so the
261
+ * MCP server never buffers file bytes.
262
+ */
263
+ export async function apiUploadForm(path, fields) {
264
+ const form = new FormData();
265
+ for (const [key, value] of Object.entries(fields)) {
266
+ form.set(key, value);
267
+ }
268
+ // No Content-Type header: fetch derives the multipart boundary from FormData.
269
+ const response = await send(path, { method: "POST", body: form, headers: { Accept: "application/json" } });
270
+ return readJsonApi(response, "POST", path);
271
+ }
272
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/services/http/transport.ts"],"names":[],"mappings":"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"}
@@ -3,14 +3,65 @@ import pino from "pino";
3
3
  * Redaction paths for the structured logger. Defence-in-depth: nothing in the
4
4
  * codebase currently logs auth material, but if a request/error object that
5
5
  * carries credentials is ever passed to the logger, these paths censor it
6
- * before it reaches stdout / a log aggregator. Covers the BoondManager JWT
6
+ * before it reaches stderr / a log aggregator. Covers the BoondManager JWT
7
7
  * header, OAuth Bearer headers, and raw access tokens at one level of nesting.
8
8
  */
9
9
  export declare const REDACT_PATHS: string[];
10
+ /**
11
+ * File descriptor every log line is written to: **stderr, on every transport**.
12
+ *
13
+ * On the stdio transport stdout *is* the JSON-RPC channel — anything that is
14
+ * not an MCP message corrupts the stream and the client drops the connection.
15
+ * Pino defaults to fd 1, and so does the pino-pretty transport when no
16
+ * `destination` is given, which is exactly how the "Access policy active"
17
+ * line, the update notice and the dictionary-override warnings used to land in
18
+ * the middle of the protocol (issue #225). stderr is what Claude Desktop
19
+ * captures into its log viewer, and it is equally fine for the HTTP transport,
20
+ * so there is one destination rather than one per transport.
21
+ */
22
+ export declare const LOG_DESTINATION_FD = 2;
23
+ /**
24
+ * Read the log level from env, falling back to 'info' for production-friendly
25
+ * defaults. DEBUG / trace logs are useful during development but too noisy
26
+ * in production. Pino's level hierarchy: trace < debug < info < warn < error < fatal.
27
+ */
28
+ export declare function resolveLogLevel(env?: NodeJS.ProcessEnv): pino.Level;
29
+ /** Human-readable (pretty) output in dev, JSON in prod. Override via LOG_FORMAT. */
30
+ export declare function usePrettyOutput(env?: NodeJS.ProcessEnv): boolean;
31
+ /**
32
+ * `pino-pretty` is a **devDependency** (issue #246): it only serves the
33
+ * human-readable output of a development shell, and the `.mcpb` bundle and
34
+ * the Docker image are built with `--omit=dev`. Pino loads a transport by
35
+ * module name in a worker thread and throws at logger creation when the
36
+ * target is missing, so the pretty branch is taken only when the module
37
+ * resolves — otherwise the logger silently falls back to JSON on stderr,
38
+ * which is the production shape anyway.
39
+ */
40
+ export declare function isPrettyTransportAvailable(): boolean;
41
+ export type LoggerConfig = {
42
+ format: "pretty";
43
+ options: pino.LoggerOptions & {
44
+ transport: pino.TransportSingleOptions;
45
+ };
46
+ } | {
47
+ format: "json";
48
+ options: pino.LoggerOptions;
49
+ destinationFd: number;
50
+ };
51
+ /**
52
+ * The two shapes the logger can take, as data — so a test can assert where each
53
+ * one writes without spawning a process. Pino refuses a stream argument when
54
+ * `transport` is set (the transport runs in a worker thread), which is why the
55
+ * destination travels inside the pino-pretty options on one branch and as a
56
+ * `pino.destination()` on the other.
57
+ */
58
+ export declare function resolveLoggerConfig(env?: NodeJS.ProcessEnv, prettyAvailable?: boolean): LoggerConfig;
59
+ export declare function createLogger(env?: NodeJS.ProcessEnv): pino.Logger;
10
60
  /**
11
61
  * Centralized structured logger. Use this instead of console.log/error for
12
62
  * all application logging — it provides timestamps, levels, and JSON output
13
- * (when LOG_FORMAT=json) that plays nicely with log aggregators.
63
+ * (when LOG_FORMAT=json) that plays nicely with log aggregators. It always
64
+ * writes to stderr (see `LOG_DESTINATION_FD`).
14
65
  *
15
66
  * Example:
16
67
  * logger.info({ sessionId: "abc", userId: 123 }, "Session initialized");
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAYxB,CAAC;AAcF;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,6BAejB,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAYxB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAIpC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAAC,KAAK,CAIhF;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAOpD;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC,aAAa,GAAG;QAAE,SAAS,EAAE,IAAI,CAAC,sBAAsB,CAAA;KAAE,CAAA;CAAE,GAC9F;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,eAAe,GAAE,OAAsC,GACtD,YAAY,CAuBd;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAAC,MAAM,CAI9E;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,6BAAiB,CAAC;AAErC;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C"}