@signal4/s4 2.4.0 → 2.6.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 (60) hide show
  1. package/README.md +15 -0
  2. package/bin/signal4-mcp-http.js +2 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +584 -99
  5. package/dist/cli.js.map +1 -1
  6. package/dist/client.d.ts +56 -1
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +108 -12
  9. package/dist/client.js.map +1 -1
  10. package/dist/format.d.ts +67 -18
  11. package/dist/format.d.ts.map +1 -1
  12. package/dist/format.js +637 -358
  13. package/dist/format.js.map +1 -1
  14. package/dist/handler_types.d.ts +4 -0
  15. package/dist/handler_types.d.ts.map +1 -1
  16. package/dist/handler_types.js +8 -1
  17. package/dist/handler_types.js.map +1 -1
  18. package/dist/handlers/ads.d.ts.map +1 -1
  19. package/dist/handlers/ads.js +11 -0
  20. package/dist/handlers/ads.js.map +1 -1
  21. package/dist/handlers/bookmarks.js +4 -4
  22. package/dist/handlers/bookmarks.js.map +1 -1
  23. package/dist/handlers/claims.d.ts.map +1 -1
  24. package/dist/handlers/claims.js +57 -17
  25. package/dist/handlers/claims.js.map +1 -1
  26. package/dist/handlers/explore.d.ts +2 -1
  27. package/dist/handlers/explore.d.ts.map +1 -1
  28. package/dist/handlers/explore.js +64 -10
  29. package/dist/handlers/explore.js.map +1 -1
  30. package/dist/handlers/registry.d.ts.map +1 -1
  31. package/dist/handlers/registry.js +2 -1
  32. package/dist/handlers/registry.js.map +1 -1
  33. package/dist/handlers/reports.d.ts.map +1 -1
  34. package/dist/handlers/reports.js +53 -31
  35. package/dist/handlers/reports.js.map +1 -1
  36. package/dist/handlers/search.d.ts.map +1 -1
  37. package/dist/handlers/search.js +63 -37
  38. package/dist/handlers/search.js.map +1 -1
  39. package/dist/handlers/timeline.d.ts.map +1 -1
  40. package/dist/handlers/timeline.js +12 -1
  41. package/dist/handlers/timeline.js.map +1 -1
  42. package/dist/http.d.ts +16 -0
  43. package/dist/http.d.ts.map +1 -0
  44. package/dist/http.js +132 -0
  45. package/dist/http.js.map +1 -0
  46. package/dist/index.d.ts +3 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +12 -149
  49. package/dist/index.js.map +1 -1
  50. package/dist/provenance.d.ts +41 -0
  51. package/dist/provenance.d.ts.map +1 -0
  52. package/dist/provenance.js +65 -0
  53. package/dist/provenance.js.map +1 -0
  54. package/dist/server_core.d.ts +40 -0
  55. package/dist/server_core.d.ts.map +1 -0
  56. package/dist/server_core.js +174 -0
  57. package/dist/server_core.js.map +1 -0
  58. package/dist/types.d.ts +140 -0
  59. package/dist/types.d.ts.map +1 -1
  60. package/package.json +6 -2
