local-model-suitability-mcp 1.1.22 → 1.1.24
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/server.json +36 -36
- package/src/server.js +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.24] - 2026-06-26
|
|
4
|
+
- fix: trial extension requests now written to Redis (lms:trial:{email}) on grant -- permanent audit trail that survives redeploys; previously in-memory only
|
|
5
|
+
|
|
6
|
+
## [1.1.23] - 2026-06-25
|
|
7
|
+
- feat: calls_remaining field added to check_local_viability response -- "unlimited" for paid keys, numeric free-tier headroom otherwise (HTTP POST and stdio transports)
|
|
8
|
+
- feat: verdict_ttl field added (86400s/24h)
|
|
9
|
+
- feat: data_source_status field added (full/degraded) -- "degraded" when Anthropic responds but returns unparseable output and a fallback verdict is used; "full" otherwise, including the CONFIDENTIAL local-rule shortcut which never calls Anthropic
|
|
10
|
+
|
|
3
11
|
## [1.1.22] - 2026-06-24
|
|
4
12
|
- feat: unauthenticated /public-stats endpoint -- first_deployed, lifetime tool calls, uptime %, version, for agent orchestrators evaluating server trustworthiness
|
|
5
13
|
- feat: /process-trial-followups endpoint + 24h follow-up record on trial-extension grant
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-model-suitability-mcp",
|
|
3
3
|
"mcpName": "io.github.OjasKord/local-model-suitability-mcp",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.24",
|
|
5
5
|
"description": "AI model router for agents. Checks whether a local model can handle the task before calling cloud inference. LOCAL/CLOUD verdict saves cost on every call.",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"type": "module",
|
package/server.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "io.github.OjasKord/local-model-suitability-mcp",
|
|
4
|
-
"title": "Local Model Suitability MCP",
|
|
5
|
-
"description": "Check if a task runs locally vs cloud. Save money on calls that don't need cloud inference.",
|
|
6
|
-
"version": "1.1.
|
|
7
|
-
"websiteUrl": "https://kordagencies.com",
|
|
8
|
-
"repository": {
|
|
9
|
-
"url": "https://github.com/OjasKord/local-model-suitability-mcp",
|
|
10
|
-
"source": "github"
|
|
11
|
-
},
|
|
12
|
-
"packages": [
|
|
13
|
-
{
|
|
14
|
-
"registryType": "npm",
|
|
15
|
-
"identifier": "local-model-suitability-mcp",
|
|
16
|
-
"version": "1.1.
|
|
17
|
-
"transport": {
|
|
18
|
-
"type": "stdio"
|
|
19
|
-
},
|
|
20
|
-
"environmentVariables": [
|
|
21
|
-
{
|
|
22
|
-
"name": "ANTHROPIC_API_KEY",
|
|
23
|
-
"description": "Anthropic API key for Claude routing analysis",
|
|
24
|
-
"isRequired": true,
|
|
25
|
-
"isSecret": true
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"remotes": [
|
|
31
|
-
{
|
|
32
|
-
"type": "streamable-http",
|
|
33
|
-
"url": "https://local-model-suitability-mcp-production.up.railway.app"
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.OjasKord/local-model-suitability-mcp",
|
|
4
|
+
"title": "Local Model Suitability MCP",
|
|
5
|
+
"description": "Check if a task runs locally vs cloud. Save money on calls that don't need cloud inference.",
|
|
6
|
+
"version": "1.1.23",
|
|
7
|
+
"websiteUrl": "https://kordagencies.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/OjasKord/local-model-suitability-mcp",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"packages": [
|
|
13
|
+
{
|
|
14
|
+
"registryType": "npm",
|
|
15
|
+
"identifier": "local-model-suitability-mcp",
|
|
16
|
+
"version": "1.1.23",
|
|
17
|
+
"transport": {
|
|
18
|
+
"type": "stdio"
|
|
19
|
+
},
|
|
20
|
+
"environmentVariables": [
|
|
21
|
+
{
|
|
22
|
+
"name": "ANTHROPIC_API_KEY",
|
|
23
|
+
"description": "Anthropic API key for Claude routing analysis",
|
|
24
|
+
"isRequired": true,
|
|
25
|
+
"isSecret": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"remotes": [
|
|
31
|
+
{
|
|
32
|
+
"type": "streamable-http",
|
|
33
|
+
"url": "https://local-model-suitability-mcp-production.up.railway.app"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/src/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createHmac, timingSafeEqual } from 'crypto';
|
|
|
3
3
|
import { readFileSync, writeFileSync } from 'fs';
|
|
4
4
|
import Anthropic from '@anthropic-ai/sdk';
|
|
5
5
|
|
|
6
|
-
const VERSION = '1.1.
|
|
6
|
+
const VERSION = '1.1.24';
|
|
7
7
|
const FIRST_DEPLOYED = '2026-04-13T06:41:38Z';
|
|
8
8
|
const LIFETIME_CALLS_REDIS_KEY = 'lms:lifetime_calls';
|
|
9
9
|
const UPTIME_HEARTBEAT_KEY = 'lms:uptime:heartbeat_count';
|
|
@@ -16,6 +16,8 @@ const ENTERPRISE_UPGRADE_URL = 'https://buy.stripe.com/28E9AS27PbPvfkoe7Sebu0q';
|
|
|
16
16
|
const ALLOWED_PAYMENT_LINK_IDS = ['plink_1TQzCBD6WvRe6sn3H1q5t2LF', 'plink_1TQzDSD6WvRe6sn3UM2G1EgX'];
|
|
17
17
|
const PERSIST_FILE = '/tmp/lms_stats.json';
|
|
18
18
|
const LEGAL_DISCLAIMER = 'AI-powered routing analysis. We do not log or store your task content. Results are for cost-optimisation guidance only. Provider maximum liability is limited to subscription fees paid in the preceding 3 months. Full terms: kordagencies.com/terms.html';
|
|
19
|
+
// Caching/staleness policy per tool, in seconds.
|
|
20
|
+
const VERDICT_TTL = { check_local_viability: 86400 };
|
|
19
21
|
|
|
20
22
|
function nowISO() { return new Date().toISOString(); }
|
|
21
23
|
|
|
@@ -382,6 +384,8 @@ async function checkLocalViability(task, qualityThreshold, dataSensitivity) {
|
|
|
382
384
|
cloud_justified_reason: null,
|
|
383
385
|
data_sensitivity_override: true,
|
|
384
386
|
analysis_type: 'AI-powered cost routing — NOT a simple lookup',
|
|
387
|
+
verdict_ttl: VERDICT_TTL.check_local_viability,
|
|
388
|
+
data_source_status: 'full',
|
|
385
389
|
_disclaimer: LEGAL_DISCLAIMER
|
|
386
390
|
};
|
|
387
391
|
}
|
|
@@ -429,6 +433,7 @@ Respond ONLY with a JSON object — no markdown, no explanation outside the JSON
|
|
|
429
433
|
|
|
430
434
|
const raw = response.content[0].text.trim();
|
|
431
435
|
let parsed;
|
|
436
|
+
let aiDegraded = false;
|
|
432
437
|
try {
|
|
433
438
|
parsed = JSON.parse(raw);
|
|
434
439
|
} catch(e) {
|
|
@@ -441,6 +446,7 @@ Respond ONLY with a JSON object — no markdown, no explanation outside the JSON
|
|
|
441
446
|
recommended_local_models: ['llama3.2:8b', 'mistral-7b'],
|
|
442
447
|
cloud_justified_reason: null
|
|
443
448
|
};
|
|
449
|
+
aiDegraded = true;
|
|
444
450
|
}
|
|
445
451
|
|
|
446
452
|
const _rLms = {
|
|
@@ -448,6 +454,8 @@ Respond ONLY with a JSON object — no markdown, no explanation outside the JSON
|
|
|
448
454
|
task_quality_threshold: quality,
|
|
449
455
|
data_sensitivity: sensitivity,
|
|
450
456
|
analysis_type: 'AI-powered cost routing — NOT a simple lookup',
|
|
457
|
+
verdict_ttl: VERDICT_TTL.check_local_viability,
|
|
458
|
+
data_source_status: aiDegraded ? 'degraded' : 'full',
|
|
451
459
|
checked_at: nowISO(),
|
|
452
460
|
_disclaimer: LEGAL_DISCLAIMER
|
|
453
461
|
};
|
|
@@ -707,6 +715,7 @@ const server = createServer(async (req, res) => {
|
|
|
707
715
|
stats.free_tier_calls_by_ip[clientIp][month] = Math.max(0, current - TRIAL_EXTENSION_CALLS);
|
|
708
716
|
trialExtensions.set(emailKey, { name, email, use_case: use_case || '', ip: clientIp, granted_at: nowISO() });
|
|
709
717
|
saveStats();
|
|
718
|
+
await redisSet(REDIS_PREFIX + ':trial:' + email.toLowerCase().trim(), { name, email, use_case: use_case || '', ip: clientIp, timestamp: nowISO(), server: 'local-model-suitability-mcp' });
|
|
710
719
|
// 24h follow-up record -- processed by /process-trial-followups (fleet cron)
|
|
711
720
|
await redisSet(REDIS_PREFIX + ':followup:' + email.toLowerCase().trim(), { email, name, server: 'local-model-suitability-mcp', granted_at: nowISO(), sent: false });
|
|
712
721
|
const sendTrialEmail = async (to, subject, html) => {
|
|
@@ -890,9 +899,11 @@ const server = createServer(async (req, res) => {
|
|
|
890
899
|
redisIncr(LIFETIME_CALLS_REDIS_KEY).catch(() => {});
|
|
891
900
|
logCall('check_local_viability', access.tier, clientIp);
|
|
892
901
|
appendSessionLog(clientIp, 'check_local_viability').catch((e) => console.error('[SessionLog] appendSessionLog failed:', e));
|
|
902
|
+
const callsRemaining = access.tier === 'free' ? Math.max(0, FREE_TIER_LIMIT - getFreeTierCount(clientIp)) : 'unlimited';
|
|
893
903
|
|
|
894
904
|
try {
|
|
895
905
|
const result = await checkLocalViability(task, quality_threshold, data_sensitivity);
|
|
906
|
+
result.calls_remaining = callsRemaining;
|
|
896
907
|
|
|
897
908
|
// Partial response for free tier
|
|
898
909
|
if (access.tier === 'free') {
|
|
@@ -902,6 +913,7 @@ const server = createServer(async (req, res) => {
|
|
|
902
913
|
reason: result.reason,
|
|
903
914
|
analysis_type: result.analysis_type,
|
|
904
915
|
checked_at: result.checked_at,
|
|
916
|
+
calls_remaining: result.calls_remaining,
|
|
905
917
|
_disclaimer: result._disclaimer,
|
|
906
918
|
upgrade_url: PRO_UPGRADE_URL
|
|
907
919
|
};
|
|
@@ -977,6 +989,7 @@ function setupStdio() {
|
|
|
977
989
|
} else {
|
|
978
990
|
try {
|
|
979
991
|
const result = await checkLocalViability(task, quality_threshold, data_sensitivity);
|
|
992
|
+
result.calls_remaining = 'unlimited';
|
|
980
993
|
response = { jsonrpc: '2.0', id: req.id, result: { content: [{ type: 'text', text: JSON.stringify(result) }] } };
|
|
981
994
|
} catch(e) {
|
|
982
995
|
response = { jsonrpc: '2.0', id: req.id, result: { content: [{ type: 'text', text: JSON.stringify({ error: e.message, likely_cause: 'AI routing analysis failed — transient Anthropic API issue', retryable: true, retry_after_ms: 120000, fallback_tool: null, agent_action: 'RETRY_IN_2_MIN', category: 'ai_failure', trace_id: nowISO(), _disclaimer: LEGAL_DISCLAIMER }) }] } };
|