@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.
- package/README.md +15 -0
- package/bin/signal4-mcp-http.js +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +584 -99
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +56 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +108 -12
- package/dist/client.js.map +1 -1
- package/dist/format.d.ts +67 -18
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +637 -358
- package/dist/format.js.map +1 -1
- package/dist/handler_types.d.ts +4 -0
- package/dist/handler_types.d.ts.map +1 -1
- package/dist/handler_types.js +8 -1
- package/dist/handler_types.js.map +1 -1
- package/dist/handlers/ads.d.ts.map +1 -1
- package/dist/handlers/ads.js +11 -0
- package/dist/handlers/ads.js.map +1 -1
- package/dist/handlers/bookmarks.js +4 -4
- package/dist/handlers/bookmarks.js.map +1 -1
- package/dist/handlers/claims.d.ts.map +1 -1
- package/dist/handlers/claims.js +57 -17
- package/dist/handlers/claims.js.map +1 -1
- package/dist/handlers/explore.d.ts +2 -1
- package/dist/handlers/explore.d.ts.map +1 -1
- package/dist/handlers/explore.js +64 -10
- package/dist/handlers/explore.js.map +1 -1
- package/dist/handlers/registry.d.ts.map +1 -1
- package/dist/handlers/registry.js +2 -1
- package/dist/handlers/registry.js.map +1 -1
- package/dist/handlers/reports.d.ts.map +1 -1
- package/dist/handlers/reports.js +53 -31
- package/dist/handlers/reports.js.map +1 -1
- package/dist/handlers/search.d.ts.map +1 -1
- package/dist/handlers/search.js +63 -37
- package/dist/handlers/search.js.map +1 -1
- package/dist/handlers/timeline.d.ts.map +1 -1
- package/dist/handlers/timeline.js +12 -1
- package/dist/handlers/timeline.js.map +1 -1
- package/dist/http.d.ts +16 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +132 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -149
- package/dist/index.js.map +1 -1
- package/dist/provenance.d.ts +41 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +65 -0
- package/dist/provenance.js.map +1 -0
- package/dist/server_core.d.ts +40 -0
- package/dist/server_core.d.ts.map +1 -0
- package/dist/server_core.js +174 -0
- package/dist/server_core.js.map +1 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.d.ts.map +1 -1
- 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
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAyB,MAAM,kBAAkB,CAAC;AAEtF,8EAA8E;AAC9E,yBAAyB;AACzB,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,wEAAwE;AACxE,gDAAgD;AAChD,8EAA8E;AAE9E,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACvC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,MAAM,aAAa,GAAG,IAAI,GAAG,EAA0B,CAAC;AAExD,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QACvD,IAAI,aAAa,CAAC,IAAI,IAAI,kBAAkB,EAAE,CAAC;YAC7C,sEAAsE;YACtE,iEAAiE;YACjE,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACjD,IAAI,MAAM,KAAK,SAAS;gBAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,qCAAqC;IACnE,CAAC;IACD,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,oEAAoE;AACpE,8EAA8E;AAE9E,SAAS,aAAa,CAAC,GAAoB;IACzC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IAClE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,mEAAmE;AACnE,8EAA8E;AAE9E,KAAK,UAAU,gBAAgB,CAAC,GAAoB,EAAE,GAAmB;IACvE,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,MAAM,EAAE,kFAAkF;SAC3F,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,wDAAwD,EAAE,CAAC,CAAC;QACzF,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,kBAAkB,EAAE,SAAS,EAAE,iBAAiB;KACjD,CAAC,CAAC;IACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,wEAAwE;IACxE,uEAAuE;IACvE,MAAM,UAAU,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE;QACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE;QAC9C,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE;KAC1C;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC3C,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5C,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/C,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAChF,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACvC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;IACtC,OAAO,CAAC,GAAG,CACT,qDAAqD,IAAI,CAAC,IAAI,IAAI,IAAI,OAAO;QAC7E,aAAa,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,wBAAwB,GAAG,CACxE,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
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
|
|
13
|
-
import { HANDLERS } from "./handlers/registry.js";
|
|
11
|
+
import { buildServer, CachedManifest } from "./server_core.js";
|
|
14
12
|
applyConfig();
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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":"AAAA
|
|
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"}
|