package/dist/http.js ADDED
@@ -0,0 +1,132 @@
1
+ /** Signal4 MCP server — streamable-http entry point.
2
+ *
3
+ * Serves the MCP protocol over HTTP at /mcp. NO auth logic lives here beyond
4
+ * envelope checks: the client's API key (X-API-Key or Authorization: Bearer)
5
+ * is passed through to the backend on every call, and the backend natively
6
+ * validates it, enforces scopes/projects, rate-limits, and records usage.
7
+ *
8
+ * Run with:
9
+ * node dist/http.js --host 127.0.0.1 --port 7998
10
+ *
11
+ * Env:
12
+ * SIGNAL4_API_URL — backend base URL (default https://api.signal4.ca;
13
+ * PM2 sets http://localhost:7999)
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import { createServer } from "node:http";
17
+ import { parseArgs } from "node:util";
18
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
19
+ import { withApiKey } from "./client.js";
20
+ import { buildServer, CachedManifest } from "./server_core.js";
21
+ // ---------------------------------------------------------------------------
22
+ // Per-key manifest cache
23
+ //
24
+ // The backend manifest endpoint scope-filters tools per key, so different
25
+ // keys get different tool lists. Tool authorization = membership in that
26
+ // key's manifest. Keyed by sha256 of the raw key; TTL bounds how long a
27
+ // revoked/rescoped key keeps its old tool list.
28
+ // ---------------------------------------------------------------------------
29
+ const MANIFEST_TTL_MS = 15 * 60 * 1000;
30
+ const MANIFEST_CACHE_MAX = 100;
31
+ const manifestCache = new Map();
32
+ function manifestFor(key) {
33
+ const hash = createHash("sha256").update(key).digest("hex");
34
+ let entry = manifestCache.get(hash);
35
+ if (!entry) {
36
+ entry = new CachedManifest({ ttlMs: MANIFEST_TTL_MS });
37
+ if (manifestCache.size >= MANIFEST_CACHE_MAX) {
38
+ // Evict the least-recently-used entry (Map preserves insertion order;
39
+ // we re-insert on every hit below, so the first key is the LRU).
40
+ const oldest = manifestCache.keys().next().value;
41
+ if (oldest !== undefined)
42
+ manifestCache.delete(oldest);
43
+ }
44
+ }
45
+ else {
46
+ manifestCache.delete(hash); // re-insert to mark as recently used
47
+ }
48
+ manifestCache.set(hash, entry);
49
+ return entry;
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // Key extraction — envelope only, validation belongs to the backend
53
+ // ---------------------------------------------------------------------------
54
+ function extractApiKey(req) {
55
+ const headerKey = req.headers["x-api-key"];
56
+ if (typeof headerKey === "string" && headerKey)
57
+ return headerKey.trim();
58
+ const auth = req.headers["authorization"];
59
+ if (typeof auth === "string" && auth.toLowerCase().startsWith("bearer ")) {
60
+ return auth.slice(7).trim();
61
+ }
62
+ return null;
63
+ }
64
+ function sendJson(res, status, body) {
65
+ res.writeHead(status, { "Content-Type": "application/json" });
66
+ res.end(JSON.stringify(body));
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Request handling — stateless: one server + transport per request
70
+ // ---------------------------------------------------------------------------
71
+ async function handleMcpRequest(req, res) {
72
+ const key = extractApiKey(req);
73
+ if (!key) {
74
+ sendJson(res, 401, {
75
+ detail: "Missing API key. Send it as 'X-API-Key: <key>' or 'Authorization: Bearer <key>'.",
76
+ });
77
+ return;
78
+ }
79
+ if (!key.startsWith("sk_")) {
80
+ sendJson(res, 401, { detail: "Invalid API key format. Signal4 keys start with 'sk_'." });
81
+ return;
82
+ }
83
+ const server = buildServer(manifestFor(key));
84
+ const transport = new StreamableHTTPServerTransport({
85
+ sessionIdGenerator: undefined, // stateless mode
86
+ });
87
+ res.on("close", () => {
88
+ void transport.close();
89
+ void server.close();
90
+ });
91
+ // Everything the MCP handlers do — manifest fetch, tool handler backend
92
+ // calls — runs inside this key's context, so client.ts sends THIS key.
93
+ await withApiKey(key, async () => {
94
+ await server.connect(transport);
95
+ await transport.handleRequest(req, res);
96
+ });
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // HTTP server
100
+ // ---------------------------------------------------------------------------
101
+ const { values: args } = parseArgs({
102
+ options: {
103
+ host: { type: "string", default: "127.0.0.1" },
104
+ port: { type: "string", default: "7998" },
105
+ },
106
+ });
107
+ const httpServer = createServer((req, res) => {
108
+ const path = (req.url ?? "/").split("?")[0];
109
+ if (req.method === "GET" && path === "/health") {
110
+ sendJson(res, 200, { status: "ok", service: "signal4-mcp", transport: "http" });
111
+ return;
112
+ }
113
+ if (path === "/mcp") {
114
+ handleMcpRequest(req, res).catch((err) => {
115
+ console.error("MCP request failed:", err);
116
+ if (!res.headersSent) {
117
+ sendJson(res, 500, { detail: "Internal server error" });
118
+ }
119
+ else {
120
+ res.end();
121
+ }
122
+ });
123
+ return;
124
+ }
125
+ sendJson(res, 404, { detail: "Not found" });
126
+ });
127
+ const port = Number(args.port);
128
+ httpServer.listen(port, args.host, () => {
129
+ console.log(`Signal4 MCP (streamable-http) listening on http://${args.host}:${port}/mcp ` +
130
+ `(backend: ${process.env.SIGNAL4_API_URL ?? "https://api.signal4.ca"})`);
131
+ });
132
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  /** Signal4 MCP server — media intelligence tools for AI agents.
2
2
  *
3
- * Connects to the Signal4 REST API at api.signal4.ca.
3
+ * Stdio entry point. Connects to the Signal4 REST API at api.signal4.ca.
4
4
  * Tool definitions (names, descriptions, schemas) are fetched from the API
5
5
  * at startup — the npm package contains only handler logic, not metadata.
6
+ *
7
+ * For the streamable-http entry point, see http.ts.
6
8
  */
