@aris-mcp/server 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +73 -640
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -5,496 +5,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { z as z2 } from "zod";
|
|
7
7
|
|
|
8
|
-
// ../core/dist/redact.js
|
|
9
|
-
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
10
|
-
"ssn",
|
|
11
|
-
"password",
|
|
12
|
-
"api_key",
|
|
13
|
-
"email",
|
|
14
|
-
"credit",
|
|
15
|
-
"secret",
|
|
16
|
-
"token",
|
|
17
|
-
"authorization"
|
|
18
|
-
]);
|
|
19
|
-
var EMAIL_REGEX = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
|
20
|
-
var SSN_REGEX = /\b\d{3}-\d{2}-\d{4}\b/g;
|
|
21
|
-
var PHONE_REGEX = /\b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g;
|
|
22
|
-
function isSensitiveKey(key) {
|
|
23
|
-
const lower = key.toLowerCase();
|
|
24
|
-
for (const sensitive of SENSITIVE_KEYS) {
|
|
25
|
-
if (lower.includes(sensitive)) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
function redactStringValue(value) {
|
|
32
|
-
let result = value;
|
|
33
|
-
result = result.replace(EMAIL_REGEX, "[REDACTED_EMAIL]");
|
|
34
|
-
result = result.replace(SSN_REGEX, "[REDACTED_SSN]");
|
|
35
|
-
result = result.replace(PHONE_REGEX, "[REDACTED_PHONE]");
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
function walkAndRedact(obj) {
|
|
39
|
-
if (obj === null || obj === void 0) {
|
|
40
|
-
return obj;
|
|
41
|
-
}
|
|
42
|
-
if (Array.isArray(obj)) {
|
|
43
|
-
return obj.map((item) => walkAndRedact(item));
|
|
44
|
-
}
|
|
45
|
-
if (typeof obj === "object") {
|
|
46
|
-
const result = {};
|
|
47
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
48
|
-
if (isSensitiveKey(key)) {
|
|
49
|
-
result[key] = "[REDACTED]";
|
|
50
|
-
} else if (typeof value === "string") {
|
|
51
|
-
result[key] = redactStringValue(value);
|
|
52
|
-
} else {
|
|
53
|
-
result[key] = walkAndRedact(value);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
if (typeof obj === "string") {
|
|
59
|
-
return redactStringValue(obj);
|
|
60
|
-
}
|
|
61
|
-
return obj;
|
|
62
|
-
}
|
|
63
|
-
function redactPII(input) {
|
|
64
|
-
try {
|
|
65
|
-
const parsed = JSON.parse(input);
|
|
66
|
-
const redacted = walkAndRedact(parsed);
|
|
67
|
-
return JSON.stringify(redacted);
|
|
68
|
-
} catch {
|
|
69
|
-
return input;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ../core/dist/compress.js
|
|
74
|
-
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
75
|
-
"the",
|
|
76
|
-
"is",
|
|
77
|
-
"at",
|
|
78
|
-
"of",
|
|
79
|
-
"on",
|
|
80
|
-
"and",
|
|
81
|
-
"a",
|
|
82
|
-
"to",
|
|
83
|
-
"in",
|
|
84
|
-
"for",
|
|
85
|
-
"it",
|
|
86
|
-
"this",
|
|
87
|
-
"that",
|
|
88
|
-
"with",
|
|
89
|
-
"from",
|
|
90
|
-
"by",
|
|
91
|
-
"an",
|
|
92
|
-
"be",
|
|
93
|
-
"as",
|
|
94
|
-
"are",
|
|
95
|
-
"was",
|
|
96
|
-
"were",
|
|
97
|
-
"been",
|
|
98
|
-
"has",
|
|
99
|
-
"have",
|
|
100
|
-
"had",
|
|
101
|
-
"do",
|
|
102
|
-
"does",
|
|
103
|
-
"did",
|
|
104
|
-
"will",
|
|
105
|
-
"would",
|
|
106
|
-
"could",
|
|
107
|
-
"should",
|
|
108
|
-
"can",
|
|
109
|
-
"may",
|
|
110
|
-
"might"
|
|
111
|
-
]);
|
|
112
|
-
function estimateTokens(text) {
|
|
113
|
-
return Math.ceil(text.length / 4);
|
|
114
|
-
}
|
|
115
|
-
function extractKeywords(query) {
|
|
116
|
-
const words = query.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/).filter((w) => w.length > 0 && !STOP_WORDS.has(w));
|
|
117
|
-
return new Set(words);
|
|
118
|
-
}
|
|
119
|
-
function splitSentences(text) {
|
|
120
|
-
return text.split(/(?<=[.?!])\s+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
121
|
-
}
|
|
122
|
-
function scoreSentence(sentence, keywords) {
|
|
123
|
-
const words = sentence.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/);
|
|
124
|
-
let score = 0;
|
|
125
|
-
for (const word of words) {
|
|
126
|
-
if (keywords.has(word)) {
|
|
127
|
-
score++;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return score;
|
|
131
|
-
}
|
|
132
|
-
function compressContext(query, context) {
|
|
133
|
-
const originalTokens = estimateTokens(context);
|
|
134
|
-
const keywords = extractKeywords(query);
|
|
135
|
-
let compressed;
|
|
136
|
-
if (keywords.size === 0) {
|
|
137
|
-
compressed = context.length > 500 ? context.slice(0, 500) + "..." : context;
|
|
138
|
-
} else {
|
|
139
|
-
const sentences = splitSentences(context);
|
|
140
|
-
const relevant = sentences.filter((sentence) => scoreSentence(sentence, keywords) > 0);
|
|
141
|
-
if (relevant.length === 0) {
|
|
142
|
-
compressed = context.length > 500 ? context.slice(0, 500) + "..." : context;
|
|
143
|
-
} else {
|
|
144
|
-
compressed = relevant.join(" ");
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const compressedTokens = estimateTokens(compressed);
|
|
148
|
-
const savedTokens = originalTokens - compressedTokens;
|
|
149
|
-
const savingsPercent = originalTokens > 0 ? Math.round(savedTokens / originalTokens * 100) : 0;
|
|
150
|
-
return {
|
|
151
|
-
original: context,
|
|
152
|
-
compressed,
|
|
153
|
-
originalTokens,
|
|
154
|
-
compressedTokens,
|
|
155
|
-
savedTokens,
|
|
156
|
-
savingsPercent
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ../core/dist/pipeline.js
|
|
161
|
-
var MAX_PROMPT_LENGTH = 1e5;
|
|
162
|
-
var MAX_CONTEXT_LENGTH = 5e5;
|
|
163
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
164
|
-
function sanitizeContext(context) {
|
|
165
|
-
let sanitized = context.replace(/<[^>]*>/g, "");
|
|
166
|
-
const injectionPatterns = [
|
|
167
|
-
/\[SYSTEM\]/gi,
|
|
168
|
-
/\[INST\]/gi,
|
|
169
|
-
/\[\/INST\]/gi,
|
|
170
|
-
/<<SYS>>/gi,
|
|
171
|
-
/<\/SYS>/gi,
|
|
172
|
-
/\[IMPORTANT\]/gi,
|
|
173
|
-
/\{system\}/gi,
|
|
174
|
-
/###\s*(?:system|instruction|admin)\s*:/gi,
|
|
175
|
-
/(?:^|\n)system\s*:\s*/gi,
|
|
176
|
-
/BEGIN SYSTEM PROMPT/gi,
|
|
177
|
-
/END SYSTEM PROMPT/gi,
|
|
178
|
-
/IGNORE PREVIOUS INSTRUCTIONS/gi,
|
|
179
|
-
/IGNORE ALL PREVIOUS/gi,
|
|
180
|
-
/YOU ARE NOW/gi,
|
|
181
|
-
/NEW INSTRUCTIONS:/gi,
|
|
182
|
-
/OVERRIDE:/gi
|
|
183
|
-
];
|
|
184
|
-
for (const pattern of injectionPatterns) {
|
|
185
|
-
sanitized = sanitized.replace(pattern, "");
|
|
186
|
-
}
|
|
187
|
-
return sanitized;
|
|
188
|
-
}
|
|
189
|
-
var ArisPipeline = class {
|
|
190
|
-
llm;
|
|
191
|
-
cache;
|
|
192
|
-
enableRedaction;
|
|
193
|
-
enableCompression;
|
|
194
|
-
debug;
|
|
195
|
-
constructor(config) {
|
|
196
|
-
this.llm = config.llmProvider;
|
|
197
|
-
this.cache = config.cache;
|
|
198
|
-
this.enableRedaction = config.enableRedaction ?? true;
|
|
199
|
-
this.enableCompression = config.enableCompression ?? true;
|
|
200
|
-
this.debug = config.debug ?? false;
|
|
201
|
-
}
|
|
202
|
-
async process(query, context, options) {
|
|
203
|
-
if (query.length > MAX_PROMPT_LENGTH) {
|
|
204
|
-
throw new Error(`Prompt exceeds maximum length of ${MAX_PROMPT_LENGTH} characters (got ${query.length})`);
|
|
205
|
-
}
|
|
206
|
-
if (context && context.length > MAX_CONTEXT_LENGTH) {
|
|
207
|
-
throw new Error(`Context exceeds maximum length of ${MAX_CONTEXT_LENGTH} characters (got ${context.length})`);
|
|
208
|
-
}
|
|
209
|
-
const sanitizedContext = context ? sanitizeContext(context) : "";
|
|
210
|
-
const originalTokens = estimateTokens(query + sanitizedContext);
|
|
211
|
-
const externalSignal = options?.signal;
|
|
212
|
-
const timeoutController = new AbortController();
|
|
213
|
-
let timeoutId;
|
|
214
|
-
if (!externalSignal) {
|
|
215
|
-
timeoutId = setTimeout(() => timeoutController.abort(), DEFAULT_TIMEOUT_MS);
|
|
216
|
-
}
|
|
217
|
-
const signal = externalSignal ?? timeoutController.signal;
|
|
218
|
-
try {
|
|
219
|
-
if (signal.aborted) {
|
|
220
|
-
throw new Error("Operation aborted");
|
|
221
|
-
}
|
|
222
|
-
const cacheKey = this.cache ? `aris:pipeline:${this.cache.hashRequest(query + sanitizedContext)}` : "";
|
|
223
|
-
if (this.cache && cacheKey) {
|
|
224
|
-
const cached = await this.cache.get(cacheKey);
|
|
225
|
-
if (cached) {
|
|
226
|
-
try {
|
|
227
|
-
const parsed = JSON.parse(cached);
|
|
228
|
-
this.log("Cache hit");
|
|
229
|
-
return { ...parsed, cached: true };
|
|
230
|
-
} catch {
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
let processedQuery = query;
|
|
235
|
-
let processedContext = sanitizedContext;
|
|
236
|
-
let redacted = false;
|
|
237
|
-
if (this.enableRedaction) {
|
|
238
|
-
processedQuery = redactPII(processedQuery);
|
|
239
|
-
if (processedContext) {
|
|
240
|
-
processedContext = redactPII(processedContext);
|
|
241
|
-
}
|
|
242
|
-
redacted = true;
|
|
243
|
-
this.log("PII redaction applied");
|
|
244
|
-
}
|
|
245
|
-
let compressed = false;
|
|
246
|
-
if (this.enableCompression && processedContext) {
|
|
247
|
-
const compression = compressContext(processedQuery, processedContext);
|
|
248
|
-
processedContext = compression.compressed;
|
|
249
|
-
compressed = true;
|
|
250
|
-
this.log(`Compression: ${compression.savedTokens} tokens saved (${compression.savingsPercent}%)`);
|
|
251
|
-
}
|
|
252
|
-
const prompt = processedContext ? `Context:
|
|
253
|
-
${processedContext}
|
|
254
|
-
|
|
255
|
-
Query: ${processedQuery}` : processedQuery;
|
|
256
|
-
const processedTokens = estimateTokens(prompt);
|
|
257
|
-
if (signal.aborted) {
|
|
258
|
-
throw new Error("Operation aborted");
|
|
259
|
-
}
|
|
260
|
-
this.log(`Sending to ${this.llm.name}`);
|
|
261
|
-
const llmResponse = await this.llm.send(prompt, { signal });
|
|
262
|
-
const savedTokens = originalTokens - processedTokens;
|
|
263
|
-
const savingsPercent = originalTokens > 0 ? Math.round(savedTokens / originalTokens * 100) : 0;
|
|
264
|
-
const result = {
|
|
265
|
-
response: llmResponse.content,
|
|
266
|
-
model: llmResponse.model,
|
|
267
|
-
originalTokens,
|
|
268
|
-
processedTokens,
|
|
269
|
-
savedTokens,
|
|
270
|
-
savingsPercent,
|
|
271
|
-
cached: false,
|
|
272
|
-
redacted,
|
|
273
|
-
compressed,
|
|
274
|
-
usage: llmResponse.usage
|
|
275
|
-
};
|
|
276
|
-
if (this.cache && cacheKey) {
|
|
277
|
-
await this.cache.set(cacheKey, JSON.stringify(result));
|
|
278
|
-
this.log("Response cached");
|
|
279
|
-
}
|
|
280
|
-
return result;
|
|
281
|
-
} finally {
|
|
282
|
-
if (timeoutId !== void 0) {
|
|
283
|
-
clearTimeout(timeoutId);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
log(message) {
|
|
288
|
-
if (this.debug) {
|
|
289
|
-
process.stderr.write(`[aris:pipeline] ${message}
|
|
290
|
-
`);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
// ../core/dist/providers/openrouter.js
|
|
296
|
-
var DEFAULT_MODEL = "minimax/minimax-m2.5";
|
|
297
|
-
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
298
|
-
var OpenRouterProvider = class {
|
|
299
|
-
name = "openrouter";
|
|
300
|
-
apiKey;
|
|
301
|
-
model;
|
|
302
|
-
baseUrl;
|
|
303
|
-
constructor(config) {
|
|
304
|
-
this.apiKey = config.apiKey;
|
|
305
|
-
this.model = config.model ?? DEFAULT_MODEL;
|
|
306
|
-
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
307
|
-
}
|
|
308
|
-
async send(prompt, options) {
|
|
309
|
-
const model = options?.model ?? this.model;
|
|
310
|
-
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
311
|
-
method: "POST",
|
|
312
|
-
headers: {
|
|
313
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
314
|
-
"Content-Type": "application/json",
|
|
315
|
-
"HTTP-Referer": "https://aris.dev",
|
|
316
|
-
"X-Title": "Aris MCP"
|
|
317
|
-
},
|
|
318
|
-
body: JSON.stringify({
|
|
319
|
-
model,
|
|
320
|
-
messages: [{ role: "user", content: prompt }],
|
|
321
|
-
max_tokens: options?.maxTokens ?? 4096,
|
|
322
|
-
temperature: options?.temperature ?? 0.7
|
|
323
|
-
}),
|
|
324
|
-
signal: options?.signal
|
|
325
|
-
});
|
|
326
|
-
if (!response.ok) {
|
|
327
|
-
const errorText = await response.text();
|
|
328
|
-
throw new Error(`OpenRouter API error (${response.status}): ${errorText}`);
|
|
329
|
-
}
|
|
330
|
-
const data = await response.json();
|
|
331
|
-
const content = data.choices?.[0]?.message?.content ?? "";
|
|
332
|
-
return {
|
|
333
|
-
content,
|
|
334
|
-
model: data.model ?? model,
|
|
335
|
-
usage: data.usage ? {
|
|
336
|
-
promptTokens: data.usage.prompt_tokens,
|
|
337
|
-
completionTokens: data.usage.completion_tokens,
|
|
338
|
-
totalTokens: data.usage.total_tokens
|
|
339
|
-
} : void 0
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
async healthCheck() {
|
|
343
|
-
try {
|
|
344
|
-
const response = await fetch(`${this.baseUrl}/models`, {
|
|
345
|
-
headers: { Authorization: `Bearer ${this.apiKey}` }
|
|
346
|
-
});
|
|
347
|
-
return response.ok;
|
|
348
|
-
} catch {
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
// ../core/dist/providers/ollama.js
|
|
355
|
-
var DEFAULT_MODEL2 = "llama3";
|
|
356
|
-
var DEFAULT_BASE_URL2 = "http://localhost:11434";
|
|
357
|
-
var OllamaProvider = class {
|
|
358
|
-
name = "ollama";
|
|
359
|
-
model;
|
|
360
|
-
baseUrl;
|
|
361
|
-
constructor(config = {}) {
|
|
362
|
-
this.model = config.model ?? DEFAULT_MODEL2;
|
|
363
|
-
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL2;
|
|
364
|
-
}
|
|
365
|
-
async send(prompt, options) {
|
|
366
|
-
const model = options?.model ?? this.model;
|
|
367
|
-
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
368
|
-
method: "POST",
|
|
369
|
-
headers: { "Content-Type": "application/json" },
|
|
370
|
-
body: JSON.stringify({
|
|
371
|
-
model,
|
|
372
|
-
messages: [{ role: "user", content: prompt }],
|
|
373
|
-
stream: false,
|
|
374
|
-
options: {
|
|
375
|
-
num_predict: options?.maxTokens ?? 4096,
|
|
376
|
-
temperature: options?.temperature ?? 0.7
|
|
377
|
-
}
|
|
378
|
-
}),
|
|
379
|
-
signal: options?.signal
|
|
380
|
-
});
|
|
381
|
-
if (!response.ok) {
|
|
382
|
-
const errorText = await response.text();
|
|
383
|
-
throw new Error(`Ollama API error (${response.status}): ${errorText}`);
|
|
384
|
-
}
|
|
385
|
-
const data = await response.json();
|
|
386
|
-
const promptTokens = data.prompt_eval_count ?? 0;
|
|
387
|
-
const completionTokens = data.eval_count ?? 0;
|
|
388
|
-
return {
|
|
389
|
-
content: data.message?.content ?? "",
|
|
390
|
-
model: data.model ?? model,
|
|
391
|
-
usage: {
|
|
392
|
-
promptTokens,
|
|
393
|
-
completionTokens,
|
|
394
|
-
totalTokens: promptTokens + completionTokens
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
async healthCheck() {
|
|
399
|
-
try {
|
|
400
|
-
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
401
|
-
return response.ok;
|
|
402
|
-
} catch {
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
// ../core/dist/cache.js
|
|
409
|
-
import { createHash } from "crypto";
|
|
410
|
-
import { Redis } from "ioredis";
|
|
411
|
-
var ArisCache = class {
|
|
412
|
-
redis;
|
|
413
|
-
constructor(redisUrl) {
|
|
414
|
-
this.redis = new Redis(redisUrl ?? "redis://localhost:6379", {
|
|
415
|
-
lazyConnect: true,
|
|
416
|
-
retryStrategy: (times) => {
|
|
417
|
-
if (times > 3)
|
|
418
|
-
return null;
|
|
419
|
-
return Math.min(times * 200, 2e3);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
this.redis.on("error", (err) => {
|
|
423
|
-
process.stderr.write(`[ArisCache] Redis connection error: ${err.message}
|
|
424
|
-
`);
|
|
425
|
-
});
|
|
426
|
-
this.redis.connect().catch((err) => {
|
|
427
|
-
process.stderr.write(`[ArisCache] Failed to connect to Redis: ${err.message}
|
|
428
|
-
`);
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
async get(key) {
|
|
432
|
-
try {
|
|
433
|
-
return await this.redis.get(key);
|
|
434
|
-
} catch (err) {
|
|
435
|
-
console.warn(`[ArisCache] get failed: ${err.message}`);
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
async set(key, value, ttlSeconds = 600) {
|
|
440
|
-
try {
|
|
441
|
-
await this.redis.set(key, value, "EX", ttlSeconds);
|
|
442
|
-
} catch (err) {
|
|
443
|
-
console.warn(`[ArisCache] set failed: ${err.message}`);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
hashRequest(body) {
|
|
447
|
-
return createHash("sha256").update(body).digest("hex");
|
|
448
|
-
}
|
|
449
|
-
async ping() {
|
|
450
|
-
try {
|
|
451
|
-
const result = await this.redis.ping();
|
|
452
|
-
return result === "PONG";
|
|
453
|
-
} catch {
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
async checkThrottle(maxJoules = 5e3) {
|
|
458
|
-
try {
|
|
459
|
-
const value = await this.redis.get("aris_system_joules");
|
|
460
|
-
if (value === null)
|
|
461
|
-
return false;
|
|
462
|
-
const joules = parseFloat(value);
|
|
463
|
-
return joules > maxJoules;
|
|
464
|
-
} catch (err) {
|
|
465
|
-
console.warn(`[ArisCache] checkThrottle failed: ${err.message}`);
|
|
466
|
-
return false;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
async disconnect() {
|
|
470
|
-
try {
|
|
471
|
-
await this.redis.quit();
|
|
472
|
-
} catch {
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
|
|
477
8
|
// src/config.ts
|
|
478
|
-
var DEFAULT_API_KEY = "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";
|
|
479
|
-
function envBool(value, defaultValue) {
|
|
480
|
-
if (value === void 0) return defaultValue;
|
|
481
|
-
return value.toLowerCase() === "true";
|
|
482
|
-
}
|
|
483
9
|
function loadConfig() {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
llmApiKey,
|
|
491
|
-
llmModel: process.env.ARIS_LLM_MODEL,
|
|
492
|
-
ollamaUrl,
|
|
493
|
-
redisUrl: process.env.ARIS_REDIS_URL,
|
|
494
|
-
enableRedaction: envBool(process.env.ARIS_ENABLE_REDACTION, true),
|
|
495
|
-
enableCompression: envBool(process.env.ARIS_ENABLE_COMPRESSION, true),
|
|
496
|
-
debug: envBool(process.env.ARIS_DEBUG, false)
|
|
497
|
-
};
|
|
10
|
+
const apiKey = process.env.ARIS_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error("ARIS_API_KEY is required");
|
|
13
|
+
}
|
|
14
|
+
const apiUrl = process.env.ARIS_API_URL ?? "https://energetic-light-production.up.railway.app";
|
|
15
|
+
return { apiKey, apiUrl };
|
|
498
16
|
}
|
|
499
17
|
|
|
500
18
|
// src/tools.ts
|
|
@@ -582,18 +100,6 @@ function log(message) {
|
|
|
582
100
|
process.stderr.write(`[aris-mcp] ${message}
|
|
583
101
|
`);
|
|
584
102
|
}
|
|
585
|
-
function createLLMProvider(config) {
|
|
586
|
-
if (config.llmProvider === "ollama") {
|
|
587
|
-
return new OllamaProvider({
|
|
588
|
-
baseUrl: config.ollamaUrl,
|
|
589
|
-
model: config.llmModel
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
return new OpenRouterProvider({
|
|
593
|
-
apiKey: config.llmApiKey,
|
|
594
|
-
model: config.llmModel
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
103
|
async function main() {
|
|
598
104
|
let config;
|
|
599
105
|
try {
|
|
@@ -602,111 +108,67 @@ async function main() {
|
|
|
602
108
|
log(`Configuration error: ${err.message}`);
|
|
603
109
|
process.exit(1);
|
|
604
110
|
}
|
|
605
|
-
log(`
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
111
|
+
log(`API: ${config.apiUrl}`);
|
|
112
|
+
async function apiCall(path, body) {
|
|
113
|
+
const res = await fetch(`${config.apiUrl}${path}`, {
|
|
114
|
+
method: body ? "POST" : "GET",
|
|
115
|
+
headers: {
|
|
116
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
117
|
+
"Content-Type": "application/json"
|
|
118
|
+
},
|
|
119
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
120
|
+
signal: AbortSignal.timeout(35e3)
|
|
121
|
+
});
|
|
122
|
+
const data = await res.json();
|
|
123
|
+
return { status: res.status, data };
|
|
614
124
|
}
|
|
615
|
-
const pipeline = new ArisPipeline({
|
|
616
|
-
llmProvider,
|
|
617
|
-
cache,
|
|
618
|
-
enableRedaction: config.enableRedaction,
|
|
619
|
-
enableCompression: config.enableCompression,
|
|
620
|
-
debug: config.debug
|
|
621
|
-
});
|
|
622
125
|
const server = new McpServer(
|
|
623
|
-
{
|
|
624
|
-
|
|
625
|
-
version: "1.0.0"
|
|
626
|
-
},
|
|
627
|
-
{
|
|
628
|
-
capabilities: {
|
|
629
|
-
tools: {}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
126
|
+
{ name: "aris-mcp", version: "2.0.0" },
|
|
127
|
+
{ capabilities: { tools: {} } }
|
|
632
128
|
);
|
|
633
129
|
server.tool(
|
|
634
130
|
ToolName.OPTIMIZE,
|
|
635
131
|
TOOL_DESCRIPTIONS[ToolName.OPTIMIZE],
|
|
636
132
|
{
|
|
637
|
-
prompt: z2.string().min(1
|
|
638
|
-
"The user prompt to optimize and send through the Aris pipeline.
|
|
133
|
+
prompt: z2.string().min(1).max(1e5).describe(
|
|
134
|
+
"The user prompt to optimize and send through the Aris pipeline."
|
|
639
135
|
),
|
|
640
|
-
context: z2.string().max(5e5
|
|
641
|
-
"Optional background context to compress before including with the prompt.
|
|
136
|
+
context: z2.string().max(5e5).optional().describe(
|
|
137
|
+
"Optional background context to compress before including with the prompt."
|
|
642
138
|
)
|
|
643
139
|
},
|
|
644
|
-
async ({ prompt, context }
|
|
140
|
+
async ({ prompt, context }) => {
|
|
645
141
|
try {
|
|
646
|
-
await
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
message: "Starting Aris optimization pipeline..."
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
await sendNotification({
|
|
656
|
-
method: "notifications/progress",
|
|
657
|
-
params: {
|
|
658
|
-
progressToken: "aris_optimize",
|
|
659
|
-
progress: 20,
|
|
660
|
-
total: 100,
|
|
661
|
-
message: "Applying PII redaction and context compression..."
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
const controller = new AbortController();
|
|
665
|
-
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
666
|
-
let result;
|
|
667
|
-
try {
|
|
668
|
-
result = await pipeline.process(prompt, context, {
|
|
669
|
-
signal: controller.signal
|
|
670
|
-
});
|
|
671
|
-
} finally {
|
|
672
|
-
clearTimeout(timeout);
|
|
142
|
+
const { status, data } = await apiCall("/optimize", { prompt, context });
|
|
143
|
+
if (status !== 200) {
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text: `Pipeline error: ${data.error ?? "Unknown error"}` }],
|
|
146
|
+
isError: true
|
|
147
|
+
};
|
|
673
148
|
}
|
|
674
|
-
await sendNotification({
|
|
675
|
-
method: "notifications/progress",
|
|
676
|
-
params: {
|
|
677
|
-
progressToken: "aris_optimize",
|
|
678
|
-
progress: 100,
|
|
679
|
-
total: 100,
|
|
680
|
-
message: "Pipeline complete."
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
149
|
const summary = [
|
|
684
|
-
|
|
150
|
+
data.response,
|
|
685
151
|
"",
|
|
686
152
|
"--- Aris Pipeline Stats ---",
|
|
687
|
-
`Model: ${
|
|
688
|
-
`Original tokens: ${
|
|
689
|
-
`Processed tokens: ${
|
|
690
|
-
`Tokens saved: ${
|
|
691
|
-
`Cached: ${
|
|
692
|
-
`Redacted: ${
|
|
693
|
-
`Compressed: ${
|
|
153
|
+
`Model: ${data.model}`,
|
|
154
|
+
`Original tokens: ${data.originalTokens}`,
|
|
155
|
+
`Processed tokens: ${data.processedTokens}`,
|
|
156
|
+
`Tokens saved: ${data.savedTokens} (${data.savingsPercent}%)`,
|
|
157
|
+
`Cached: ${data.cached}`,
|
|
158
|
+
`Redacted: ${data.redacted}`,
|
|
159
|
+
`Compressed: ${data.compressed}`
|
|
694
160
|
];
|
|
695
|
-
|
|
161
|
+
const usage = data.usage;
|
|
162
|
+
if (usage) {
|
|
696
163
|
summary.push(
|
|
697
|
-
`LLM usage - prompt: ${
|
|
164
|
+
`LLM usage - prompt: ${usage.promptTokens}, completion: ${usage.completionTokens}, total: ${usage.totalTokens}`
|
|
698
165
|
);
|
|
699
166
|
}
|
|
700
|
-
return {
|
|
701
|
-
content: [{ type: "text", text: summary.join("\n") }]
|
|
702
|
-
};
|
|
167
|
+
return { content: [{ type: "text", text: summary.join("\n") }] };
|
|
703
168
|
} catch (err) {
|
|
704
169
|
const message = err instanceof Error ? err.message : String(err);
|
|
705
|
-
const errorText = message.includes("aborted") ? "Pipeline timed out
|
|
706
|
-
return {
|
|
707
|
-
content: [{ type: "text", text: errorText }],
|
|
708
|
-
isError: true
|
|
709
|
-
};
|
|
170
|
+
const errorText = message.includes("TimeoutError") || message.includes("aborted") ? "Pipeline timed out" : message.includes("fetch") ? "Aris API unavailable" : `Pipeline error: ${message}`;
|
|
171
|
+
return { content: [{ type: "text", text: errorText }], isError: true };
|
|
710
172
|
}
|
|
711
173
|
}
|
|
712
174
|
);
|
|
@@ -716,42 +178,18 @@ async function main() {
|
|
|
716
178
|
{},
|
|
717
179
|
async () => {
|
|
718
180
|
try {
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
cache: {
|
|
728
|
-
enabled: !!cache,
|
|
729
|
-
healthy: cacheHealthy,
|
|
730
|
-
url: config.redisUrl ? "(configured)" : "(not configured)"
|
|
731
|
-
},
|
|
732
|
-
features: {
|
|
733
|
-
redaction: config.enableRedaction,
|
|
734
|
-
compression: config.enableCompression
|
|
735
|
-
},
|
|
736
|
-
debug: config.debug
|
|
737
|
-
};
|
|
738
|
-
return {
|
|
739
|
-
content: [
|
|
740
|
-
{
|
|
741
|
-
type: "text",
|
|
742
|
-
text: JSON.stringify(status, null, 2)
|
|
743
|
-
}
|
|
744
|
-
]
|
|
745
|
-
};
|
|
181
|
+
const { status, data } = await apiCall("/health");
|
|
182
|
+
if (status !== 200) {
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: "text", text: `Health check error: ${data.error ?? "Unknown error"}` }],
|
|
185
|
+
isError: true
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
746
189
|
} catch (err) {
|
|
747
190
|
const message = err instanceof Error ? err.message : String(err);
|
|
748
191
|
return {
|
|
749
|
-
content: [
|
|
750
|
-
{
|
|
751
|
-
type: "text",
|
|
752
|
-
text: `Health check error: ${message}`
|
|
753
|
-
}
|
|
754
|
-
],
|
|
192
|
+
content: [{ type: "text", text: `Health check error: ${message}` }],
|
|
755
193
|
isError: true
|
|
756
194
|
};
|
|
757
195
|
}
|
|
@@ -761,37 +199,36 @@ async function main() {
|
|
|
761
199
|
ToolName.COMPRESS,
|
|
762
200
|
TOOL_DESCRIPTIONS[ToolName.COMPRESS],
|
|
763
201
|
{
|
|
764
|
-
query: z2.string().min(1
|
|
765
|
-
"The query used to determine which sentences in the context are relevant.
|
|
202
|
+
query: z2.string().min(1).max(1e5).describe(
|
|
203
|
+
"The query used to determine which sentences in the context are relevant."
|
|
766
204
|
),
|
|
767
|
-
context: z2.string().min(1
|
|
768
|
-
"The context text to compress.
|
|
205
|
+
context: z2.string().min(1).max(5e5).describe(
|
|
206
|
+
"The context text to compress."
|
|
769
207
|
)
|
|
770
208
|
},
|
|
771
209
|
async ({ query, context }) => {
|
|
772
210
|
try {
|
|
773
|
-
const
|
|
211
|
+
const { status, data } = await apiCall("/compress", { query, context });
|
|
212
|
+
if (status !== 200) {
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: "text", text: `Compression error: ${data.error ?? "Unknown error"}` }],
|
|
215
|
+
isError: true
|
|
216
|
+
};
|
|
217
|
+
}
|
|
774
218
|
const summary = [
|
|
775
219
|
"Compressed context:",
|
|
776
|
-
|
|
220
|
+
data.compressed,
|
|
777
221
|
"",
|
|
778
222
|
"--- Compression Stats ---",
|
|
779
|
-
`Original tokens: ${
|
|
780
|
-
`Compressed tokens: ${
|
|
781
|
-
`Tokens saved: ${
|
|
223
|
+
`Original tokens: ${data.originalTokens}`,
|
|
224
|
+
`Compressed tokens: ${data.compressedTokens}`,
|
|
225
|
+
`Tokens saved: ${data.savedTokens} (${data.savingsPercent}%)`
|
|
782
226
|
];
|
|
783
|
-
return {
|
|
784
|
-
content: [{ type: "text", text: summary.join("\n") }]
|
|
785
|
-
};
|
|
227
|
+
return { content: [{ type: "text", text: summary.join("\n") }] };
|
|
786
228
|
} catch (err) {
|
|
787
229
|
const message = err instanceof Error ? err.message : String(err);
|
|
788
230
|
return {
|
|
789
|
-
content: [
|
|
790
|
-
{
|
|
791
|
-
type: "text",
|
|
792
|
-
text: `Compression error: ${message}`
|
|
793
|
-
}
|
|
794
|
-
],
|
|
231
|
+
content: [{ type: "text", text: `Compression error: ${message}` }],
|
|
795
232
|
isError: true
|
|
796
233
|
};
|
|
797
234
|
}
|
|
@@ -803,12 +240,8 @@ async function main() {
|
|
|
803
240
|
const shutdown = async () => {
|
|
804
241
|
log("Shutting down...");
|
|
805
242
|
try {
|
|
806
|
-
if (cache) {
|
|
807
|
-
await cache.disconnect();
|
|
808
|
-
}
|
|
809
243
|
await server.close();
|
|
810
|
-
} catch
|
|
811
|
-
log(`Error during shutdown: ${err.message}`);
|
|
244
|
+
} catch {
|
|
812
245
|
}
|
|
813
246
|
process.exit(0);
|
|
814
247
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aris-mcp/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aris-mcp": "dist/index.js"
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
34
|
-
"ioredis": "^5.6.1",
|
|
35
34
|
"zod": "^3.24.4"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|