@torqon/mcp 0.1.6 → 0.1.8

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/cli.js CHANGED
@@ -264,7 +264,7 @@ async function cmdLogin() {
264
264
  printLogo();
265
265
  // Generate a random one-time state token
266
266
  const state = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
267
- const WEB_URL = API_URL.includes('localhost') ? 'http://localhost:3002' : 'https://torqon.vercel.app';
267
+ const WEB_URL = API_URL.includes('localhost') ? 'http://localhost:3002' : 'https://torqon.dev';
268
268
  const loginUrl = `${WEB_URL}/auth/cli?state=${state}`;
269
269
  console.log(` ${B('Opening browser to complete login...')}\n`);
270
270
  console.log(` ${Gr('URL:')} ${Cy(loginUrl)}\n`);
package/dist/index.js CHANGED
@@ -185,22 +185,131 @@ server.tool('retrieve_context', 'Retrieves relevant facts from Torqon memory for
185
185
  };
186
186
  });
187
187
  // ── optimize_context ─────────────────────────────────────────────────────────
188
- // Compresses a long document or prompt before using it.
189
- server.tool('optimize_context', 'Compresses a large document or long prompt into a dense structured context package. Use before feeding large text to reduce tokens.', {
188
+ // Real algorithmic compression no LLM call, no backend, pure local processing.
189
+ // Extracts structured facts, strips filler, deduplicates, returns dense format.
190
+ function estimateTokens(text) {
191
+ // ~4 chars per token (GPT/Claude average)
192
+ return Math.ceil(text.length / 4);
193
+ }
194
+ function compressText(raw) {
195
+ const lines = raw.split('\n');
196
+ const facts = [];
197
+ const seen = new Set();
198
+ // Filler phrases to strip from lines
199
+ const fillerPatterns = [
200
+ /\bplease note that\b/gi,
201
+ /\bit is (important|worth noting|essential) (to note |that )?/gi,
202
+ /\bas (mentioned|noted|discussed) (above|before|earlier|previously)\b/gi,
203
+ /\bin (this|the) (context|case|situation|scenario)\b/gi,
204
+ /\bbasically\b/gi,
205
+ /\bessentially\b/gi,
206
+ /\bfundamentally\b/gi,
207
+ /\bof course\b/gi,
208
+ /\bneedless to say\b/gi,
209
+ /\bit goes without saying\b/gi,
210
+ /\bfor (all intents and purposes|the most part)\b/gi,
211
+ /\bin order to\b/gi, // → "to"
212
+ /\bdue to the fact that\b/gi, // → "because"
213
+ /\bat this point in time\b/gi, // → "now"
214
+ /\bthe fact that\b/gi,
215
+ /\bwhat this means is\b/gi,
216
+ /\bwhat we (can|need to) (see|do|understand) (is|here)?\b/gi,
217
+ ];
218
+ // Verbose phrase replacements
219
+ const replacements = [
220
+ [/in order to/gi, 'to'],
221
+ [/due to the fact that/gi, 'because'],
222
+ [/at this point in time/gi, 'now'],
223
+ [/a large number of/gi, 'many'],
224
+ [/a majority of/gi, 'most'],
225
+ [/is able to/gi, 'can'],
226
+ [/is going to/gi, 'will'],
227
+ [/make sure (to|that)/gi, 'ensure'],
228
+ [/take into account/gi, 'consider'],
229
+ [/with (the )?regard(s)? to/gi, 'regarding'],
230
+ [/in the event that/gi, 'if'],
231
+ [/on a daily basis/gi, 'daily'],
232
+ [/on a weekly basis/gi, 'weekly'],
233
+ [/on a monthly basis/gi, 'monthly'],
234
+ ];
235
+ for (const line of lines) {
236
+ let l = line.trim();
237
+ if (!l)
238
+ continue;
239
+ // Skip pure decoration lines
240
+ if (/^[-=*#]{3,}$/.test(l))
241
+ continue;
242
+ // Skip lines that are just whitespace or single chars
243
+ if (l.length < 3)
244
+ continue;
245
+ // Apply replacements
246
+ for (const [pattern, replacement] of replacements) {
247
+ l = l.replace(pattern, replacement);
248
+ }
249
+ // Strip filler phrases
250
+ for (const pattern of fillerPatterns) {
251
+ l = l.replace(pattern, '');
252
+ }
253
+ // Collapse multiple spaces
254
+ l = l.replace(/\s{2,}/g, ' ').trim();
255
+ // Skip if now too short
256
+ if (l.length < 4)
257
+ continue;
258
+ // Deduplication — normalize for comparison
259
+ const normalized = l.toLowerCase().replace(/[^a-z0-9]/g, '');
260
+ if (seen.has(normalized))
261
+ continue;
262
+ seen.add(normalized);
263
+ facts.push(l);
264
+ }
265
+ // Group lines under their nearest heading
266
+ const sections = [];
267
+ let currentSection = { heading: '', items: [] };
268
+ for (const fact of facts) {
269
+ // Detect headings: ALL CAPS line, or line ending with colon, or markdown ##
270
+ const isHeading = /^#{1,3}\s/.test(fact) ||
271
+ /^[A-Z][A-Z\s\-_]{4,}:?$/.test(fact) ||
272
+ (fact.endsWith(':') && fact.length < 60 && !fact.includes('.'));
273
+ if (isHeading) {
274
+ if (currentSection.items.length > 0 || currentSection.heading) {
275
+ sections.push(currentSection);
276
+ }
277
+ currentSection = { heading: fact.replace(/^#+\s*/, '').replace(/:$/, ''), items: [] };
278
+ }
279
+ else {
280
+ currentSection.items.push(fact);
281
+ }
282
+ }
283
+ if (currentSection.items.length > 0 || currentSection.heading) {
284
+ sections.push(currentSection);
285
+ }
286
+ // Render dense output
287
+ const out = [];
288
+ for (const section of sections) {
289
+ if (section.heading) {
290
+ out.push(`[${section.heading.toUpperCase()}]`);
291
+ }
292
+ for (const item of section.items) {
293
+ // Bullet items stay as-is; plain sentences get prefixed with •
294
+ const prefix = item.startsWith('-') || item.startsWith('*') || item.startsWith('•') ? '' : '• ';
295
+ out.push(`${prefix}${item.replace(/^[-*]\s*/, '')}`);
296
+ }
297
+ if (section.items.length > 0)
298
+ out.push('');
299
+ }
300
+ return out.join('\n').trim();
301
+ }
302
+ server.tool('optimize_context', 'Compresses a large document or long prompt into a dense token-efficient format. Strips filler, deduplicates, restructures. Use before feeding large text to reduce tokens.', {
190
303
  message: z.string().describe('The raw text to compress'),
191
304
  }, async ({ message }) => {
192
- const response = await fetch(`${API_URL}/chat`, {
193
- method: 'POST',
194
- headers,
195
- body: JSON.stringify({
196
- conversationId: SESSION_ID,
197
- message,
198
- apiKey: API_KEY,
199
- }),
200
- });
201
- const data = await response.json();
305
+ const inputTokens = estimateTokens(message);
306
+ const compressed = compressText(message);
307
+ const outputTokens = estimateTokens(compressed);
308
+ const saved = inputTokens - outputTokens;
309
+ const pct = Math.round((saved / inputTokens) * 100);
310
+ const header = `[TORQON OPTIMIZED — ${inputTokens} tokens → ${outputTokens} tokens (${pct}% reduction)]\n\n`;
202
311
  return {
203
- content: [{ type: 'text', text: data.response?.content ?? 'No response from Torqon' }],
312
+ content: [{ type: 'text', text: header + compressed }],
204
313
  };
205
314
  });
206
315
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
- {
1
+ {
2
2
  "name": "@torqon/mcp",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -26,4 +26,3 @@
26
26
  "typescript": "^5.0.0"
27
27
  }
28
28
  }
29
-