7
9
  export {};
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
package/dist/index.js CHANGED
@@ -1,160 +1,23 @@
1
1
  /** Signal4 MCP server — media intelligence tools for AI agents.
2
2
  *
3
- * Connects to the Signal4 REST API at api.signal4.ca.
3
+ * Stdio entry point. Connects to the Signal4 REST API at api.signal4.ca.
4
4
  * Tool definitions (names, descriptions, schemas) are fetched from the API
5
5
  * at startup — the npm package contains only handler logic, not metadata.
6
+ *
7
+ * For the streamable-http entry point, see http.ts.
6
8
  */
7
- import { createRequire } from "node:module";
8
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
- import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
11
10
  import { applyConfig } from "./config.js";
12
- import * as client from "./client.js";
13
- import { HANDLERS } from "./handlers/registry.js";
11
+ import { buildServer, CachedManifest } from "./server_core.js";
14
12
  applyConfig();
15
- // ---------------------------------------------------------------------------
16
- // Manifest cache — fetched once per session from the API
17
- // ---------------------------------------------------------------------------
18
- let _manifest = null;
19
- let _authError = null;
20
- let _lastFailedAt = 0;
21
- const RETRY_COOLDOWN_MS = 60_000;
22
- async function fetchManifest() {
23
- if (_manifest)
24
- return _manifest;
25
- if (!process.env.SIGNAL4_API_KEY) {
26
- _authError =
27
- "SIGNAL4_API_KEY environment variable is not set. " +
28
- "Get your API key from Signal4 and set it in your MCP server config.";
29
- throw new Error(_authError);
30
- }
31
- // Don't hammer the API with a key that keeps failing
32
- if (_authError && Date.now() - _lastFailedAt < RETRY_COOLDOWN_MS) {
33
- throw new Error(_authError);
34
- }
35
- try {
36
- _manifest = await client.getManifest();
37
- _authError = null;
38
- return _manifest;
39
- }
40
- catch (err) {
41
- const msg = err instanceof Error ? err.message : String(err);
42
- if (/HTTP 40[13]/.test(msg)) {
43
- _authError = msg;
44
- _lastFailedAt = Date.now();
45
- }
46
- throw err;
47
- }
48
- }
49
- const AUTH_DIAGNOSTIC_TOOL = "signal4_connection_status";
50
- function diagnosticToolList() {
51
- const reason = _authError ?? "Unknown authentication error";
52
- return {
53
- tools: [
54
- {
55
- name: AUTH_DIAGNOSTIC_TOOL,
56
- description: `⚠ Signal4 connection failed: ${reason}. ` +
57
- "No tools are available until the API key is fixed. " +
58
- "Check the SIGNAL4_API_KEY in your MCP server config.",
59
- inputSchema: { type: "object", properties: {} },
60
- },
61
- ],
62
- };
63
- }
64
- function diagnosticToolResponse() {
65
- return {
66
- content: [
67
- {
68
- type: "text",
69
- text: `Signal4 connection error: ${_authError}\n\n` +
70
- "To fix this:\n" +
71
- "1. Get a valid API key from Signal4\n" +
72
- "2. Update SIGNAL4_API_KEY in your MCP server config\n" +
73
- "3. Restart the MCP server",
74
- },
75
- ],
76
- isError: true,
77
- };
78
- }
79
- // ---------------------------------------------------------------------------
80
- // Server setup
81
- // ---------------------------------------------------------------------------
82
- const FALLBACK_INSTRUCTIONS = "Signal4 media intelligence platform. Authenticating...";
83
- const { version: PKG_VERSION } = createRequire(import.meta.url)("../package.json");
84
- const server = new McpServer({ name: "Signal4", version: PKG_VERSION }, { instructions: FALLBACK_INSTRUCTIONS });
85
- // Register tool capabilities and custom handlers on the underlying Server
86
- // BEFORE any .tool() call — we own the tool protocol directly so we can
87
- // serve raw JSON Schema from the manifest without zod conversion.
88
- server.server.registerCapabilities({ tools: { listChanged: true } });
89
- server.server.setRequestHandler(ListToolsRequestSchema, async () => {
90
- try {
91
- const m = await fetchManifest();
92
- return {
93
- tools: m.tools.map((t) => ({
94
- name: t.name,
95
- description: t.description,
96
- inputSchema: t.inputSchema,
97
- })),
98
- };
99
- }
100
- catch {
101
- return diagnosticToolList();
102
- }
103
- });
104
- server.server.setRequestHandler(CallToolRequestSchema, async (request) => {
105
- try {
106
- const { name, arguments: args } = request.params;
107
- if (name === AUTH_DIAGNOSTIC_TOOL) {
108
- return diagnosticToolResponse();
109
- }
110
- // Verify the tool is in the manifest (scope enforcement)
111
- const m = await fetchManifest();
112
- const authorized = m.tools.some((t) => t.name === name);
113
- if (!authorized) {
114
- return {
115
- content: [{ type: "text", text: `Tool "${name}" is not available.` }],
116
- isError: true,
117
- };
118
- }
119
- const handler = HANDLERS[name];
120
- if (!handler) {
121
- return {
122
- content: [{ type: "text", text: `Tool "${name}" is not implemented.` }],
123
- isError: true,
124
- };
125
- }
126
- return await handler(args ?? {});
127
- }
128
- catch (err) {
129
- const raw = err instanceof Error ? err.message : String(err);
130
- let msg = raw;
131
- if (/HTTP 40[13]/.test(raw)) {
132
- msg = "API key is invalid or expired. Check the SIGNAL4_API_KEY in your MCP server config.";
133
- }
134
- else if (/HTTP 404/.test(raw)) {
135
- msg = "The requested resource was not found. Check that the ID came from a previous search or listing result.";
136
- }
137
- else if (/HTTP 422/.test(raw)) {
138
- msg = raw + " Check the tool description for valid parameter types and formats.";
139
- }
140
- else if (raw.includes("expand_query must be called before")) {
141
- msg = "Analysis pipeline error — try workflow='simple_rag' or simplify your query.";
142
- }
143
- else if (raw.includes("Input should be less than or equal to")) {
144
- msg = raw + " Check the tool description for valid parameter ranges.";
145
- }
146
- else if (/fetch failed|ECONNREFUSED|ETIMEDOUT/.test(raw)) {
147
- msg = "Could not reach the Signal4 API. The service may be temporarily unavailable — try again in a moment.";
148
- }
149
- return {
150
- content: [{ type: "text", text: `Error: ${msg}` }],
151
- isError: true,
152
- };
153
- }
13
+ // Single global manifest cache keyed by the env key, cached for the session.
14
+ const manifest = new CachedManifest({
15
+ missingKeyMessage: () => process.env.SIGNAL4_API_KEY
16
+ ? null
17
+ : "SIGNAL4_API_KEY environment variable is not set. " +
18
+ "Get your API key from Signal4 and set it in your MCP server config.",
154
19
  });
155
- // ---------------------------------------------------------------------------
156
- // Start
157
- // ---------------------------------------------------------------------------
20
+ const server = buildServer(manifest);
158
21
  async function main() {
159
22
  const transport = new StdioServerTransport();
160
23
  await server.connect(transport);
@@ -162,7 +25,7 @@ async function main() {
162
25
  // If this fails (bad key, network), the ListTools handler will retry
163
26
  // on the next tools/list request.
164
27
  try {
165
- await fetchManifest();
28
+ await manifest.fetch();
166
29
  server.server.sendToolListChanged();
167
30
  }
168
31
  catch {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE/D,WAAW,EAAE,CAAC;AAEd,6EAA6E;AAC7E,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC;IAClC,iBAAiB,EAAE,GAAG,EAAE,CACtB,OAAO,CAAC,GAAG,CAAC,eAAe;QACzB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,mDAAmD;YACnD,qEAAqE;CAC5E,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAErC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,mEAAmE;IACnE,qEAAqE;IACrE,kCAAkC;IAClC,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Window/provenance footers (MCP vocabulary) + speaker empty-state help.
3
+ *
4
+ * format.ts's windowFooter speaks CLI ("--days N"); MCP tools take `days` /
5
+ * `start_date` parameters, so evidence-bearing tools append this variant
6
+ * instead. Every tool that searches a window must SAY what window it searched:
7
+ * thin results inside a default window are indistinguishable from a thin
8
+ * corpus otherwise.
9
+ */
10
+ /** The concrete calendar window a days-based endpoint actually covered. */
11
+ export declare function windowFromDays(days: number): {
12
+ startDate: string;
13
+ endDate: string;
14
+ };
15
+ /**
16
+ * One-line footer stating the date range a tool actually searched, in MCP
17
+ * parameter vocabulary (`days` / `start_date`), matching the style of
18
+ * format.ts's windowFooter.
19
+ */
20
+ export declare function mcpWindowFooter(opts: {
21
+ startDate?: string;
22
+ endDate?: string;
23
+ days?: number;
24
+ /** True when the caller supplied no window and the tool's default applied. */
25
+ defaultWindow?: boolean;
26
+ fullHistory?: boolean;
27
+ /** Max days the endpoint accepts, for the widen hint (default 365). */
28
+ maxDays?: number;
29
+ /** True for endpoints that only take `days` (no start_date parameter). */
30
+ daysOnly?: boolean;
31
+ }): string;
32
+ /** For endpoints with no time filter at all (e.g. entity_claims). */
33
+ export declare function allTimeFooter(): string;
34
+ /**
35
+ * When a speaker-filtered search returns nothing, distinguish "the speaker
36
+ * doesn't exist in the corpus" from "the speaker said nothing matching".
37
+ * Returns a note to prepend, or undefined when the name resolves cleanly
38
+ * (i.e. the emptiness is about the topic/window, not the name).
39
+ */
40
+ export declare function speakerEmptyStateNote(name: string): Promise<string | undefined>;
41
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAKnF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,MAAM,CAUT;AAED,qEAAqE;AACrE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgBrF"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Window/provenance footers (MCP vocabulary) + speaker empty-state help.
3
+ *
4
+ * format.ts's windowFooter speaks CLI ("--days N"); MCP tools take `days` /
5
+ * `start_date` parameters, so evidence-bearing tools append this variant
6
+ * instead. Every tool that searches a window must SAY what window it searched:
7
+ * thin results inside a default window are indistinguishable from a thin
8
+ * corpus otherwise.
9
+ */
10
+ import * as client from "./client.js";
11
+ /** The concrete calendar window a days-based endpoint actually covered. */
12
+ export function windowFromDays(days) {
13
+ return {
14
+ startDate: new Date(Date.now() - days * 86400000).toISOString().slice(0, 10),
15
+ endDate: new Date().toISOString().slice(0, 10),
16
+ };
17
+ }
18
+ /**
19
+ * One-line footer stating the date range a tool actually searched, in MCP
20
+ * parameter vocabulary (`days` / `start_date`), matching the style of
21
+ * format.ts's windowFooter.
22
+ */
23
+ export function mcpWindowFooter(opts) {
24
+ if (opts.fullHistory)
25
+ return "\n_Searched the full corpus history._";
26
+ let { startDate, endDate } = opts;
27
+ if (!startDate && opts.days != null)
28
+ ({ startDate, endDate } = windowFromDays(opts.days));
29
+ const daysStr = opts.days != null ? ` (${opts.days} days)` : "";
30
+ const span = startDate ? `${startDate} → ${endDate ?? "today"}${daysStr}` : `last ${opts.days ?? 30} days`;
31
+ const hint = opts.defaultWindow ? ` (default ${opts.days ?? 30}d window)` : "";
32
+ const max = opts.maxDays ?? 365;
33
+ const widen = opts.daysOnly ? `widen with days (≤${max})` : `widen with days (≤${max}) or start_date YYYY-MM-DD`;
34
+ return `\n_Searched ${span}${hint} — older content may exist; ${widen}._`;
35
+ }
36
+ /** For endpoints with no time filter at all (e.g. entity_claims). */
37
+ export function allTimeFooter() {
38
+ return "\n_Searched the full claim history — this tool has no time window._";
39
+ }
40
+ /**
41
+ * When a speaker-filtered search returns nothing, distinguish "the speaker
42
+ * doesn't exist in the corpus" from "the speaker said nothing matching".
43
+ * Returns a note to prepend, or undefined when the name resolves cleanly
44
+ * (i.e. the emptiness is about the topic/window, not the name).
45
+ */
46
+ export async function speakerEmptyStateNote(name) {
47
+ let matches;
48
+ try {
49
+ matches = await client.searchSpeakers(name, 3);
50
+ }
51
+ catch {
52
+ return undefined; // lookup failure must never mask the real empty state
53
+ }
54
+ const names = matches
55
+ .map((m) => m.name ?? m.primary_name)
56
+ .filter((n) => !!n);
57
+ if (!names.length) {
58
+ return `No speaker matching "${name}" found in the corpus. Check the spelling or use find_speakers to browse.`;
59
+ }
60
+ const exact = names.some((n) => n.toLowerCase() === name.trim().toLowerCase());
61
+ if (exact)
62
+ return undefined;
63
+ return `No speaker matching "${name}" found in the corpus. Closest names: ${names.join(", ")}. Did you mean one of these? (Pass the canonical spelling as speaker.)`;
64
+ }
65
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC5E,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAW/B;IACC,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,uCAAuC,CAAC;IACrE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAClC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI;QAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1F,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,MAAM,OAAO,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,EAAE,OAAO,CAAC;IAC3G,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,qBAAqB,GAAG,GAAG,CAAC,CAAC,CAAC,qBAAqB,GAAG,4BAA4B,CAAC;IACjH,OAAO,eAAe,IAAI,GAAG,IAAI,+BAA+B,KAAK,IAAI,CAAC;AAC5E,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,aAAa;IAC3B,OAAO,qEAAqE,CAAC;AAC/E,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC,CAAC,sDAAsD;IAC1E,CAAC;IACD,MAAM,KAAK,GAAG,OAAO;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC;SACpC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,wBAAwB,IAAI,2EAA2E,CAAC;IACjH,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/E,IAAI,KAAK;QAAE,OAAO,SAAS,CAAC;IAC5B,OAAO,wBAAwB,IAAI,yCAAyC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wEAAwE,CAAC;AACvK,CAAC"}
@@ -0,0 +1,40 @@
1
+ /** Shared MCP server wiring — used by both the stdio entry (index.ts) and the
2
+ * streamable-http entry (http.ts).
3
+ *
4
+ * Tool definitions (names, descriptions, schemas) come from the API's manifest
5
+ * endpoint, which scope-filters tools per key. Tool authorization is therefore
6
+ * simply membership in the caller's manifest. The manifest cache strategy
7
+ * differs per transport (single global for stdio, per-key for HTTP), so the
8
+ * server is parameterized on a ManifestProvider.
9
+ */
10
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
+ import type { ManifestResponse } from "./client.js";
12
+ export declare const RETRY_COOLDOWN_MS = 60000;
13
+ export interface ManifestProvider {
14
+ /** Fetch (or return cached) manifest for the current caller. */
15
+ fetch(): Promise<ManifestResponse>;
16
+ /** Last auth error for the current caller, if any. */
17
+ authError(): string | null;
18
+ }
19
+ /**
20
+ * Manifest cache with the 60s auth-failure cooldown. One instance per key
21
+ * context: index.ts holds a single global one (env key, cached forever, as
22
+ * before); http.ts holds one per key hash with a TTL.
23
+ */
24
+ export declare class CachedManifest implements ManifestProvider {
25
+ private readonly opts;
26
+ private manifest;
27
+ private fetchedAt;
28
+ private _authError;
29
+ private lastFailedAt;
30
+ constructor(opts?: {
31
+ /** Cache TTL in ms; Infinity = cache for process lifetime. */
32
+ ttlMs?: number;
33
+ /** Return an error message if no key is available (stdio env check). */
34
+ missingKeyMessage?: () => string | null;
35
+ });
36
+ authError(): string | null;
37
+ fetch(): Promise<ManifestResponse>;
38
+ }
39
+ export declare function buildServer(provider: ManifestProvider): McpServer;
40
+ //# sourceMappingURL=server_core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server_core.d.ts","sourceRoot":"","sources":["../src/server_core.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnC,sDAAsD;IACtD,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,qBAAa,cAAe,YAAW,gBAAgB;IAOnD,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAK;gBAGN,IAAI,GAAE;QACrB,8DAA8D;QAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,wEAAwE;QACxE,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;KACpC;IAGR,SAAS,IAAI,MAAM,GAAG,IAAI;IAIpB,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC;CA6BzC;AAkDD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CA6EjE"}