@blen/fedreg-mcp-server 2.0.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 (161) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +15 -0
  4. package/README.md +219 -0
  5. package/SECURITY.md +99 -0
  6. package/dist/auth/embedded.d.ts +2 -0
  7. package/dist/auth/embedded.d.ts.map +1 -0
  8. package/dist/auth/embedded.js +17 -0
  9. package/dist/auth/embedded.js.map +1 -0
  10. package/dist/auth/index.d.ts +2 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +2 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/bin.d.ts +3 -0
  15. package/dist/bin.d.ts.map +1 -0
  16. package/dist/bin.js +120 -0
  17. package/dist/bin.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +8 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/sandbox/deno.d.ts +13 -0
  23. package/dist/sandbox/deno.d.ts.map +1 -0
  24. package/dist/sandbox/deno.js +203 -0
  25. package/dist/sandbox/deno.js.map +1 -0
  26. package/dist/sandbox/index.d.ts +20 -0
  27. package/dist/sandbox/index.d.ts.map +1 -0
  28. package/dist/sandbox/index.js +46 -0
  29. package/dist/sandbox/index.js.map +1 -0
  30. package/dist/sandbox/isolate.d.ts +7 -0
  31. package/dist/sandbox/isolate.d.ts.map +1 -0
  32. package/dist/sandbox/isolate.js +100 -0
  33. package/dist/sandbox/isolate.js.map +1 -0
  34. package/dist/sandbox/policy.d.ts +7 -0
  35. package/dist/sandbox/policy.d.ts.map +1 -0
  36. package/dist/sandbox/policy.js +70 -0
  37. package/dist/sandbox/policy.js.map +1 -0
  38. package/dist/sandbox/types.d.ts +41 -0
  39. package/dist/sandbox/types.d.ts.map +1 -0
  40. package/dist/sandbox/types.js +2 -0
  41. package/dist/sandbox/types.js.map +1 -0
  42. package/dist/sdk/bindings.d.ts +10 -0
  43. package/dist/sdk/bindings.d.ts.map +1 -0
  44. package/dist/sdk/bindings.js +15 -0
  45. package/dist/sdk/bindings.js.map +1 -0
  46. package/dist/sdk/ecfr-client.d.ts +61 -0
  47. package/dist/sdk/ecfr-client.d.ts.map +1 -0
  48. package/dist/sdk/ecfr-client.js +45 -0
  49. package/dist/sdk/ecfr-client.js.map +1 -0
  50. package/dist/sdk/fr-client.d.ts +81 -0
  51. package/dist/sdk/fr-client.d.ts.map +1 -0
  52. package/dist/sdk/fr-client.js +77 -0
  53. package/dist/sdk/fr-client.js.map +1 -0
  54. package/dist/sdk/regs-client.d.ts +34 -0
  55. package/dist/sdk/regs-client.d.ts.map +1 -0
  56. package/dist/sdk/regs-client.js +44 -0
  57. package/dist/sdk/regs-client.js.map +1 -0
  58. package/dist/sdk/runtime.d.ts +21 -0
  59. package/dist/sdk/runtime.d.ts.map +1 -0
  60. package/dist/sdk/runtime.js +31 -0
  61. package/dist/sdk/runtime.js.map +1 -0
  62. package/dist/sdk/sources/corpus-loader.d.ts +6 -0
  63. package/dist/sdk/sources/corpus-loader.d.ts.map +1 -0
  64. package/dist/sdk/sources/corpus-loader.js +9 -0
  65. package/dist/sdk/sources/corpus-loader.js.map +1 -0
  66. package/dist/sdk/sources/ecfr.d.ts +3 -0
  67. package/dist/sdk/sources/ecfr.d.ts.map +1 -0
  68. package/dist/sdk/sources/ecfr.js +12 -0
  69. package/dist/sdk/sources/ecfr.js.map +1 -0
  70. package/dist/sdk/sources/fr.d.ts +3 -0
  71. package/dist/sdk/sources/fr.d.ts.map +1 -0
  72. package/dist/sdk/sources/fr.js +12 -0
  73. package/dist/sdk/sources/fr.js.map +1 -0
  74. package/dist/sdk/sources/index.d.ts +7 -0
  75. package/dist/sdk/sources/index.d.ts.map +1 -0
  76. package/dist/sdk/sources/index.js +25 -0
  77. package/dist/sdk/sources/index.js.map +1 -0
  78. package/dist/sdk/sources/regs.d.ts +3 -0
  79. package/dist/sdk/sources/regs.d.ts.map +1 -0
  80. package/dist/sdk/sources/regs.js +24 -0
  81. package/dist/sdk/sources/regs.js.map +1 -0
  82. package/dist/sdk/sources/source.d.ts +32 -0
  83. package/dist/sdk/sources/source.d.ts.map +1 -0
  84. package/dist/sdk/sources/source.js +2 -0
  85. package/dist/sdk/sources/source.js.map +1 -0
  86. package/dist/search/bm25.d.ts +25 -0
  87. package/dist/search/bm25.d.ts.map +1 -0
  88. package/dist/search/bm25.js +67 -0
  89. package/dist/search/bm25.js.map +1 -0
  90. package/dist/search/corpus.d.ts +24 -0
  91. package/dist/search/corpus.d.ts.map +1 -0
  92. package/dist/search/corpus.js +23 -0
  93. package/dist/search/corpus.js.map +1 -0
  94. package/dist/server/authz.d.ts +18 -0
  95. package/dist/server/authz.d.ts.map +1 -0
  96. package/dist/server/authz.js +42 -0
  97. package/dist/server/authz.js.map +1 -0
  98. package/dist/server/http.d.ts +21 -0
  99. package/dist/server/http.d.ts.map +1 -0
  100. package/dist/server/http.js +159 -0
  101. package/dist/server/http.js.map +1 -0
  102. package/dist/server/ipRateLimiter.d.ts +11 -0
  103. package/dist/server/ipRateLimiter.d.ts.map +1 -0
  104. package/dist/server/ipRateLimiter.js +27 -0
  105. package/dist/server/ipRateLimiter.js.map +1 -0
  106. package/dist/server/mcpServer.d.ts +15 -0
  107. package/dist/server/mcpServer.d.ts.map +1 -0
  108. package/dist/server/mcpServer.js +67 -0
  109. package/dist/server/mcpServer.js.map +1 -0
  110. package/dist/server/stdio.d.ts +3 -0
  111. package/dist/server/stdio.d.ts.map +1 -0
  112. package/dist/server/stdio.js +16 -0
  113. package/dist/server/stdio.js.map +1 -0
  114. package/dist/server/toolCatalog.d.ts +20 -0
  115. package/dist/server/toolCatalog.d.ts.map +1 -0
  116. package/dist/server/toolCatalog.js +29 -0
  117. package/dist/server/toolCatalog.js.map +1 -0
  118. package/dist/server/wellKnown.d.ts +16 -0
  119. package/dist/server/wellKnown.d.ts.map +1 -0
  120. package/dist/server/wellKnown.js +17 -0
  121. package/dist/server/wellKnown.js.map +1 -0
  122. package/dist/server/zodToJsonSchema.d.ts +7 -0
  123. package/dist/server/zodToJsonSchema.d.ts.map +1 -0
  124. package/dist/server/zodToJsonSchema.js +44 -0
  125. package/dist/server/zodToJsonSchema.js.map +1 -0
  126. package/dist/supervisor/index.d.ts +19 -0
  127. package/dist/supervisor/index.d.ts.map +1 -0
  128. package/dist/supervisor/index.js +22 -0
  129. package/dist/supervisor/index.js.map +1 -0
  130. package/dist/tools/describeSchema.d.ts +35 -0
  131. package/dist/tools/describeSchema.d.ts.map +1 -0
  132. package/dist/tools/describeSchema.js +14 -0
  133. package/dist/tools/describeSchema.js.map +1 -0
  134. package/dist/tools/execute.d.ts +33 -0
  135. package/dist/tools/execute.d.ts.map +1 -0
  136. package/dist/tools/execute.js +28 -0
  137. package/dist/tools/execute.js.map +1 -0
  138. package/dist/tools/searchApi.d.ts +27 -0
  139. package/dist/tools/searchApi.d.ts.map +1 -0
  140. package/dist/tools/searchApi.js +14 -0
  141. package/dist/tools/searchApi.js.map +1 -0
  142. package/dist/util/httpClient.d.ts +46 -0
  143. package/dist/util/httpClient.d.ts.map +1 -0
  144. package/dist/util/httpClient.js +139 -0
  145. package/dist/util/httpClient.js.map +1 -0
  146. package/dist/util/logger.d.ts +7 -0
  147. package/dist/util/logger.d.ts.map +1 -0
  148. package/dist/util/logger.js +19 -0
  149. package/dist/util/logger.js.map +1 -0
  150. package/dist/util/quotas.d.ts +13 -0
  151. package/dist/util/quotas.d.ts.map +1 -0
  152. package/dist/util/quotas.js +29 -0
  153. package/dist/util/quotas.js.map +1 -0
  154. package/dist/util/tokenBucket.d.ts +11 -0
  155. package/dist/util/tokenBucket.d.ts.map +1 -0
  156. package/dist/util/tokenBucket.js +30 -0
  157. package/dist/util/tokenBucket.js.map +1 -0
  158. package/package.json +95 -0
  159. package/schema/ecfr.json +148 -0
  160. package/schema/fr.json +194 -0
  161. package/schema/regs.json +67 -0
