@k-system/tickr-mcp 1.0.0 → 1.0.2
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/debug-logger.js +13 -0
- package/dist/server.js +44 -14
- package/package.json +1 -1
package/dist/debug-logger.js
CHANGED
|
@@ -4,6 +4,7 @@ import { homedir } from "node:os";
|
|
|
4
4
|
const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
5
5
|
const MAX_PARAMS_LENGTH = 200;
|
|
6
6
|
let _debugEnabled = null;
|
|
7
|
+
let _versionLogged = false;
|
|
7
8
|
/**
|
|
8
9
|
* Zkontroluje zda je debug logování zapnuté (TICKR_DEBUG=true|1).
|
|
9
10
|
*/
|
|
@@ -36,6 +37,18 @@ export function debugLog(toolName, params, responseStatus, durationMs) {
|
|
|
36
37
|
catch {
|
|
37
38
|
// Soubor ještě neexistuje — ok
|
|
38
39
|
}
|
|
40
|
+
// Zalogovat verzi MCP serveru při prvním volání
|
|
41
|
+
if (!_versionLogged) {
|
|
42
|
+
_versionLogged = true;
|
|
43
|
+
try {
|
|
44
|
+
const pkg = JSON.parse(require("node:fs").readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
45
|
+
const timestamp = new Date().toISOString();
|
|
46
|
+
appendFileSync(logPath, `[${timestamp}] [init] MCP server v${pkg.version} started (debug=true)\n`, "utf-8");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// package.json nenalezen — pokračuj bez verze
|
|
50
|
+
}
|
|
51
|
+
}
|
|
39
52
|
// Zkrátit params na max 200 znaků
|
|
40
53
|
let paramsStr = JSON.stringify(params);
|
|
41
54
|
if (paramsStr.length > MAX_PARAMS_LENGTH) {
|
package/dist/server.js
CHANGED
|
@@ -96,9 +96,10 @@ export async function startServer() {
|
|
|
96
96
|
name: "tickr",
|
|
97
97
|
version: "0.1.5",
|
|
98
98
|
});
|
|
99
|
-
// Dedup cache pro create operace — eliminuje race condition z double dispatch (4ms gap)
|
|
99
|
+
// Dedup cache pro create operace — eliminuje race condition z double dispatch (1-4ms gap)
|
|
100
100
|
const MUTATING_PREFIXES = ["create_", "add_", "triage_accept", "triage_reject"];
|
|
101
101
|
const dedupCache = new Map();
|
|
102
|
+
const pendingOps = new Map();
|
|
102
103
|
const DEDUP_TTL_MS = 30_000; // 30 sekund
|
|
103
104
|
// Obalení server.tool — dedup cache (vždy) + debug logging (volitelné)
|
|
104
105
|
{
|
|
@@ -113,29 +114,58 @@ export async function startServer() {
|
|
|
113
114
|
// Dedup pro mutující operace
|
|
114
115
|
if (isMutating) {
|
|
115
116
|
const hash = name + ":" + JSON.stringify(params);
|
|
117
|
+
// 1. Check completed cache
|
|
116
118
|
const cached = dedupCache.get(hash);
|
|
117
119
|
if (cached && Date.now() - cached.timestamp < DEDUP_TTL_MS) {
|
|
118
120
|
if (debug)
|
|
119
121
|
debugLog(name, (params ?? {}), "dedup-cached", 0);
|
|
120
122
|
return cached.result;
|
|
121
123
|
}
|
|
124
|
+
// 2. Check if another call with same params is already in-flight
|
|
125
|
+
const pending = pendingOps.get(hash);
|
|
126
|
+
if (pending) {
|
|
127
|
+
if (debug)
|
|
128
|
+
debugLog(name, (params ?? {}), "dedup-pending", 0);
|
|
129
|
+
return pending;
|
|
130
|
+
}
|
|
131
|
+
// 3. Mark as pending BEFORE starting async work — prevents race condition
|
|
132
|
+
const promise = (async () => {
|
|
133
|
+
const start = Date.now();
|
|
134
|
+
try {
|
|
135
|
+
const result = await originalHandler(params, extra);
|
|
136
|
+
// Cache úspěšné mutující výsledky
|
|
137
|
+
if (!result.isError) {
|
|
138
|
+
dedupCache.set(hash, { result, timestamp: Date.now() });
|
|
139
|
+
if (dedupCache.size > 100) {
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
for (const [k, v] of dedupCache) {
|
|
142
|
+
if (now - v.timestamp > DEDUP_TTL_MS)
|
|
143
|
+
dedupCache.delete(k);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (debug)
|
|
148
|
+
debugLog(name, (params ?? {}), result.isError ? "error" : "ok", Date.now() - start);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
if (debug)
|
|
153
|
+
debugLog(name, (params ?? {}), "error", Date.now() - start);
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
})();
|
|
157
|
+
pendingOps.set(hash, promise);
|
|
158
|
+
try {
|
|
159
|
+
return await promise;
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
pendingOps.delete(hash);
|
|
163
|
+
}
|
|
122
164
|
}
|
|
165
|
+
// Non-mutating operace — bez dedup
|
|
123
166
|
const start = Date.now();
|
|
124
167
|
try {
|
|
125
168
|
const result = await originalHandler(params, extra);
|
|
126
|
-
// Cache úspěšné mutující výsledky
|
|
127
|
-
if (isMutating && !result.isError) {
|
|
128
|
-
const hash = name + ":" + JSON.stringify(params);
|
|
129
|
-
dedupCache.set(hash, { result, timestamp: Date.now() });
|
|
130
|
-
// Cleanup starých záznamů
|
|
131
|
-
if (dedupCache.size > 100) {
|
|
132
|
-
const now = Date.now();
|
|
133
|
-
for (const [k, v] of dedupCache) {
|
|
134
|
-
if (now - v.timestamp > DEDUP_TTL_MS)
|
|
135
|
-
dedupCache.delete(k);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
169
|
if (debug)
|
|
140
170
|
debugLog(name, (params ?? {}), result.isError ? "error" : "ok", Date.now() - start);
|
|
141
171
|
return result;
|