@cyanheads/federal-regulations-mcp-server 0.2.1 → 0.2.2

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 (36) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +1 -1
  4. package/changelog/0.2.x/0.2.2.md +15 -0
  5. package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts +3 -3
  6. package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/definitions/find-comments.tool.js +8 -4
  8. package/dist/mcp-server/tools/definitions/find-comments.tool.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts +5 -4
  10. package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/definitions/get-docket.tool.js +10 -5
  12. package/dist/mcp-server/tools/definitions/get-docket.tool.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/get-document.tool.d.ts +1 -1
  14. package/dist/mcp-server/tools/definitions/get-document.tool.js +1 -1
  15. package/dist/mcp-server/tools/definitions/get-document.tool.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/index.d.ts +7 -7
  17. package/dist/services/ecfr/ecfr-service.d.ts +6 -1
  18. package/dist/services/ecfr/ecfr-service.d.ts.map +1 -1
  19. package/dist/services/ecfr/ecfr-service.js +14 -5
  20. package/dist/services/ecfr/ecfr-service.js.map +1 -1
  21. package/dist/services/ecfr-mirror/ecfr-mirror.js +11 -3
  22. package/dist/services/ecfr-mirror/ecfr-mirror.js.map +1 -1
  23. package/dist/services/federal-register/federal-register-service.d.ts +15 -5
  24. package/dist/services/federal-register/federal-register-service.d.ts.map +1 -1
  25. package/dist/services/federal-register/federal-register-service.js +33 -8
  26. package/dist/services/federal-register/federal-register-service.js.map +1 -1
  27. package/dist/services/regulations-gov/regulations-gov-service.d.ts +27 -8
  28. package/dist/services/regulations-gov/regulations-gov-service.d.ts.map +1 -1
  29. package/dist/services/regulations-gov/regulations-gov-service.js +76 -19
  30. package/dist/services/regulations-gov/regulations-gov-service.js.map +1 -1
  31. package/dist/services/upstream-failure.d.ts +83 -8
  32. package/dist/services/upstream-failure.d.ts.map +1 -1
  33. package/dist/services/upstream-failure.js +100 -8
  34. package/dist/services/upstream-failure.js.map +1 -1
  35. package/package.json +1 -1
  36. package/server.json +3 -3
@@ -1,14 +1,18 @@
1
1
  /**
2
- * @fileoverview Shared helper that stamps the `upstream_unavailable` contract
3
- * reason onto a transport failure on its way out of a service.
2
+ * @fileoverview The shared pieces of how a transport failure becomes
3
+ * `upstream_unavailable` on this surface: `fetchUpstream` raises one from a
4
+ * request that never produced a response, `rethrowTransportFailure` re-codes the
5
+ * 5xx statuses the status→code map does not call one, `retryTransportOnly` says
6
+ * which of them a retry can fix, and `withUpstreamReason` stamps the contract
7
+ * reason onto whichever transport failure reaches it.
4
8
  *
5
9
  * Every tool on this surface declares `upstream_unavailable`, but the failure it
6
- * describes is raised inside the framework's fetch pipeline, below any handler:
7
- * `fetchWithTimeout` classifies a 5xx, a network error, or a deadline, and
8
- * `withRetry` exhausts its attempts before the error surfaces. Nothing on that
9
- * path knows the calling tool's contract, so the answer used to reach the caller
10
- * with the right code and a null `data.reason` — the one failure an agent most
11
- * needs to switch on to decide whether to retry.
10
+ * describes is raised below any handler: the framework's fetch pipeline
11
+ * classifies a 5xx, a network error, or a deadline, and `withRetry` exhausts its
12
+ * attempts before the error surfaces. Nothing on that path knows the calling
13
+ * tool's contract, so the answer used to reach the caller with the right code and
14
+ * a null `data.reason` — the one failure an agent most needs to switch on to
15
+ * decide whether to retry.
12
16
  *
13
17
  * Services carry the reason the way the framework prescribes for code that has
14
18
  * no `ctx.fail`: put `reason` in the thrown error's `data`, and spread
@@ -18,6 +22,47 @@
18
22
  * @module services/upstream-failure
19
23
  */