@@ -0,0 +1,159 @@
1
+ import { createServer } from 'node:http';
2
+ import { createMcpHandler } from '@modelcontextprotocol/server';
3
+ import { toNodeHandler } from '@modelcontextprotocol/node';
4
+ import { buildMcpServer, MCP_PROTOCOL_VERSION } from './mcpServer.js';
5
+ import { IpRateLimiter } from './ipRateLimiter.js';
6
+ import { SubjectQuota } from '../util/quotas.js';
7
+ import { buildVerifier } from './authz.js';
8
+ import { oauthProtectedResource } from './wellKnown.js';
9
+ import { log } from '../util/logger.js';
10
+ const MCP_PATH = '/mcp';
11
+ const RESOURCE_METADATA_PATH = '/.well-known/oauth-protected-resource/mcp';
12
+ /** Key the tool-catalog context off the subject this request authenticated as. */
13
+ const SUBJECT_KEY = 'fedreg.subject';
14
+ /** Cap on echoed error text: handler rejections quote caller-supplied headers and body. */
15
+ const MAX_LOGGED_ERROR_CHARS = 300;
16
+ function truncate(message) {
17
+ return message.length > MAX_LOGGED_ERROR_CHARS
18
+ ? `${message.slice(0, MAX_LOGGED_ERROR_CHARS)}…[truncated]`
19
+ : message;
20
+ }
21
+ function subjectOf(ctx) {
22
+ const s = ctx.authInfo?.extra?.[SUBJECT_KEY];
23
+ return typeof s === 'string' ? s : undefined;
24
+ }
25
+ /**
26
+ * Scopes actually granted to the presented token, read from the standard OAuth claims
27
+ * (`scope` as a space-delimited string, or `scp` as an array).
28
+ *
29
+ * Deliberately NOT `opts.auth.scopes` — that is the server's advertised
30
+ * `scopes_supported` list, and copying it here would hand every caller an `AuthInfo`
31
+ * claiming every configured scope regardless of what its token was granted.
32
+ */
33
+ function grantedScopes(claims) {
34
+ const raw = claims.scope ?? claims.scp;
35
+ if (typeof raw === 'string')
36
+ return raw.split(' ').filter(Boolean);
37
+ if (Array.isArray(raw))
38
+ return raw.filter((s) => typeof s === 'string');
39
+ return [];
40
+ }
41
+ export async function startHttp(deps, opts) {
42
+ const verifier = buildVerifier(opts.auth);
43
+ const limiter = new IpRateLimiter(opts.rps, opts.burst);
44
+ const quota = new SubjectQuota(opts.subjectDailyQuota);
45
+ // Stateless: the factory runs once per request, so the subject a tool sees is always the
46
+ // one this request's own bearer token verified as. There is no session to bind, reuse, or
47
+ // outlive a token — per-tenant quota attribution follows from re-authenticating every call.
48
+ const mcpHandler = createMcpHandler((ctx) => buildMcpServer(deps, { subject: subjectOf(ctx) }), {
49
+ legacy: 'stateless',
50
+ // This server declares only `tools`, but the SDK routes `subscriptions/listen`
51
+ // regardless and would otherwise hold up to 1024 long-lived SSE streams per handler
52
+ // — unbounded long-lived state of exactly the kind `FEDREG_MAX_SESSIONS` used to cap.
53
+ // `0` refuses every listen request (the limit check is `open.size >= max`).
54
+ maxSubscriptions: 0,
55
+ // Rejections carry attacker-supplied header/body fragments, so cap what reaches stderr.
56
+ onerror: (err) => log.warn('mcp.handler.error', { message: truncate(err.message) }),
57
+ });
58
+ const nodeHandler = toNodeHandler(mcpHandler, {
59
+ onerror: (err) => log.warn('mcp.adapter.error', { message: truncate(err.message) }),
60
+ });
61
+ const server = createServer(async (req, res) => {
62
+ try {
63
+ const ip = (req.socket.remoteAddress ?? 'unknown').replace('::ffff:', '');
64
+ if (!limiter.allow(ip))
65
+ return reply(res, 429, { error: 'rate_limited' });
66
+ // DNS rebinding protection: validate Host header against allowlist when provided.
67
+ if (opts.allowedHosts && opts.allowedHosts.length > 0) {
68
+ const host = String(req.headers.host ?? '').toLowerCase();
69
+ if (!opts.allowedHosts.includes(host)) {
70
+ return reply(res, 421, { error: 'host_not_allowed', host });
71
+ }
72
+ }
73
+ const origin = opts.publicOrigin ?? `http://${req.headers.host ?? `${opts.host}:${opts.port}`}`;
74
+ const url = new URL(req.url ?? '/', origin);
75
+ if (url.pathname === RESOURCE_METADATA_PATH && req.method === 'GET') {
76
+ return reply(res, 200, oauthProtectedResource(opts.auth, `${origin}${MCP_PATH}`));
77
+ }
78
+ if (url.pathname === '/health' && req.method === 'GET') {
79
+ return reply(res, 200, { ok: true, sandbox: deps.sandbox.kind, protocolVersion: MCP_PROTOCOL_VERSION });
80
+ }
81
+ if (url.pathname === MCP_PATH) {
82
+ // Auth (unless --insecure). Runs on every request: with no protocol session there
83
+ // is no other point at which a caller's identity could have been established.
84
+ let subject = 'anonymous';
85
+ let token = '';
86
+ let scopes = [];
87
+ if (!opts.insecure) {
88
+ const auth = req.headers.authorization;
89
+ if (!auth?.startsWith('Bearer ')) {
90
+ res.writeHead(401, {
91
+ 'content-type': 'application/json',
92
+ 'www-authenticate': `Bearer resource_metadata="${origin}${RESOURCE_METADATA_PATH}"`,
93
+ });
94
+ return res.end(JSON.stringify({ error: 'unauthorized' }));
95
+ }
96
+ token = auth.slice(7);
97
+ try {
98
+ const ctx = await verifier.verify(token);
99
+ subject = ctx.subject;
100
+ scopes = grantedScopes(ctx.claims);
101
+ }
102
+ catch (err) {
103
+ res.writeHead(401, {
104
+ 'content-type': 'application/json',
105
+ 'www-authenticate': `Bearer resource_metadata="${origin}${RESOURCE_METADATA_PATH}", error="invalid_token"`,
106
+ });
107
+ return res.end(JSON.stringify({ error: 'invalid_token', detail: err.message }));
108
+ }
109
+ const q = quota.consume(subject);
110
+ if (!q.allowed) {
111
+ return reply(res, 429, { error: 'quota_exceeded', resetAt: new Date(q.resetAt).toISOString() });
112
+ }
113
+ }
114
+ // toNodeHandler forwards `req.auth` to the factory as pass-through authInfo.
115
+ const authInfo = {
116
+ token,
117
+ clientId: subject,
118
+ scopes,
119
+ extra: { [SUBJECT_KEY]: subject },
120
+ };
121
+ req.auth = authInfo;
122
+ await nodeHandler(req, res);
123
+ return;
124
+ }
125
+ reply(res, 404, { error: 'not_found' });
126
+ }
127
+ catch (err) {
128
+ log.error('http.unhandled', { message: err.message, stack: err.stack });
129
+ if (!res.headersSent)
130
+ reply(res, 500, { error: 'internal' });
131
+ }
132
+ });
133
+ await new Promise((resolve) => server.listen(opts.port, opts.host, () => resolve()));
134
+ const addr = server.address();
135
+ const actualPort = typeof addr === 'object' && addr ? addr.port : opts.port;
136
+ log.info('http.listening', {
137
+ host: opts.host, port: actualPort,
138
+ insecure: opts.insecure ?? false,
139
+ sandbox: deps.sandbox.kind,
140
+ auth: opts.auth.provider,
141
+ protocolVersion: MCP_PROTOCOL_VERSION,
142
+ });
143
+ return {
144
+ port: actualPort,
145
+ close: async () => {
146
+ // Handler first: `server.close()` only stops new connections and resolves once every
147
+ // existing one is idle, so an open SSE exchange (kept warm by keepalive frames) would
148
+ // block it forever. Closing the handler aborts in-flight exchanges and releases those
149
+ // sockets, after which the listener can actually drain.
150
+ await mcpHandler.close();
151
+ await new Promise(r => server.close(() => r()));
152
+ },
153
+ };
154
+ }
155
+ function reply(res, status, body) {
156
+ res.writeHead(status, { 'content-type': 'application/json' });
157
+ res.end(JSON.stringify(body));
158
+ }
159
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Token-bucket IP rate limiter. In-memory; one process.
3
+ */
4
+ export declare class IpRateLimiter {
5
+ private readonly rps;
6
+ private readonly burst;
7
+ private readonly buckets;
8
+ constructor(rps: number, burst: number);
9
+ allow(ip: string): boolean;
10
+ }
11
+ //# sourceMappingURL=ipRateLimiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipRateLimiter.d.ts","sourceRoot":"","sources":["../../src/server/ipRateLimiter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,aAAa;IAEZ,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAU,OAAO,CAAC,QAAQ,CAAC,KAAK;IADhE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4D;gBACvD,GAAG,EAAE,MAAM,EAAmB,KAAK,EAAE,MAAM;IAExE,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAc3B"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Token-bucket IP rate limiter. In-memory; one process.
3
+ */
4
+ export class IpRateLimiter {
5
+ rps;
6
+ burst;
7
+ buckets = new Map();
8
+ constructor(rps, burst) {
9
+ this.rps = rps;
10
+ this.burst = burst;
11
+ }
12
+ allow(ip) {
13
+ const now = Date.now();
14
+ const b = this.buckets.get(ip) ?? { tokens: this.burst, updatedAt: now };
15
+ const elapsedSec = (now - b.updatedAt) / 1000;
16
+ b.tokens = Math.min(this.burst, b.tokens + elapsedSec * this.rps);
17
+ b.updatedAt = now;
18
+ if (b.tokens < 1) {
19
+ this.buckets.set(ip, b);
20
+ return false;
21
+ }
22
+ b.tokens -= 1;
23
+ this.buckets.set(ip, b);
24
+ return true;
25
+ }
26
+ }
27
+ //# sourceMappingURL=ipRateLimiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipRateLimiter.js","sourceRoot":"","sources":["../../src/server/ipRateLimiter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,aAAa;IAEK;IAA8B;IAD1C,OAAO,GAAG,IAAI,GAAG,EAAiD,CAAC;IACpF,YAA6B,GAAW,EAAmB,KAAa;QAA3C,QAAG,GAAH,GAAG,CAAQ;QAAmB,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAE5E,KAAK,CAAC,EAAU;QACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;QACzE,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;QAC9C,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QAClE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC;QAClB,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QACd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
+ import { type CatalogDeps } from './toolCatalog.js';
3
+ import type { RequestCtx } from '../tools/execute.js';
4
+ /**
5
+ * The MCP revision this server speaks.
6
+ *
7
+ * Declared here rather than imported: the SDK's exported `LATEST_PROTOCOL_VERSION` and
8
+ * `SUPPORTED_PROTOCOL_VERSIONS` describe only the legacy `initialize` era (`2025-11-25`
9
+ * and older) kept for the backward-compatibility leg. The SDK does hold the modern value
10
+ * as `FIRST_MODERN_PROTOCOL_VERSION`, but that lives in its `core-internal` package — not
11
+ * exported at runtime and absent from every published `.d.mts` — so it is not importable.
12
+ */
13
+ export declare const MCP_PROTOCOL_VERSION = "2026-07-28";
14
+ export declare function buildMcpServer(deps: CatalogDeps, requestCtx?: RequestCtx): McpServer;
15
+ //# sourceMappingURL=mcpServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpServer.d.ts","sourceRoot":"","sources":["../../src/server/mcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuC,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AA2BjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,CAqCpF"}
@@ -0,0 +1,67 @@
1
+ import { McpServer, fromJsonSchema } from '@modelcontextprotocol/server';
2
+ import { buildCatalog } from './toolCatalog.js';
3
+ import { log } from '../util/logger.js';
4
+ /**
5
+ * The MCP revision this server speaks.
6
+ *
7
+ * Declared here rather than imported: the SDK's exported `LATEST_PROTOCOL_VERSION` and
8
+ * `SUPPORTED_PROTOCOL_VERSIONS` describe only the legacy `initialize` era (`2025-11-25`
9
+ * and older) kept for the backward-compatibility leg. The SDK does hold the modern value
10
+ * as `FIRST_MODERN_PROTOCOL_VERSION`, but that lives in its `core-internal` package — not
11
+ * exported at runtime and absent from every published `.d.mts` — so it is not importable.
12
+ */
13
+ export const MCP_PROTOCOL_VERSION = '2026-07-28';
14
+ // The catalog is derived from process configuration (which sources are bound), not from
15
+ // the caller, so every tenant sees byte-identical results and a shared cache is safe.
16
+ const LIST_CACHE_HINT = { ttlMs: 300_000, cacheScope: 'public' };
17
+ /**
18
+ * Converted tool input schemas, cached for the process lifetime.
19
+ *
20
+ * `fromJsonSchema` compiles an Ajv validator, and the SDK's default validator is a module
21
+ * singleton that caches every compiled schema in a plain `Map` keyed by schema *identity*,
22
+ * with no eviction. Since `buildMcpServer` runs per request and `buildCatalog` mints fresh
23
+ * schema literals each time, converting inline would retain ~4 KB per call forever —
24
+ * roughly 12 KB per HTTP request, unbounded. The three tool inputs derive only from
25
+ * module-level Zod constants, so each is converted exactly once here.
26
+ */
27
+ const inputSchemaCache = new Map();
28
+ function cachedInputSchema(name, jsonSchema) {
29
+ let converted = inputSchemaCache.get(name);
30
+ if (!converted) {
31
+ converted = fromJsonSchema(jsonSchema);
32
+ inputSchemaCache.set(name, converted);
33
+ }
34
+ return converted;
35
+ }
36
+ export function buildMcpServer(deps, requestCtx) {
37
+ const server = new McpServer({ name: 'fedreg-mcp-server', version: '2.0.0' }, // x-release-please-version
38
+ {
39
+ capabilities: { tools: {} },
40
+ cacheHints: { 'tools/list': LIST_CACHE_HINT, 'server/discover': LIST_CACHE_HINT },
41
+ });
42
+ for (const tool of buildCatalog(deps, requestCtx)) {
43
+ server.registerTool(tool.name, {
44
+ description: tool.description,
45
+ // The catalog emits JSON Schema; `fromJsonSchema` attaches the SDK's default Ajv
46
+ // validator, so arguments are checked here BEFORE the handler re-parses with Zod.
47
+ // Note `zodToJsonSchema` emits `additionalProperties: false`, so unknown argument
48
+ // keys are now rejected rather than silently stripped by Zod (see CHANGELOG).
49
+ inputSchema: cachedInputSchema(tool.name, tool.inputSchema),
50
+ }, async (args) => {
51
+ try {
52
+ const result = await tool.handler(args ?? {});
53
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
54
+ }
55
+ catch (err) {
56
+ const e = err;
57
+ log.warn('tool.error', { tool: tool.name, name: e.name, message: e.message });
58
+ return {
59
+ isError: true,
60
+ content: [{ type: 'text', text: `${e.name}: ${e.message}` }],
61
+ };
62
+ }
63
+ });
64
+ }
65
+ return server;
66
+ }
67
+ //# sourceMappingURL=mcpServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpServer.js","sourceRoot":"","sources":["../../src/server/mcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAuB,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAoB,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEjD,wFAAwF;AACxF,sFAAsF;AACtF,MAAM,eAAe,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAiB,EAAE,CAAC;AAE1E;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA6C,CAAC;AAE9E,SAAS,iBAAiB,CAAC,IAAY,EAAE,UAAmB;IAC1D,IAAI,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,cAAc,CAAC,UAA4B,CAAC,CAAC;QACzD,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,UAAuB;IACvE,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,2BAA2B;IAC5E;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,UAAU,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE;KAClF,CACF,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC;QAClD,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,IAAI,EACT;YACE,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,iFAAiF;YACjF,kFAAkF;YAClF,kFAAkF;YAClF,8EAA8E;YAC9E,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC;SAC5D,EACD,KAAK,EAAE,IAAa,EAAE,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBAC9C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YACzF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,GAAY,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9E,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;iBACtE,CAAC;YACJ,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CatalogDeps } from './toolCatalog.js';
2
+ export declare function startStdio(deps: CatalogDeps): Promise<void>;
3
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,wBAAsB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE"}
@@ -0,0 +1,16 @@
1
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
2
+ import { buildMcpServer } from './mcpServer.js';
3
+ import { log } from '../util/logger.js';
4
+ export async function startStdio(deps) {
5
+ // stdio is unauthenticated and single-tenant, so there is no subject to scope quotas by.
6
+ //
7
+ // `serveStdio` reports every out-of-band failure (transport start, a throwing factory,
8
+ // message-pump errors) through `onerror` and swallows it otherwise. Without this the
9
+ // process would log `stdio.ready` and then answer every request with -32603 in silence;
10
+ // the v1 `await server.connect(transport)` at least rejected into `bin.fatal`.
11
+ serveStdio(() => buildMcpServer(deps), {
12
+ onerror: (err) => log.error('stdio.error', { name: err.name, message: err.message }),
13
+ });
14
+ log.info('stdio.ready', { sandbox: deps.sandbox.kind });
15
+ }
16
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAiB;IAChD,yFAAyF;IACzF,EAAE;IACF,uFAAuF;IACvF,qFAAqF;IACrF,wFAAwF;IACxF,+EAA+E;IAC/E,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE;QACrC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;KACrF,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type RequestCtx } from '../tools/execute.js';
2
+ import type { Sdk } from '../sdk/bindings.js';
3
+ import type { SandboxRunner } from '../sandbox/types.js';
4
+ import type { Corpus } from '../search/corpus.js';
5
+ import type { SubjectQuota } from '../util/quotas.js';
6
+ export interface ToolDescriptor {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: Record<string, unknown>;
10
+ handler: (args: unknown) => Promise<unknown>;
11
+ }
12
+ export interface CatalogDeps {
13
+ sdk: Sdk;
14
+ sandbox: SandboxRunner;
15
+ corpus: Corpus;
16
+ regsMaxCallsPerExecute: number;
17
+ regsSubjectQuota: SubjectQuota;
18
+ }
19
+ export declare function buildCatalog(deps: CatalogDeps, requestCtx?: RequestCtx): ToolDescriptor[];
20
+ //# sourceMappingURL=toolCatalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolCatalog.d.ts","sourceRoot":"","sources":["../../src/server/toolCatalog.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,YAAY,CAAC;CAChC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,cAAc,EAAE,CA0BzF"}
@@ -0,0 +1,29 @@
1
+ import { SearchApiInput, searchApi } from '../tools/searchApi.js';
2
+ import { DescribeSchemaInput, describeSchema } from '../tools/describeSchema.js';
3
+ import { ExecuteInput, execute } from '../tools/execute.js';
4
+ import { zodToJsonSchema } from './zodToJsonSchema.js';
5
+ export function buildCatalog(deps, requestCtx) {
6
+ const names = deps.sdk.registeredNames.join(', ');
7
+ const namespaces = deps.sdk.registeredNames.map(n => `${n}.*`).join(', ');
8
+ return [
9
+ {
10
+ name: 'search_api',
11
+ description: `BM25 search over the API endpoints and curated field dictionary for all bound sources (${names}). Returns TypeScript signatures and examples for use in execute.`,
12
+ inputSchema: zodToJsonSchema(SearchApiInput),
13
+ handler: async (args) => searchApi(SearchApiInput.parse(args), deps.corpus),
14
+ },
15
+ {
16
+ name: 'describe_schema',
17
+ description: `Look up an endpoint or field by exact dotted id (path) or by namespace prefix. Use to drill into the bound source surfaces (${namespaces}).`,
18
+ inputSchema: zodToJsonSchema(DescribeSchemaInput),
19
+ handler: async (args) => describeSchema(DescribeSchemaInput.parse(args), deps.corpus.entries),
20
+ },
21
+ {
22
+ name: 'execute',
23
+ description: `Run TypeScript inside a sandbox (no net, fs, env, or subprocess). Globals: ${names}. Return the awaited expression as the result.`,
24
+ inputSchema: zodToJsonSchema(ExecuteInput),
25
+ handler: async (args) => execute(ExecuteInput.parse(args), deps, requestCtx),
26
+ },
27
+ ];
28
+ }
29
+ //# sourceMappingURL=toolCatalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolCatalog.js","sourceRoot":"","sources":["../../src/server/toolCatalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,OAAO,EAAmB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAqBvD,MAAM,UAAU,YAAY,CAAC,IAAiB,EAAE,UAAuB;IACrE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1E,OAAO;QACL;YACE,IAAI,EAAE,YAAY;YAClB,WAAW,EACT,0FAA0F,KAAK,mEAAmE;YACpK,WAAW,EAAE,eAAe,CAAC,cAAc,CAAC;YAC5C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC;SAC5E;QACD;YACE,IAAI,EAAE,iBAAiB;YACvB,WAAW,EACT,+HAA+H,UAAU,IAAI;YAC/I,WAAW,EAAE,eAAe,CAAC,mBAAmB,CAAC;YACjD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;SAC9F;QACD;YACE,IAAI,EAAE,SAAS;YACf,WAAW,EACT,8EAA8E,KAAK,gDAAgD;YACrI,WAAW,EAAE,eAAe,CAAC,YAAY,CAAC;YAC1C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC;SAC7E;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { AuthConfig } from './authz.js';
2
+ /**
3
+ * OAuth 2.0 Protected Resource Metadata document per RFC 9728.
4
+ *
5
+ * `resource` is REQUIRED and MUST be the canonical URL of the protected resource
6
+ * (the MCP endpoint, e.g. https://example.com/mcp).
7
+ */
8
+ export declare function oauthProtectedResource(cfg: AuthConfig, resourceUrl: string): {
9
+ resource: string;
10
+ authorization_servers: string[];
11
+ bearer_methods_supported: string[];
12
+ scopes_supported: string[];
13
+ resource_documentation: string;
14
+ resource_name: string;
15
+ };
16
+ //# sourceMappingURL=wellKnown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wellKnown.d.ts","sourceRoot":"","sources":["../../src/server/wellKnown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM;;;;;;;EAS1E"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * OAuth 2.0 Protected Resource Metadata document per RFC 9728.
3
+ *
4
+ * `resource` is REQUIRED and MUST be the canonical URL of the protected resource
5
+ * (the MCP endpoint, e.g. https://example.com/mcp).
6
+ */
7
+ export function oauthProtectedResource(cfg, resourceUrl) {
8
+ return {
9
+ resource: resourceUrl,
10
+ authorization_servers: cfg.issuer ? [cfg.issuer] : [],
11
+ bearer_methods_supported: ['header'],
12
+ scopes_supported: cfg.scopes ?? [],
13
+ resource_documentation: 'https://github.com/blen-labs/fedreg-mcp-server#self-hosting-remote-http',
14
+ resource_name: 'Federal Register, eCFR & regulations.gov MCP Server',
15
+ };
16
+ }
17
+ //# sourceMappingURL=wellKnown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wellKnown.js","sourceRoot":"","sources":["../../src/server/wellKnown.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAe,EAAE,WAAmB;IACzE,OAAO;QACL,QAAQ,EAAE,WAAW;QACrB,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;QACrD,wBAAwB,EAAE,CAAC,QAAQ,CAAC;QACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;QAClC,sBAAsB,EAAE,yEAAyE;QACjG,aAAa,EAAE,qDAAqD;KACrE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Minimal Zod -> JSON Schema converter sufficient for our three tool inputs.
4
+ * Not a general implementation; intentionally tight to the surface we use.
5
+ */
6
+ export declare function zodToJsonSchema(schema: z.ZodTypeAny): Record<string, unknown>;
7
+ //# sourceMappingURL=zodToJsonSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zodToJsonSchema.d.ts","sourceRoot":"","sources":["../../src/server/zodToJsonSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqC7E"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Minimal Zod -> JSON Schema converter sufficient for our three tool inputs.
3
+ * Not a general implementation; intentionally tight to the surface we use.
4
+ */
5
+ export function zodToJsonSchema(schema) {
6
+ const def = schema._def;
7
+ switch (def.typeName) {
8
+ case 'ZodObject': {
9
+ const shape = def.shape();
10
+ const properties = {};
11
+ const required = [];
12
+ for (const [key, value] of Object.entries(shape)) {
13
+ properties[key] = zodToJsonSchema(value);
14
+ if (!value.isOptional?.() && !(value._def?.typeName === 'ZodDefault')) {
15
+ required.push(key);
16
+ }
17
+ }
18
+ const out = { type: 'object', properties };
19
+ if (required.length)
20
+ out.required = required;
21
+ out.additionalProperties = false;
22
+ return out;
23
+ }
24
+ case 'ZodString':
25
+ return { type: 'string', ...(def.description ? { description: def.description } : {}) };
26
+ case 'ZodNumber':
27
+ return { type: 'number', ...(def.description ? { description: def.description } : {}) };
28
+ case 'ZodBoolean':
29
+ return { type: 'boolean' };
30
+ case 'ZodOptional':
31
+ return zodToJsonSchema(def.innerType);
32
+ case 'ZodDefault':
33
+ return { ...zodToJsonSchema(def.innerType), default: def.defaultValue() };
34
+ case 'ZodArray':
35
+ return { type: 'array', items: zodToJsonSchema(def.type) };
36
+ case 'ZodEnum':
37
+ return { type: 'string', enum: def.values };
38
+ case 'ZodEffects':
39
+ return zodToJsonSchema(def.schema);
40
+ default:
41
+ return {};
42
+ }
43
+ }
44
+ //# sourceMappingURL=zodToJsonSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zodToJsonSchema.js","sourceRoot":"","sources":["../../src/server/zodToJsonSchema.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,MAAM,GAAG,GAAI,MAAc,CAAC,IAAI,CAAC;IACjC,QAAQ,GAAG,CAAC,QAAQ,EAAE,CAAC;QACrB,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC;YAC1B,MAAM,UAAU,GAA4B,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAe,KAAK,CAAC,EAAE,CAAC;gBAC/D,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;gBACzC,IAAI,CAAE,KAAa,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,CAAC,EAAE,CAAC;oBAC/E,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,MAAM,GAAG,GAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;YACpE,IAAI,QAAQ,CAAC,MAAM;gBAAE,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC7C,GAAG,CAAC,oBAAoB,GAAG,KAAK,CAAC;YACjC,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK,WAAW;YACd,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1F,KAAK,WAAW;YACd,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1F,KAAK,YAAY;YACf,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7B,KAAK,aAAa;YAChB,OAAO,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,KAAK,YAAY;YACf,OAAO,EAAE,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC;QAC5E,KAAK,UAAU;YACb,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,KAAK,SAAS;YACZ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;QAC9C,KAAK,YAAY;YACf,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { CatalogDeps } from '../server/toolCatalog.js';
2
+ import type { SandboxKind } from '../sandbox/types.js';
3
+ export interface SupervisorConfig {
4
+ frBaseUrl: string;
5
+ ecfrBaseUrl: string;
6
+ regsBaseUrl: string;
7
+ regsApiKey?: string;
8
+ userAgent: string;
9
+ upstreamTimeoutMs: number;
10
+ upstreamRetries: number;
11
+ cacheTtlMs: number;
12
+ cacheMaxItems: number;
13
+ sandbox: SandboxKind;
14
+ regsMaxCallsPerExecute: number;
15
+ regsRatePerHour: number;
16
+ regsSubjectRatePerHour: number;
17
+ }
18
+ export declare function buildSupervisor(cfg: SupervisorConfig): Promise<CatalogDeps>;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/supervisor/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAcjF"}
@@ -0,0 +1,22 @@
1
+ import { buildSdk } from '../sdk/bindings.js';
2
+ import { getSources } from '../sdk/sources/index.js';
3
+ import { buildCorpus } from '../search/corpus.js';
4
+ import { pickSandbox } from '../sandbox/index.js';
5
+ import { TokenBucket } from '../util/tokenBucket.js';
6
+ import { SubjectQuota } from '../util/quotas.js';
7
+ export async function buildSupervisor(cfg) {
8
+ const regsLimiter = new TokenBucket(cfg.regsRatePerHour / 3600, cfg.regsRatePerHour);
9
+ const sourceCfg = {
10
+ frBaseUrl: cfg.frBaseUrl, ecfrBaseUrl: cfg.ecfrBaseUrl, regsBaseUrl: cfg.regsBaseUrl,
11
+ regsApiKey: cfg.regsApiKey, userAgent: cfg.userAgent, timeoutMs: cfg.upstreamTimeoutMs,
12
+ retries: cfg.upstreamRetries, cacheTtlMs: cfg.cacheTtlMs, cacheMaxItems: cfg.cacheMaxItems,
13
+ regsPreflightLimiter: regsLimiter,
14
+ };
15
+ const sources = getSources(sourceCfg);
16
+ const sdk = buildSdk(sourceCfg);
17
+ const corpus = buildCorpus(sources);
18
+ const sandbox = await pickSandbox(cfg.sandbox);
19
+ const regsSubjectQuota = new SubjectQuota(cfg.regsSubjectRatePerHour, 60 * 60 * 1000);
20
+ return { sdk, sandbox, corpus, regsMaxCallsPerExecute: cfg.regsMaxCallsPerExecute, regsSubjectQuota };
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/supervisor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAoBjD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAqB;IACzD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,eAAe,GAAG,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;IACrF,MAAM,SAAS,GAAG;QAChB,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW;QACpF,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,iBAAiB;QACtF,OAAO,EAAE,GAAG,CAAC,eAAe,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa;QAC1F,oBAAoB,EAAE,WAAW;KAClC,CAAC;IACF,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,gBAAgB,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACtF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,GAAG,CAAC,sBAAsB,EAAE,gBAAgB,EAAE,CAAC;AACxG,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ import { type CorpusEntry } from '../search/corpus.js';
3
+ export declare const DescribeSchemaInput: z.ZodEffects<z.ZodObject<{
4
+ path: z.ZodOptional<z.ZodString>;
5
+ prefix: z.ZodOptional<z.ZodString>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ path?: string | undefined;
8
+ prefix?: string | undefined;
9
+ }, {
10
+ path?: string | undefined;
11
+ prefix?: string | undefined;
12
+ }>, {
13
+ path?: string | undefined;
14
+ prefix?: string | undefined;
15
+ }, {
16
+ path?: string | undefined;
17
+ prefix?: string | undefined;
18
+ }>;
19
+ export type DescribeSchemaInputT = z.infer<typeof DescribeSchemaInput>;
20
+ export type DescribeSchemaResult = {
21
+ found: false;
22
+ message: string;
23
+ } | {
24
+ found: true;
25
+ entries: Array<{
26
+ id: string;
27
+ kind: 'endpoint' | 'field';
28
+ binding: string;
29
+ description: string;
30
+ signature?: string;
31
+ example?: string;
32
+ }>;
33
+ };
34
+ export declare function describeSchema(input: DescribeSchemaInputT, entries: Map<string, CorpusEntry>): DescribeSchemaResult;
35
+ //# sourceMappingURL=describeSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describeSchema.d.ts","sourceRoot":"","sources":["../../src/tools/describeSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE7E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAG+E,CAAC;AAChH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAC5B;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC;AAE5J,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,oBAAoB,CAMnH"}
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+ import { lookupByPathOrPrefix } from '../search/corpus.js';
3
+ export const DescribeSchemaInput = z.object({
4
+ path: z.string().optional().describe("Exact dotted id, e.g. 'fr.documents.search' or 'regs.comments.search'"),
5
+ prefix: z.string().optional().describe("Prefix to enumerate, e.g. 'fr.documents' or 'regs.'"),
6
+ }).refine(v => Boolean(v.path) !== Boolean(v.prefix), { message: 'Provide exactly one of `path` or `prefix`' });
7
+ export function describeSchema(input, entries) {
8
+ const hits = lookupByPathOrPrefix(entries, input);
9
+ if (hits.length === 0) {
10
+ return { found: false, message: `No entries matched ${input.path ? `path '${input.path}'` : `prefix '${input.prefix}'`}.` };
11
+ }
12
+ return { found: true, entries: hits.map(e => ({ id: e.id, kind: e.kind, binding: e.binding, description: e.description, signature: e.signature, example: e.example })) };
13
+ }
14
+ //# sourceMappingURL=describeSchema.js.map