@cyanheads/cyanheads-mcp-server 0.1.1
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/AGENTS.md +367 -0
- package/CLAUDE.md +367 -0
- package/Dockerfile +99 -0
- package/LICENSE +201 -0
- package/README.md +143 -0
- package/changelog/0.1.x/0.1.0.md +12 -0
- package/changelog/0.1.x/0.1.1.md +38 -0
- package/changelog/template.md +127 -0
- package/dist/config/server-config.d.ts +19 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +66 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/describe.tool.d.ts +67 -0
- package/dist/mcp-server/tools/definitions/describe.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/describe.tool.js +192 -0
- package/dist/mcp-server/tools/definitions/describe.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search.tool.d.ts +53 -0
- package/dist/mcp-server/tools/definitions/search.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search.tool.js +123 -0
- package/dist/mcp-server/tools/definitions/search.tool.js.map +1 -0
- package/dist/services/catalog/catalog-service.d.ts +50 -0
- package/dist/services/catalog/catalog-service.d.ts.map +1 -0
- package/dist/services/catalog/catalog-service.js +233 -0
- package/dist/services/catalog/catalog-service.js.map +1 -0
- package/dist/services/catalog/embeddings-runtime.d.ts +21 -0
- package/dist/services/catalog/embeddings-runtime.d.ts.map +1 -0
- package/dist/services/catalog/embeddings-runtime.js +62 -0
- package/dist/services/catalog/embeddings-runtime.js.map +1 -0
- package/dist/services/catalog/remote-catalog-provider.d.ts +20 -0
- package/dist/services/catalog/remote-catalog-provider.d.ts.map +1 -0
- package/dist/services/catalog/remote-catalog-provider.js +84 -0
- package/dist/services/catalog/remote-catalog-provider.js.map +1 -0
- package/dist/services/catalog/snippets.d.ts +16 -0
- package/dist/services/catalog/snippets.d.ts.map +1 -0
- package/dist/services/catalog/snippets.js +51 -0
- package/dist/services/catalog/snippets.js.map +1 -0
- package/dist/services/catalog/types.d.ts +135 -0
- package/dist/services/catalog/types.d.ts.map +1 -0
- package/dist/services/catalog/types.js +8 -0
- package/dist/services/catalog/types.js.map +1 -0
- package/package.json +83 -0
- package/server.json +141 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CatalogService — fleet catalog with semantic search.
|
|
3
|
+
* Loads fleet.json (v2 with baked embeddings) at startup, packs vectors into a
|
|
4
|
+
* Float32Array for cache locality, and serves cosine-similarity search by
|
|
5
|
+
* embedding the query at runtime and dot-producting against the in-memory index.
|
|
6
|
+
* Polls the remote payload on a configurable interval; atomic swap on change.
|
|
7
|
+
* @module services/catalog/catalog-service
|
|
8
|
+
*/
|
|
9
|
+
import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
10
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
11
|
+
import { TransformersEmbeddingsRuntime } from './embeddings-runtime.js';
|
|
12
|
+
import { RemoteJsonCatalogProvider } from './remote-catalog-provider.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// CatalogService
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export class CatalogService {
|
|
17
|
+
_index = null;
|
|
18
|
+
_provider;
|
|
19
|
+
_embeddings;
|
|
20
|
+
_config;
|
|
21
|
+
_refreshTimer = null;
|
|
22
|
+
constructor(config, embeddings) {
|
|
23
|
+
this._config = config;
|
|
24
|
+
this._provider = new RemoteJsonCatalogProvider(config);
|
|
25
|
+
this._embeddings = embeddings ?? new TransformersEmbeddingsRuntime(config.embeddingModelId);
|
|
26
|
+
}
|
|
27
|
+
async initialize() {
|
|
28
|
+
logger.info(`Loading fleet catalog from ${this._config.catalogUrl}`);
|
|
29
|
+
const payload = await this._provider.load();
|
|
30
|
+
this._assertModelMatch(payload);
|
|
31
|
+
this._index = this._buildIndex(payload);
|
|
32
|
+
await this._embeddings.initialize();
|
|
33
|
+
logger.info(`Fleet catalog loaded: ${payload.servers.length} servers, ${this._index.toolByName.size} tools ` +
|
|
34
|
+
`(model=${payload.embeddingModel}, dims=${payload.embeddingDims}, generatedAt=${payload.generatedAt})`);
|
|
35
|
+
if (this._config.catalogRefreshSeconds > 0) {
|
|
36
|
+
this._refreshTimer = setInterval(() => {
|
|
37
|
+
void this._maybeRefresh();
|
|
38
|
+
}, this._config.catalogRefreshSeconds * 1000);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async search(params) {
|
|
42
|
+
const index = this._assertInitialized();
|
|
43
|
+
const queryVec = await this._embeddings.embedQuery(params.query, index.dims, index.payload.embeddingQueryPrefix);
|
|
44
|
+
const floor = this._config.similarityFloor;
|
|
45
|
+
const results = [];
|
|
46
|
+
if (params.scope === 'servers') {
|
|
47
|
+
for (const [i, server] of index.payload.servers.entries()) {
|
|
48
|
+
if (params.category && server.category !== params.category)
|
|
49
|
+
continue;
|
|
50
|
+
const score = dotRow(queryVec, index.serverVectors, i, index.dims);
|
|
51
|
+
if (score < floor)
|
|
52
|
+
continue;
|
|
53
|
+
results.push({
|
|
54
|
+
name: server.name,
|
|
55
|
+
server: server.name,
|
|
56
|
+
brief: server.description,
|
|
57
|
+
category: server.category,
|
|
58
|
+
score,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
let toolI = 0;
|
|
64
|
+
for (const server of index.payload.servers) {
|
|
65
|
+
for (const tool of server.tools) {
|
|
66
|
+
if (params.category && server.category !== params.category) {
|
|
67
|
+
toolI++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const score = dotRow(queryVec, index.toolVectors, toolI, index.dims);
|
|
71
|
+
toolI++;
|
|
72
|
+
if (score < floor)
|
|
73
|
+
continue;
|
|
74
|
+
results.push({
|
|
75
|
+
name: tool.name,
|
|
76
|
+
server: server.name,
|
|
77
|
+
brief: tool.description,
|
|
78
|
+
category: server.category,
|
|
79
|
+
score,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
results.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
85
|
+
return params.limit != null ? results.slice(0, params.limit) : results;
|
|
86
|
+
}
|
|
87
|
+
getTool(name) {
|
|
88
|
+
const index = this._assertInitialized();
|
|
89
|
+
return index.toolByName.get(name) ?? null;
|
|
90
|
+
}
|
|
91
|
+
getServer(name) {
|
|
92
|
+
const index = this._assertInitialized();
|
|
93
|
+
return index.serverByName.get(name) ?? null;
|
|
94
|
+
}
|
|
95
|
+
listCategories() {
|
|
96
|
+
const index = this._assertInitialized();
|
|
97
|
+
const cats = new Set();
|
|
98
|
+
for (const server of index.payload.servers)
|
|
99
|
+
cats.add(server.category);
|
|
100
|
+
return Array.from(cats).sort();
|
|
101
|
+
}
|
|
102
|
+
stats() {
|
|
103
|
+
const index = this._assertInitialized();
|
|
104
|
+
return {
|
|
105
|
+
toolCount: index.toolByName.size,
|
|
106
|
+
serverCount: index.payload.servers.length,
|
|
107
|
+
initializedAt: index.initializedAt,
|
|
108
|
+
embeddingModel: index.payload.embeddingModel,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Stop the background refresh timer (test-only / shutdown). */
|
|
112
|
+
shutdown() {
|
|
113
|
+
if (this._refreshTimer) {
|
|
114
|
+
clearInterval(this._refreshTimer);
|
|
115
|
+
this._refreshTimer = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
// Internals
|
|
120
|
+
// -------------------------------------------------------------------------
|
|
121
|
+
_assertModelMatch(payload) {
|
|
122
|
+
if (payload.embeddingModel !== this._config.embeddingModelId) {
|
|
123
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Catalog embedding model mismatch: payload declares "${payload.embeddingModel}" but server expects "${this._config.embeddingModelId}". ` +
|
|
124
|
+
'Update EMBEDDING_MODEL_ID or regenerate the catalog with the matching model.', {
|
|
125
|
+
payloadModel: payload.embeddingModel,
|
|
126
|
+
expected: this._config.embeddingModelId,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
_buildIndex(payload) {
|
|
131
|
+
const dims = payload.embeddingDims;
|
|
132
|
+
const serverCount = payload.servers.length;
|
|
133
|
+
let toolCount = 0;
|
|
134
|
+
for (const s of payload.servers)
|
|
135
|
+
toolCount += s.tools.length;
|
|
136
|
+
const serverVectors = new Float32Array(serverCount * dims);
|
|
137
|
+
const toolVectors = new Float32Array(toolCount * dims);
|
|
138
|
+
const toolByName = new Map();
|
|
139
|
+
const serverByName = new Map();
|
|
140
|
+
const toolNamesInOrder = [];
|
|
141
|
+
let toolI = 0;
|
|
142
|
+
for (const [si, server] of payload.servers.entries()) {
|
|
143
|
+
if (server.name.includes('_')) {
|
|
144
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Server name "${server.name}" contains underscores. Fleet naming convention requires hyphenated server names.`);
|
|
145
|
+
}
|
|
146
|
+
if (server.embedding.length !== dims) {
|
|
147
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Server "${server.name}" embedding has ${server.embedding.length} dims; expected ${dims}.`);
|
|
148
|
+
}
|
|
149
|
+
serverVectors.set(server.embedding, si * dims);
|
|
150
|
+
serverByName.set(server.name, server);
|
|
151
|
+
for (const tool of server.tools) {
|
|
152
|
+
if (tool.embedding.length !== dims) {
|
|
153
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Tool "${tool.name}" embedding has ${tool.embedding.length} dims; expected ${dims}.`);
|
|
154
|
+
}
|
|
155
|
+
toolVectors.set(tool.embedding, toolI * dims);
|
|
156
|
+
toolByName.set(tool.name, { ...tool, serverRecord: server });
|
|
157
|
+
toolNamesInOrder.push(tool.name);
|
|
158
|
+
toolI++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
payload,
|
|
163
|
+
serverVectors,
|
|
164
|
+
toolVectors,
|
|
165
|
+
toolByName,
|
|
166
|
+
serverByName,
|
|
167
|
+
toolNamesInOrder,
|
|
168
|
+
dims,
|
|
169
|
+
initializedAt: new Date().toISOString(),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async _maybeRefresh() {
|
|
173
|
+
try {
|
|
174
|
+
const current = this._index;
|
|
175
|
+
if (!current)
|
|
176
|
+
return;
|
|
177
|
+
const payload = await this._provider.load();
|
|
178
|
+
if (payload.generatedAt === current.payload.generatedAt) {
|
|
179
|
+
logger.debug(`Catalog refresh: no change (generatedAt=${payload.generatedAt})`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (payload.embeddingModel !== this._config.embeddingModelId) {
|
|
183
|
+
logger.warning(`Catalog refresh skipped: payload claims model "${payload.embeddingModel}" but server expects "${this._config.embeddingModelId}". Keeping last-known state.`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const next = this._buildIndex(payload);
|
|
187
|
+
this._index = next;
|
|
188
|
+
logger.info(`Catalog refreshed: ${payload.servers.length} servers, ${next.toolByName.size} tools (generatedAt=${payload.generatedAt})`);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
logger.warning(`Catalog refresh failed; keeping last-known state. ${err instanceof Error ? err.message : String(err)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
_assertInitialized() {
|
|
195
|
+
if (!this._index) {
|
|
196
|
+
throw serviceUnavailable('CatalogService not initialized — call initialize() in setup()', {
|
|
197
|
+
reason: 'catalog_empty',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return this._index;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Dot product helper
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
function dotRow(query, store, rowIdx, dims) {
|
|
207
|
+
const offset = rowIdx * dims;
|
|
208
|
+
let sum = 0;
|
|
209
|
+
for (let i = 0; i < dims; i++) {
|
|
210
|
+
// biome-ignore lint/style/noNonNullAssertion: bounds enforced by loop + Float32Array sizing
|
|
211
|
+
sum += query[i] * store[offset + i];
|
|
212
|
+
}
|
|
213
|
+
return sum;
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Init / accessor
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
let _service;
|
|
219
|
+
export function initCatalogService(config, embeddings) {
|
|
220
|
+
_service = new CatalogService(config, embeddings);
|
|
221
|
+
}
|
|
222
|
+
export function getCatalogService() {
|
|
223
|
+
if (!_service) {
|
|
224
|
+
throw serviceUnavailable('CatalogService not initialized — call initCatalogService() in setup()', { reason: 'catalog_empty' });
|
|
225
|
+
}
|
|
226
|
+
return _service;
|
|
227
|
+
}
|
|
228
|
+
/** Test-only reset. */
|
|
229
|
+
export function resetCatalogServiceForTests() {
|
|
230
|
+
_service?.shutdown();
|
|
231
|
+
_service = undefined;
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=catalog-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-service.js","sourceRoot":"","sources":["../../../src/services/catalog/catalog-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Embeddings runtime — wraps @huggingface/transformers's
|
|
3
|
+
* feature-extraction pipeline to embed query strings at search time.
|
|
4
|
+
* Loaded once at startup, kept warm for the lifetime of the process.
|
|
5
|
+
* Applies the asymmetric query prefix declared in the catalog payload and
|
|
6
|
+
* Matryoshka-truncates + L2-normalizes the output to match document vectors.
|
|
7
|
+
* @module services/catalog/embeddings-runtime
|
|
8
|
+
*/
|
|
9
|
+
export interface IEmbeddingsRuntime {
|
|
10
|
+
embedQuery(text: string, dims: number, queryPrefix: string): Promise<Float32Array>;
|
|
11
|
+
initialize(): Promise<void>;
|
|
12
|
+
readonly modelId: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class TransformersEmbeddingsRuntime implements IEmbeddingsRuntime {
|
|
15
|
+
readonly modelId: string;
|
|
16
|
+
private _extractor;
|
|
17
|
+
constructor(modelId: string);
|
|
18
|
+
initialize(): Promise<void>;
|
|
19
|
+
embedQuery(text: string, dims: number, queryPrefix: string): Promise<Float32Array>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=embeddings-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings-runtime.d.ts","sourceRoot":"","sources":["../../../src/services/catalog/embeddings-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACnF,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,6BAA8B,YAAW,kBAAkB;aAG1C,OAAO,EAAE,MAAM;IAF3C,OAAO,CAAC,UAAU,CAA0C;gBAEhC,OAAO,EAAE,MAAM;IAErC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAmCzF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Embeddings runtime — wraps @huggingface/transformers's
|
|
3
|
+
* feature-extraction pipeline to embed query strings at search time.
|
|
4
|
+
* Loaded once at startup, kept warm for the lifetime of the process.
|
|
5
|
+
* Applies the asymmetric query prefix declared in the catalog payload and
|
|
6
|
+
* Matryoshka-truncates + L2-normalizes the output to match document vectors.
|
|
7
|
+
* @module services/catalog/embeddings-runtime
|
|
8
|
+
*/
|
|
9
|
+
import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
|
|
10
|
+
import { logger } from '@cyanheads/mcp-ts-core/utils';
|
|
11
|
+
import { pipeline } from '@huggingface/transformers';
|
|
12
|
+
export class TransformersEmbeddingsRuntime {
|
|
13
|
+
modelId;
|
|
14
|
+
_extractor = null;
|
|
15
|
+
constructor(modelId) {
|
|
16
|
+
this.modelId = modelId;
|
|
17
|
+
}
|
|
18
|
+
async initialize() {
|
|
19
|
+
if (this._extractor)
|
|
20
|
+
return;
|
|
21
|
+
logger.info(`Loading embedding model: ${this.modelId}`);
|
|
22
|
+
const startMs = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
this._extractor = (await pipeline('feature-extraction', this.modelId));
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Failed to load embedding model ${this.modelId}: ${err instanceof Error ? err.message : String(err)}`, { modelId: this.modelId }, { cause: err });
|
|
28
|
+
}
|
|
29
|
+
logger.info(`Embedding model loaded in ${((Date.now() - startMs) / 1000).toFixed(1)}s`);
|
|
30
|
+
}
|
|
31
|
+
async embedQuery(text, dims, queryPrefix) {
|
|
32
|
+
if (!this._extractor) {
|
|
33
|
+
throw new McpError(JsonRpcErrorCode.InternalError, 'Embeddings runtime not initialized — call initialize() first.');
|
|
34
|
+
}
|
|
35
|
+
const input = `${queryPrefix}${text}`;
|
|
36
|
+
const tensor = await this._extractor([input], {
|
|
37
|
+
pooling: 'cls',
|
|
38
|
+
normalize: false,
|
|
39
|
+
});
|
|
40
|
+
const rows = tensor.tolist();
|
|
41
|
+
const fullVec = rows[0];
|
|
42
|
+
if (!fullVec || fullVec.length < dims) {
|
|
43
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Embedding pipeline returned a vector of length ${fullVec?.length ?? 0}; expected at least ${dims}.`);
|
|
44
|
+
}
|
|
45
|
+
const out = new Float32Array(dims);
|
|
46
|
+
let sumSq = 0;
|
|
47
|
+
for (let i = 0; i < dims; i++) {
|
|
48
|
+
// biome-ignore lint/style/noNonNullAssertion: length checked above
|
|
49
|
+
const v = fullVec[i];
|
|
50
|
+
out[i] = v;
|
|
51
|
+
sumSq += v * v;
|
|
52
|
+
}
|
|
53
|
+
if (sumSq === 0)
|
|
54
|
+
return out;
|
|
55
|
+
const norm = Math.sqrt(sumSq);
|
|
56
|
+
// biome-ignore lint/style/noNonNullAssertion: Float32Array allocated with exact dims
|
|
57
|
+
for (let i = 0; i < dims; i++)
|
|
58
|
+
out[i] = out[i] / norm;
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=embeddings-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings-runtime.js","sourceRoot":"","sources":["../../../src/services/catalog/embeddings-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAkC,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAQrF,MAAM,OAAO,6BAA6B;IAGZ;IAFpB,UAAU,GAAqC,IAAI,CAAC;IAE5D,YAA4B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAE/C,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,MAAM,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,CAAC,MAAM,QAAQ,CAC/B,oBAAoB,EACpB,IAAI,CAAC,OAAO,CACb,CAA8B,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,QAAQ,CAChB,gBAAgB,CAAC,aAAa,EAC9B,kCAAkC,IAAI,CAAC,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACrG,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EACzB,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1F,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,IAAY,EAAE,WAAmB;QAC9D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,QAAQ,CAChB,gBAAgB,CAAC,aAAa,EAC9B,+DAA+D,CAChE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,WAAW,GAAG,IAAI,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,EAAE;YAC5C,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAgB,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;YACtC,MAAM,IAAI,QAAQ,CAChB,gBAAgB,CAAC,aAAa,EAC9B,kDAAkD,OAAO,EAAE,MAAM,IAAI,CAAC,uBAAuB,IAAI,GAAG,CACrG,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9B,mEAAmE;YACnE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;YACtB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACX,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;QACD,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,qFAAqF;QACrF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC;QACvD,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview RemoteJsonCatalogProvider — fetches and validates the fleet.json
|
|
3
|
+
* payload (schema v2, with baked embeddings) from a configurable URL. Caches the
|
|
4
|
+
* validated result in memory after a successful load.
|
|
5
|
+
* @module services/catalog/remote-catalog-provider
|
|
6
|
+
*/
|
|
7
|
+
import type { ServerConfig } from '../../config/server-config.js';
|
|
8
|
+
import type { FleetPayload } from './types.js';
|
|
9
|
+
export declare class RemoteJsonCatalogProvider {
|
|
10
|
+
private readonly _url;
|
|
11
|
+
private readonly _timeoutMs;
|
|
12
|
+
constructor(config: Pick<ServerConfig, 'catalogUrl' | 'catalogFetchTimeoutMs'>);
|
|
13
|
+
/**
|
|
14
|
+
* Fetch and validate the remote fleet.json payload.
|
|
15
|
+
* Merges the provided signal (if any) with the configured timeout signal.
|
|
16
|
+
* Throws McpError(InternalError) on any failure.
|
|
17
|
+
*/
|
|
18
|
+
load(signal?: AbortSignal): Promise<FleetPayload>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=remote-catalog-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-catalog-provider.d.ts","sourceRoot":"","sources":["../../../src/services/catalog/remote-catalog-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAyC/C,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,uBAAuB,CAAC;IAK9E;;;;OAIG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAoDxD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview RemoteJsonCatalogProvider — fetches and validates the fleet.json
|
|
3
|
+
* payload (schema v2, with baked embeddings) from a configurable URL. Caches the
|
|
4
|
+
* validated result in memory after a successful load.
|
|
5
|
+
* @module services/catalog/remote-catalog-provider
|
|
6
|
+
*/
|
|
7
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
8
|
+
import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Validation schema (v2)
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const EmbeddingSchema = z.array(z.number());
|
|
13
|
+
const CatalogToolSchema = z.object({
|
|
14
|
+
name: z.string(),
|
|
15
|
+
description: z.string(),
|
|
16
|
+
embedding: EmbeddingSchema,
|
|
17
|
+
});
|
|
18
|
+
const CatalogRecordSchema = z.object({
|
|
19
|
+
name: z.string(),
|
|
20
|
+
displayName: z.string(),
|
|
21
|
+
description: z.string(),
|
|
22
|
+
category: z.enum(['research', 'government', 'public-data', 'utility']),
|
|
23
|
+
endpoint: z.string(),
|
|
24
|
+
npm: z.string(),
|
|
25
|
+
github: z.string(),
|
|
26
|
+
version: z.string(),
|
|
27
|
+
auth: z.string(),
|
|
28
|
+
embedding: EmbeddingSchema,
|
|
29
|
+
tools: z.array(CatalogToolSchema),
|
|
30
|
+
});
|
|
31
|
+
const FleetPayloadSchema = z.object({
|
|
32
|
+
version: z.literal('2'),
|
|
33
|
+
generatedAt: z.string(),
|
|
34
|
+
embeddingModel: z.string(),
|
|
35
|
+
embeddingDims: z.number().int().positive(),
|
|
36
|
+
embeddingQueryPrefix: z.string(),
|
|
37
|
+
servers: z.array(CatalogRecordSchema),
|
|
38
|
+
});
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Provider
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
export class RemoteJsonCatalogProvider {
|
|
43
|
+
_url;
|
|
44
|
+
_timeoutMs;
|
|
45
|
+
constructor(config) {
|
|
46
|
+
this._url = config.catalogUrl;
|
|
47
|
+
this._timeoutMs = config.catalogFetchTimeoutMs;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Fetch and validate the remote fleet.json payload.
|
|
51
|
+
* Merges the provided signal (if any) with the configured timeout signal.
|
|
52
|
+
* Throws McpError(InternalError) on any failure.
|
|
53
|
+
*/
|
|
54
|
+
async load(signal) {
|
|
55
|
+
const timeoutSignal = AbortSignal.timeout(this._timeoutMs);
|
|
56
|
+
const combinedSignal = signal != null ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
57
|
+
let response;
|
|
58
|
+
try {
|
|
59
|
+
response = await fetch(this._url, { signal: combinedSignal });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Catalog fetch failed: ${err instanceof Error ? err.message : String(err)}`, { url: this._url }, { cause: err });
|
|
63
|
+
}
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Catalog fetch returned ${response.status} ${response.statusText}`, { url: this._url, status: response.status, statusText: response.statusText });
|
|
66
|
+
}
|
|
67
|
+
let raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = await response.json();
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Catalog response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, { url: this._url }, { cause: err });
|
|
73
|
+
}
|
|
74
|
+
const parsed = FleetPayloadSchema.safeParse(raw);
|
|
75
|
+
if (!parsed.success) {
|
|
76
|
+
const first = parsed.error.issues[0];
|
|
77
|
+
const path = first?.path.join('.') ?? '(unknown)';
|
|
78
|
+
const msg = first?.message ?? 'validation failed';
|
|
79
|
+
throw new McpError(JsonRpcErrorCode.InternalError, `Catalog payload failed validation at ${path}: ${msg}`, { url: this._url, issues: parsed.error.issues.slice(0, 5) });
|
|
80
|
+
}
|
|
81
|
+
return parsed.data;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=remote-catalog-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-catalog-provider.js","sourceRoot":"","sources":["../../../src/services/catalog/remote-catalog-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Per-client install snippet factory registry.
|
|
3
|
+
* A plain Record mapping ClientId to a factory function that produces an InstallSnippet
|
|
4
|
+
* from a CatalogRecord. Snippets are derived at request time from the record's
|
|
5
|
+
* { name, npm, endpoint } fields — no precomputed snippet list lives in the catalog JSON.
|
|
6
|
+
* @module services/catalog/snippets
|
|
7
|
+
*/
|
|
8
|
+
import type { CatalogRecord, ClientId, InstallSnippet } from './types.js';
|
|
9
|
+
/** Factory signature: given a CatalogRecord, produce one InstallSnippet. */
|
|
10
|
+
type SnippetFactory = (record: CatalogRecord) => InstallSnippet;
|
|
11
|
+
/** Registry of all supported clients. */
|
|
12
|
+
export declare const SNIPPET_REGISTRY: Record<ClientId, SnippetFactory>;
|
|
13
|
+
/** Generate install snippets for all supported clients for a given record. */
|
|
14
|
+
export declare function buildAllSnippets(record: CatalogRecord): InstallSnippet[];
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=snippets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippets.d.ts","sourceRoot":"","sources":["../../../src/services/catalog/snippets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1E,4EAA4E;AAC5E,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,cAAc,CAAC;AA8ChE,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,cAAc,CAK7D,CAAC;AAEF,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,EAAE,CAExE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Per-client install snippet factory registry.
|
|
3
|
+
* A plain Record mapping ClientId to a factory function that produces an InstallSnippet
|
|
4
|
+
* from a CatalogRecord. Snippets are derived at request time from the record's
|
|
5
|
+
* { name, npm, endpoint } fields — no precomputed snippet list lives in the catalog JSON.
|
|
6
|
+
* @module services/catalog/snippets
|
|
7
|
+
*/
|
|
8
|
+
/** Build the Claude Desktop JSON fragment. All catalog servers have a hosted endpoint. */
|
|
9
|
+
function claudeDesktopSnippet(record) {
|
|
10
|
+
return {
|
|
11
|
+
client: 'claude-desktop',
|
|
12
|
+
label: 'Claude Desktop (JSON config)',
|
|
13
|
+
payload: JSON.stringify({ [record.name]: { type: 'sse', url: record.endpoint } }, null, 2),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Build the Claude Code CLI command. */
|
|
17
|
+
function claudeCodeSnippet(record) {
|
|
18
|
+
return {
|
|
19
|
+
client: 'claude-code',
|
|
20
|
+
label: 'Claude Code (CLI)',
|
|
21
|
+
payload: `claude mcp add --transport sse ${record.name} ${record.endpoint}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Build the Cursor .cursor/mcp.json fragment. */
|
|
25
|
+
function cursorSnippet(record) {
|
|
26
|
+
return {
|
|
27
|
+
client: 'cursor',
|
|
28
|
+
label: 'Cursor (mcp.json)',
|
|
29
|
+
payload: JSON.stringify({ mcpServers: { [record.name]: { type: 'sse', url: record.endpoint } } }, null, 2),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Build the Cline VS Code extension MCP settings fragment. */
|
|
33
|
+
function clineSnippet(record) {
|
|
34
|
+
return {
|
|
35
|
+
client: 'cline',
|
|
36
|
+
label: 'Cline (VS Code)',
|
|
37
|
+
payload: JSON.stringify({ [record.name]: { type: 'sse', url: record.endpoint, disabled: false, autoApprove: [] } }, null, 2),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Registry of all supported clients. */
|
|
41
|
+
export const SNIPPET_REGISTRY = {
|
|
42
|
+
'claude-desktop': claudeDesktopSnippet,
|
|
43
|
+
'claude-code': claudeCodeSnippet,
|
|
44
|
+
cursor: cursorSnippet,
|
|
45
|
+
cline: clineSnippet,
|
|
46
|
+
};
|
|
47
|
+
/** Generate install snippets for all supported clients for a given record. */
|
|
48
|
+
export function buildAllSnippets(record) {
|
|
49
|
+
return Object.values(SNIPPET_REGISTRY).map((factory) => factory(record));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=snippets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippets.js","sourceRoot":"","sources":["../../../src/services/catalog/snippets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,0FAA0F;AAC1F,SAAS,oBAAoB,CAAC,MAAqB;IACjD,OAAO;QACL,MAAM,EAAE,gBAAgB;QACxB,KAAK,EAAE,8BAA8B;QACrC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;KAC3F,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,SAAS,iBAAiB,CAAC,MAAqB;IAC9C,OAAO;QACL,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,mBAAmB;QAC1B,OAAO,EAAE,kCAAkC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,EAAE;KAC5E,CAAC;AACJ,CAAC;AAED,kDAAkD;AAClD,SAAS,aAAa,CAAC,MAAqB;IAC1C,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,mBAAmB;QAC1B,OAAO,EAAE,IAAI,CAAC,SAAS,CACrB,EAAE,UAAU,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,EACxE,IAAI,EACJ,CAAC,CACF;KACF,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,SAAS,YAAY,CAAC,MAAqB;IACzC,OAAO;QACL,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,iBAAiB;QACxB,OAAO,EAAE,IAAI,CAAC,SAAS,CACrB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,EAC1F,IAAI,EACJ,CAAC,CACF;KACF,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAqC;IAChE,gBAAgB,EAAE,oBAAoB;IACtC,aAAa,EAAE,iBAAiB;IAChC,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,YAAY;CACpB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,MAAqB;IACpD,OAAO,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3E,CAAC"}
|