20
24
  import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
25
+ /**
26
+ * Perform an upstream request, raising a classified `ServiceUnavailable` when it
27
+ * never produced a response.
28
+ *
29
+ * `fetchWithTimeout` already does this for the services that route through it,
30
+ * but a service that has to branch on `response.status` calls `fetch` directly
31
+ * and gets no such classification — `fetch` rejects with whatever the runtime
32
+ * throws, which is not an `McpError`, so it reaches the caller as an
33
+ * unclassified `InternalError` with no reason. That is the gap this closes.
34
+ *
35
+ * **Classified by position, not by message.** A rejection here means no
36
+ * `Response` was ever produced — DNS failure, refused connection, TLS failure, a
37
+ * socket dropped before headers — which is exactly the condition
38
+ * `upstream_unavailable` names. Matching the message text instead would be a rule
39
+ * per runtime: Bun answers both a refused connection and an unresolvable host
40
+ * with the one message "Unable to connect. Is the computer able to access the
41
+ * url?" and no `cause` at all, while Node says "fetch failed" and keeps the
42
+ * syscall detail one level below that. Neither puts `ECONNREFUSED`/`ENOTFOUND`
43
+ * where the framework's patterns for them look, so those never match. Reaching
44
+ * this catch is itself the evidence.
45
+ *
46
+ * An abort is the exception and re-throws untouched: the caller ending its own
47
+ * request is not an upstream failure, and the framework already classifies it.
48
+ *
49
+ * This wrapper adds no deadline of its own — the request is bounded only by
50
+ * `init.signal`, unlike the `fetchWithTimeout` legs.
51
+ */
52
+ export async function fetchUpstream(url, init, { service, operation }) {
53
+ try {
54
+ return await fetch(url, init);
55
+ }
56
+ catch (error) {
57
+ if (init.signal?.aborted || (error instanceof Error && error.name === 'AbortError')) {
58
+ throw error;
59
+ }
60
+ const detail = error instanceof Error ? error.message : String(error);
61
+ throw serviceUnavailable(`${service} could not be reached: ${detail}`, { operation }, {
62
+ cause: error,
63
+ });
64
+ }
65
+ }
21
66
  /**
22
67
  * Codes that mean the upstream never produced a usable response. Kept to these
23
68
  * two on purpose: a 404, a 400, a 401, or a 429 is an answer, and each already
@@ -28,6 +73,53 @@ const TRANSPORT_CODES = new Set([
28
73
  JsonRpcErrorCode.ServiceUnavailable,
29
74
  JsonRpcErrorCode.Timeout,
30
75
  ]);
76
+ /**
77
+ * Re-raise an upstream failure, re-coding a 5xx the status→code map does not
78
+ * already call a transport failure.
79
+ *
80
+ * Both HTTP helpers map 500 and 501 to `InternalError` and everything else in
81
+ * the range to `ServiceUnavailable`. That split is right for a status→code map —
82
+ * "the server has a bug" is not "the server is down" — but every definition here
83
+ * declares `upstream_unavailable` for a 5xx, and a 500 is the commonest one an
84
+ * upstream serves. Left alone it reached the caller as `InternalError` with no
85
+ * reason and no hint, and `withRetry` did not retry it, while the 502 beside it
86
+ * was retried and answered `upstream_unavailable`.
87
+ *
88
+ * **Keyed on the status, not the code or the message.** `data.status` is set only
89
+ * by the two helpers that read a real `Response`, so a caller abort (an
90
+ * `InternalError` carrying `errorSource: 'FetchAborted'` and no status) and a
91
+ * programmer error (no `data` at all) cannot reach this branch — which is the
92
+ * point, since both are `InternalError` too and neither is an unreachable
93
+ * upstream. An error already carrying a transport code is left alone rather than
94
+ * re-wrapped to the same code: a 504's `Timeout` then reaches
95
+ * {@link withUpstreamReason} as itself, and the 502/503 that need nothing done
96
+ * to them keep their own object and a `cause` chain one link shorter.
97
+ */
98
+ export function rethrowTransportFailure(error) {
99
+ if (error instanceof McpError && !TRANSPORT_CODES.has(error.code)) {
100
+ const status = error.data?.status;
101
+ if (typeof status === 'number' && status >= 500) {
102
+ throw serviceUnavailable(error.message, { ...error.data }, { cause: error });
103
+ }
104
+ }
105
+ throw error;
106
+ }
107
+ /**
108
+ * Retry predicate for a service that raises a non-retryable failure with a
109
+ * retryable code.
110
+ *
111
+ * `withRetry`'s default treats every `RateLimited` as transient unless the throw
112
+ * carries `data.retryable: false`. That flag is also the client's own backoff
113
+ * hint, so opting out through it tells the caller not to retry a failure the
114
+ * tools declare as retryable. Deciding it here keeps the wire honest: a transport
115
+ * failure is retried, an answer with a status is not, and an unclassified throw
116
+ * (a parse failure mid-pipeline) stays transient the way the default has it.
117
+ */
118
+ export function retryTransportOnly(error) {
119
+ if (!(error instanceof McpError))
120
+ return true;
121
+ return TRANSPORT_CODES.has(error.code);
122
+ }
31
123
  /**
32
124
  * Run upstream work, stamping `upstream_unavailable` on a transport failure.
33
125
  *
@@ -1 +1 @@
1
- {"version":3,"file":"upstream-failure.js","sourceRoot":"","sources":["../../src/services/upstream-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAE/F;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAmB;IAChD,gBAAgB,CAAC,kBAAkB;IACnC,gBAAgB,CAAC,OAAO;CACzB,CAAC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAI,IAAgB,EAAE,GAAY;IAClE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACjC,IAAI,CAAC,CAAC,GAAG,YAAY,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,GAAG,CAAC;QAC5E,MAAM,kBAAkB,CACtB,GAAG,CAAC,OAAO,EACX,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,sBAAsB,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,sBAAsB,CAAC,EAAE,EAC3F,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"upstream-failure.js","sourceRoot":"","sources":["../../src/services/upstream-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAU/F;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,IAAiB,EACjB,EAAE,OAAO,EAAE,SAAS,EAA0B;IAE9C,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;YACpF,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,kBAAkB,CACtB,GAAG,OAAO,0BAA0B,MAAM,EAAE,EAC5C,EAAE,SAAS,EAAE,EACb;YACE,KAAK,EAAE,KAAK;SACb,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAmB;IAChD,gBAAgB,CAAC,kBAAkB;IACnC,gBAAgB,CAAC,OAAO;CACzB,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,IAAI,KAAK,YAAY,QAAQ,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;QAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;YAChD,MAAM,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAI,IAAgB,EAAE,GAAY;IAClE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACjC,IAAI,CAAC,CAAC,GAAG,YAAY,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,GAAG,CAAC;QAC5E,MAAM,kBAAkB,CACtB,GAAG,CAAC,OAAO,EACX,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,sBAAsB,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,sBAAsB,CAAC,EAAE,EAC3F,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/federal-regulations-mcp-server",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "mcpName": "io.github.cyanheads/federal-regulations-mcp-server",
5
5
  "description": "Search and trace US federal rules across the Federal Register (proposed/final rules and notices), the eCFR (codified, point-in-time CFR full text, locally mirrored), and Regulations.gov (rulemaking dockets and public comments) via MCP. STDIO or Streamable HTTP.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/cyanheads/federal-regulations-mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.1",
9
+ "version": "0.2.2",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
@@ -19,7 +19,7 @@
19
19
  "registryBaseUrl": "https://registry.npmjs.org",
20
20
  "identifier": "@cyanheads/federal-regulations-mcp-server",
21
21
  "runtimeHint": "node",
22
- "version": "0.2.1",
22
+ "version": "0.2.2",
23
23
  "packageArguments": [
24
24
  {
25
25
  "type": "positional",
@@ -55,7 +55,7 @@
55
55
  "registryBaseUrl": "https://registry.npmjs.org",
56
56
  "identifier": "@cyanheads/federal-regulations-mcp-server",
57
57
  "runtimeHint": "node",
58
- "version": "0.2.1",
58
+ "version": "0.2.2",
59
59
  "packageArguments": [
60
60
  {
61
61
  "type": "positional",