mcp-compression-proxy 1.0.2 → 1.1.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/CHANGELOG.md +22 -0
- package/README.md +227 -659
- package/dist/cli/commands.d.ts +18 -0
- package/dist/cli/commands.js +152 -13
- package/dist/cli/daemon.js +133 -49
- package/dist/cli/index.js +147 -7
- package/dist/cli/payload-interceptor.d.ts +71 -2
- package/dist/cli/payload-interceptor.js +214 -36
- package/dist/cli/runtime-mode.d.ts +3 -0
- package/dist/cli/runtime-mode.js +12 -0
- package/dist/cli/runtime-paths.d.ts +15 -0
- package/dist/cli/runtime-paths.js +26 -0
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.js +92 -1
- package/dist/config/schema.d.ts +90 -1
- package/dist/config/schema.js +95 -13
- package/dist/index.js +319 -28
- package/dist/mcp/call-script.d.ts +33 -0
- package/dist/mcp/call-script.js +153 -0
- package/dist/mcp/client-manager.d.ts +110 -27
- package/dist/mcp/client-manager.js +706 -83
- package/dist/mcp/tool-call-executor.d.ts +11 -0
- package/dist/mcp/tool-call-executor.js +94 -0
- package/dist/services/compression-cache.d.ts +18 -0
- package/dist/services/compression-cache.js +32 -0
- package/dist/services/session-manager.js +5 -0
- package/dist/services/stats-service.d.ts +4 -0
- package/dist/services/stats-service.js +18 -39
- package/dist/types/index.d.ts +59 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { Logger } from 'pino';
|
|
3
|
+
import type { MCPClientManager } from './client-manager.js';
|
|
4
|
+
export declare function matchesAuthenticationFailure(value: unknown, patterns: string[]): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Execute a backend tool call and recover once from configured authentication
|
|
7
|
+
* failures. Every auth failure invalidates the exact connection generation.
|
|
8
|
+
* Automatic replay is restricted to explicitly configured read-only tools.
|
|
9
|
+
*/
|
|
10
|
+
export declare function callToolWithAuthRecovery(manager: MCPClientManager, logger: Logger, serverName: string, toolName: string, args: Record<string, unknown>): Promise<CallToolResult>;
|
|
11
|
+
//# sourceMappingURL=tool-call-executor.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { CallToolResultSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { matchesIgnorePattern } from '../config/loader.js';
|
|
3
|
+
function searchableText(value) {
|
|
4
|
+
if (value instanceof Error) {
|
|
5
|
+
return `${value.name}: ${value.message}`;
|
|
6
|
+
}
|
|
7
|
+
if (typeof value === 'string') {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(value);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return String(value);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function isCallToolResult(value) {
|
|
18
|
+
return (typeof value === 'object' &&
|
|
19
|
+
value !== null &&
|
|
20
|
+
Array.isArray(value.content));
|
|
21
|
+
}
|
|
22
|
+
export function matchesAuthenticationFailure(value, patterns) {
|
|
23
|
+
if (patterns.length === 0)
|
|
24
|
+
return false;
|
|
25
|
+
const text = searchableText(value).toLowerCase();
|
|
26
|
+
return patterns.some((pattern) => {
|
|
27
|
+
const normalized = pattern.trim().toLowerCase();
|
|
28
|
+
return normalized.length > 0 && text.includes(normalized);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function isRetrySafe(serverName, toolName, patterns) {
|
|
32
|
+
return (matchesIgnorePattern(toolName, patterns) ||
|
|
33
|
+
matchesIgnorePattern(`${serverName}__${toolName}`, patterns));
|
|
34
|
+
}
|
|
35
|
+
async function attemptToolCall(manager, serverName, toolName, args, authErrorPatterns) {
|
|
36
|
+
let authFailure = false;
|
|
37
|
+
try {
|
|
38
|
+
const result = await manager.withClient(serverName, async ({ client, invalidate, markFailure }) => {
|
|
39
|
+
try {
|
|
40
|
+
const rawResult = await client.callTool({
|
|
41
|
+
name: toolName,
|
|
42
|
+
arguments: args,
|
|
43
|
+
}, CallToolResultSchema);
|
|
44
|
+
if (!isCallToolResult(rawResult)) {
|
|
45
|
+
throw new Error('Task-based MCP tool results are not supported by the proxy');
|
|
46
|
+
}
|
|
47
|
+
const callResult = rawResult;
|
|
48
|
+
if (matchesAuthenticationFailure(callResult, authErrorPatterns)) {
|
|
49
|
+
authFailure = true;
|
|
50
|
+
invalidate('auth-error');
|
|
51
|
+
}
|
|
52
|
+
else if (callResult.isError === true) {
|
|
53
|
+
markFailure(`Tool '${toolName}' reported an error`);
|
|
54
|
+
}
|
|
55
|
+
return callResult;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (matchesAuthenticationFailure(error, authErrorPatterns)) {
|
|
59
|
+
authFailure = true;
|
|
60
|
+
invalidate('auth-error');
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return { result, authFailure };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return { error, authFailure };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Execute a backend tool call and recover once from configured authentication
|
|
73
|
+
* failures. Every auth failure invalidates the exact connection generation.
|
|
74
|
+
* Automatic replay is restricted to explicitly configured read-only tools.
|
|
75
|
+
*/
|
|
76
|
+
export async function callToolWithAuthRecovery(manager, logger, serverName, toolName, args) {
|
|
77
|
+
const policy = manager.getAuthRecoveryPolicy(serverName);
|
|
78
|
+
const retrySafe = isRetrySafe(serverName, toolName, policy.authRetryTools);
|
|
79
|
+
const first = await attemptToolCall(manager, serverName, toolName, args, policy.authErrorPatterns);
|
|
80
|
+
if (!first.authFailure || !retrySafe) {
|
|
81
|
+
if (first.authFailure) {
|
|
82
|
+
logger.warn({ server: serverName, tool: toolName }, 'Authentication failure invalidated the MCP connection; tool was not replayed');
|
|
83
|
+
}
|
|
84
|
+
if ('error' in first)
|
|
85
|
+
throw first.error;
|
|
86
|
+
return first.result;
|
|
87
|
+
}
|
|
88
|
+
logger.warn({ server: serverName, tool: toolName }, 'Retrying read-only MCP tool after authentication recovery');
|
|
89
|
+
const second = await attemptToolCall(manager, serverName, toolName, args, policy.authErrorPatterns);
|
|
90
|
+
if ('error' in second)
|
|
91
|
+
throw second.error;
|
|
92
|
+
return second.result;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=tool-call-executor.js.map
|
|
@@ -65,6 +65,24 @@ export declare class CompressionCache {
|
|
|
65
65
|
* Check if a tool has compressed description
|
|
66
66
|
*/
|
|
67
67
|
hasCompressed(serverName: string, toolName: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Whether a cached compression was made from a description the backend no
|
|
70
|
+
* longer serves.
|
|
71
|
+
*
|
|
72
|
+
* Both sides must be present to say anything: entries cached before originals
|
|
73
|
+
* were recorded have no baseline (see `missingOriginals` in the metrics), and
|
|
74
|
+
* a backend that reports no description gives nothing to compare. Treating
|
|
75
|
+
* either as stale would put those tools into a recompression loop they could
|
|
76
|
+
* never exit.
|
|
77
|
+
*/
|
|
78
|
+
isStale(serverName: string, toolName: string, liveOriginal?: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Drop one tool's cached compression. Returns whether there was one.
|
|
81
|
+
*
|
|
82
|
+
* Persisting is left to the caller so a batch of invalidations costs a single
|
|
83
|
+
* disk write.
|
|
84
|
+
*/
|
|
85
|
+
invalidate(serverName: string, toolName: string): boolean;
|
|
68
86
|
/**
|
|
69
87
|
* Get original description if cached
|
|
70
88
|
*/
|
|
@@ -111,6 +111,38 @@ export class CompressionCache {
|
|
|
111
111
|
const key = this.getKey(serverName, toolName);
|
|
112
112
|
return !!this.cache[key]?.compressed;
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether a cached compression was made from a description the backend no
|
|
116
|
+
* longer serves.
|
|
117
|
+
*
|
|
118
|
+
* Both sides must be present to say anything: entries cached before originals
|
|
119
|
+
* were recorded have no baseline (see `missingOriginals` in the metrics), and
|
|
120
|
+
* a backend that reports no description gives nothing to compare. Treating
|
|
121
|
+
* either as stale would put those tools into a recompression loop they could
|
|
122
|
+
* never exit.
|
|
123
|
+
*/
|
|
124
|
+
isStale(serverName, toolName, liveOriginal) {
|
|
125
|
+
const entry = this.cache[this.getKey(serverName, toolName)];
|
|
126
|
+
if (!entry?.compressed)
|
|
127
|
+
return false;
|
|
128
|
+
if (!entry.original || !liveOriginal)
|
|
129
|
+
return false;
|
|
130
|
+
return entry.original !== liveOriginal;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Drop one tool's cached compression. Returns whether there was one.
|
|
134
|
+
*
|
|
135
|
+
* Persisting is left to the caller so a batch of invalidations costs a single
|
|
136
|
+
* disk write.
|
|
137
|
+
*/
|
|
138
|
+
invalidate(serverName, toolName) {
|
|
139
|
+
const key = this.getKey(serverName, toolName);
|
|
140
|
+
if (!this.cache[key])
|
|
141
|
+
return false;
|
|
142
|
+
delete this.cache[key];
|
|
143
|
+
this.logger.debug({ serverName, toolName }, 'Invalidated compressed description');
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
114
146
|
/**
|
|
115
147
|
* Get original description if cached
|
|
116
148
|
*/
|
|
@@ -102,6 +102,11 @@ export class SessionManager {
|
|
|
102
102
|
const session = this.sessions.get(sessionId);
|
|
103
103
|
if (!session)
|
|
104
104
|
return false;
|
|
105
|
+
// Reading counts as use. This runs for every tool on every tools/list, so
|
|
106
|
+
// without it a session that is only ever read - the normal case, since
|
|
107
|
+
// expand/collapse are one-off calls - ages out mid-conversation and its
|
|
108
|
+
// expanded tools silently collapse back to compressed descriptions.
|
|
109
|
+
session.lastAccessedAt = new Date().toISOString();
|
|
105
110
|
const toolKey = `${serverName}:${toolName}`;
|
|
106
111
|
return session.expandedTools.includes(toolKey);
|
|
107
112
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Logger } from 'pino';
|
|
2
2
|
import type { MCPClientManager } from '../mcp/client-manager.js';
|
|
3
|
+
import type { ConnectionLifecycleState } from '../types/index.js';
|
|
3
4
|
import type { CompressionCache } from './compression-cache.js';
|
|
4
5
|
import type { SessionManager } from './session-manager.js';
|
|
5
6
|
import type { ConfigResult } from '../config/loader.js';
|
|
@@ -7,6 +8,7 @@ type DetailLevel = 'summary' | 'full';
|
|
|
7
8
|
type ServerToolStats = {
|
|
8
9
|
name: string;
|
|
9
10
|
connected: boolean;
|
|
11
|
+
state?: ConnectionLifecycleState;
|
|
10
12
|
error?: string;
|
|
11
13
|
toolsTotal: number;
|
|
12
14
|
toolsCompressed: number;
|
|
@@ -65,6 +67,8 @@ export type LiveCoverage = {
|
|
|
65
67
|
totalTools: number;
|
|
66
68
|
compressedTools: number;
|
|
67
69
|
uncompressedTools: number;
|
|
70
|
+
/** Compressed from a description the backend has since changed. */
|
|
71
|
+
staleTools: number;
|
|
68
72
|
coveragePercent: number;
|
|
69
73
|
originalChars: number;
|
|
70
74
|
compressedChars: number;
|
|
@@ -31,8 +31,6 @@ export class StatsService {
|
|
|
31
31
|
const excludePatterns = config.excludePatterns || [];
|
|
32
32
|
const noCompressPatterns = config.noCompressPatterns || [];
|
|
33
33
|
const serverStatuses = this.clientManager.getServerStatuses();
|
|
34
|
-
const connectedClients = this.clientManager.getConnectedClients();
|
|
35
|
-
const connectedClientMap = new Map(connectedClients.map((c) => [c.name, c.client]));
|
|
36
34
|
const targetStatuses = serverFilter
|
|
37
35
|
? serverStatuses.filter((s) => s.name === serverFilter)
|
|
38
36
|
: serverStatuses;
|
|
@@ -41,41 +39,8 @@ export class StatsService {
|
|
|
41
39
|
}
|
|
42
40
|
const serverStats = [];
|
|
43
41
|
for (const status of targetStatuses) {
|
|
44
|
-
if (!status.connected) {
|
|
45
|
-
serverStats.push({
|
|
46
|
-
name: status.name,
|
|
47
|
-
connected: false,
|
|
48
|
-
error: status.lastError || 'Not connected',
|
|
49
|
-
toolsTotal: 0,
|
|
50
|
-
toolsCompressed: 0,
|
|
51
|
-
toolsUncompressed: 0,
|
|
52
|
-
toolsExcluded: 0,
|
|
53
|
-
coveragePercent: 0,
|
|
54
|
-
originalChars: 0,
|
|
55
|
-
compressedChars: 0,
|
|
56
|
-
estimatedTokensSaved: 0,
|
|
57
|
-
});
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
const client = connectedClientMap.get(status.name);
|
|
61
|
-
if (!client) {
|
|
62
|
-
serverStats.push({
|
|
63
|
-
name: status.name,
|
|
64
|
-
connected: false,
|
|
65
|
-
error: 'Client not available',
|
|
66
|
-
toolsTotal: 0,
|
|
67
|
-
toolsCompressed: 0,
|
|
68
|
-
toolsUncompressed: 0,
|
|
69
|
-
toolsExcluded: 0,
|
|
70
|
-
coveragePercent: 0,
|
|
71
|
-
originalChars: 0,
|
|
72
|
-
compressedChars: 0,
|
|
73
|
-
estimatedTokensSaved: 0,
|
|
74
|
-
});
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
42
|
try {
|
|
78
|
-
const result = await client.listTools();
|
|
43
|
+
const result = await this.clientManager.withClient(status.name, async ({ client }) => client.listTools());
|
|
79
44
|
const filtered = result.tools.filter((tool) => !matchesIgnorePattern(`${status.name}__${tool.name}`, excludePatterns));
|
|
80
45
|
const excludedCount = result.tools.length - filtered.length;
|
|
81
46
|
let toolsCompressed = 0;
|
|
@@ -100,6 +65,7 @@ export class StatsService {
|
|
|
100
65
|
serverStats.push({
|
|
101
66
|
name: status.name,
|
|
102
67
|
connected: true,
|
|
68
|
+
state: 'ready',
|
|
103
69
|
toolsTotal,
|
|
104
70
|
toolsCompressed,
|
|
105
71
|
toolsUncompressed,
|
|
@@ -115,7 +81,9 @@ export class StatsService {
|
|
|
115
81
|
this.logger.warn({ server: status.name, error: message }, 'Failed to list tools for stats');
|
|
116
82
|
serverStats.push({
|
|
117
83
|
name: status.name,
|
|
118
|
-
connected:
|
|
84
|
+
connected: false,
|
|
85
|
+
state: this.clientManager.getServerStatuses()
|
|
86
|
+
.find((current) => current.name === status.name)?.state,
|
|
119
87
|
error: message,
|
|
120
88
|
toolsTotal: 0,
|
|
121
89
|
toolsCompressed: 0,
|
|
@@ -137,8 +105,8 @@ export class StatsService {
|
|
|
137
105
|
const payload = {
|
|
138
106
|
summary: {
|
|
139
107
|
serversConfigured: config.servers?.length || serverStatuses.length,
|
|
140
|
-
serversConnected:
|
|
141
|
-
serversWithErrors:
|
|
108
|
+
serversConnected: serverStats.filter((s) => s.connected).length,
|
|
109
|
+
serversWithErrors: serverStats.filter((s) => !s.connected || s.error).length,
|
|
142
110
|
toolsTotal: aggregateTotalTools,
|
|
143
111
|
toolsCompressed: aggregateCompressed,
|
|
144
112
|
toolsUncompressed: Math.max(aggregateTotalTools - aggregateCompressed, 0),
|
|
@@ -183,6 +151,7 @@ export class StatsService {
|
|
|
183
151
|
*/
|
|
184
152
|
computeCoverage(tools) {
|
|
185
153
|
let compressedTools = 0;
|
|
154
|
+
let staleTools = 0;
|
|
186
155
|
let originalChars = 0;
|
|
187
156
|
let compressedChars = 0;
|
|
188
157
|
let latestCompressedAt;
|
|
@@ -194,6 +163,11 @@ export class StatsService {
|
|
|
194
163
|
if (compressed !== undefined) {
|
|
195
164
|
compressedTools += 1;
|
|
196
165
|
compressedChars += compressed.length;
|
|
166
|
+
// The live description is already in hand here, so staleness costs no
|
|
167
|
+
// extra listTools call - preserve that when touching this loop.
|
|
168
|
+
if (this.compressionCache.isStale(tool.serverName, tool.toolName, tool.description)) {
|
|
169
|
+
staleTools += 1;
|
|
170
|
+
}
|
|
197
171
|
}
|
|
198
172
|
else {
|
|
199
173
|
// Uncompressed tools still occupy their original description.
|
|
@@ -209,6 +183,7 @@ export class StatsService {
|
|
|
209
183
|
totalTools: tools.length,
|
|
210
184
|
compressedTools,
|
|
211
185
|
uncompressedTools: Math.max(tools.length - compressedTools, 0),
|
|
186
|
+
staleTools,
|
|
212
187
|
coveragePercent: this.coverage(compressedTools, tools.length),
|
|
213
188
|
originalChars,
|
|
214
189
|
compressedChars,
|
|
@@ -228,6 +203,10 @@ export class StatsService {
|
|
|
228
203
|
`${coverage.compressedTools}/${coverage.totalTools} compressed (${coverage.coveragePercent}%)`,
|
|
229
204
|
`${coverage.uncompressedTools} remaining`,
|
|
230
205
|
];
|
|
206
|
+
// Only when non-zero: this string ships on every tools/list.
|
|
207
|
+
if (coverage.staleTools > 0) {
|
|
208
|
+
parts.push(`${coverage.staleTools} stale`);
|
|
209
|
+
}
|
|
231
210
|
if (coverage.estimatedTokensSaved > 0) {
|
|
232
211
|
parts.push(`~${compact(coverage.estimatedTokensSaved)} tokens saved`);
|
|
233
212
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,22 +1,63 @@
|
|
|
1
1
|
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
2
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
3
|
+
/**
|
|
4
|
+
* A backend server, either spawned locally (`command`) or reached over HTTP
|
|
5
|
+
* (`url`). The two are mutually exclusive, enforced by the config schema.
|
|
6
|
+
*
|
|
7
|
+
* Flat optionals rather than a discriminated union: `initializeServers`
|
|
8
|
+
* applies defaults by spreading (`{...server, timeout: ...}`), and TypeScript
|
|
9
|
+
* cannot keep a union narrowed across that.
|
|
10
|
+
*/
|
|
3
11
|
export interface MCPServerConfig {
|
|
4
12
|
name: string;
|
|
5
|
-
command
|
|
13
|
+
command?: string;
|
|
6
14
|
args?: string[];
|
|
7
15
|
env?: Record<string, string>;
|
|
8
16
|
/** Which of the proxy's env vars to pass through. Defaults to all. */
|
|
9
17
|
inheritEnv?: boolean | string[];
|
|
18
|
+
/** Endpoint of a hosted MCP server. Mutually exclusive with `command`. */
|
|
19
|
+
url?: string;
|
|
20
|
+
/** Static headers sent with every request to `url`, e.g. Authorization. */
|
|
21
|
+
headers?: Record<string, string>;
|
|
10
22
|
enabled?: boolean;
|
|
11
23
|
timeout?: number;
|
|
24
|
+
/** Lazy recycle threshold. Reopen on the next use after this age. 0 disables. */
|
|
25
|
+
softMaxConnectionAgeSeconds?: number;
|
|
26
|
+
/** Absolute lifetime. Drain at this age and close after active calls finish. 0 disables. */
|
|
27
|
+
hardMaxConnectionAgeSeconds?: number;
|
|
28
|
+
/** Deprecated alias for softMaxConnectionAgeSeconds. */
|
|
29
|
+
maxConnectionAgeSeconds?: number;
|
|
30
|
+
/** Case-insensitive substrings that identify an authentication failure. */
|
|
31
|
+
authErrorPatterns?: string[];
|
|
32
|
+
/** Tool-name wildcard patterns that are safe to retry once after auth recovery. */
|
|
33
|
+
authRetryTools?: string[];
|
|
12
34
|
}
|
|
13
35
|
export interface MCPClientConnection {
|
|
14
36
|
name: string;
|
|
15
37
|
client: Client;
|
|
16
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Absent when the transport could not be built at all - a malformed `url`,
|
|
40
|
+
* say. Recording the failure still matters, so this cannot be required.
|
|
41
|
+
*/
|
|
42
|
+
transport?: Transport;
|
|
17
43
|
connected: boolean;
|
|
18
44
|
lastError?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The resolved config this connection was built from - defaults already
|
|
47
|
+
* merged in. A reconnect replays it verbatim instead of re-deriving the
|
|
48
|
+
* timeout and env policy, which would silently drift from the original.
|
|
49
|
+
*/
|
|
50
|
+
config: MCPServerConfig;
|
|
51
|
+
}
|
|
52
|
+
export interface ConnectionLifecycleDefaults {
|
|
53
|
+
softMaxConnectionAgeSeconds?: number;
|
|
54
|
+
hardMaxConnectionAgeSeconds?: number;
|
|
55
|
+
/** Deprecated alias for softMaxConnectionAgeSeconds. */
|
|
56
|
+
maxConnectionAgeSeconds?: number;
|
|
57
|
+
authErrorPatterns?: string[];
|
|
58
|
+
authRetryTools?: string[];
|
|
19
59
|
}
|
|
60
|
+
export type ConnectionLifecycleState = 'closed' | 'starting' | 'ready' | 'draining' | 'failed';
|
|
20
61
|
export interface AggregatedToolsResponse {
|
|
21
62
|
tools: Array<{
|
|
22
63
|
name: string;
|
|
@@ -44,6 +85,20 @@ export interface ServerStatus {
|
|
|
44
85
|
name: string;
|
|
45
86
|
connected: boolean;
|
|
46
87
|
lastError?: string;
|
|
88
|
+
state?: ConnectionLifecycleState;
|
|
89
|
+
activeCalls?: number;
|
|
90
|
+
drainingConnections?: number;
|
|
91
|
+
generation?: number;
|
|
92
|
+
connectedAt?: number;
|
|
93
|
+
lastUsedAt?: number;
|
|
94
|
+
lastAttemptAt?: number;
|
|
95
|
+
lastSuccessAt?: number;
|
|
96
|
+
connectionAgeSeconds?: number;
|
|
97
|
+
softMaxConnectionAgeSeconds?: number;
|
|
98
|
+
hardMaxConnectionAgeSeconds?: number;
|
|
99
|
+
recycleCount?: number;
|
|
100
|
+
authInvalidations?: number;
|
|
101
|
+
consecutiveFailures?: number;
|
|
47
102
|
}
|
|
48
103
|
export interface HealthResponse {
|
|
49
104
|
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
@@ -61,7 +116,7 @@ export interface ToolsQueryParams {
|
|
|
61
116
|
pattern?: string;
|
|
62
117
|
sessionId?: string;
|
|
63
118
|
}
|
|
64
|
-
export type IPCMethod = 'tools' | 'search' | 'info' | 'call' | 'stats' | 'daemon-status';
|
|
119
|
+
export type IPCMethod = 'tools' | 'search' | 'info' | 'call' | 'payload-read' | 'payload-find' | 'script' | 'stats' | 'daemon-status';
|
|
65
120
|
export interface IPCRequest {
|
|
66
121
|
id: string;
|
|
67
122
|
method: IPCMethod;
|
|
@@ -83,6 +138,7 @@ export interface CLIConfig {
|
|
|
83
138
|
export interface DaemonStatusResult {
|
|
84
139
|
running: boolean;
|
|
85
140
|
pid: number;
|
|
141
|
+
releaseId?: string;
|
|
86
142
|
uptime: number;
|
|
87
143
|
servers: ServerStatus[];
|
|
88
144
|
/**
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-compression-proxy",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server that aggregates tools from multiple MCP servers with LLM-based description compression. Reduces context consumption by 50-80% while maintaining full tool functionality.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"cli:start": "node dist/cli/index.js daemon start",
|
|
30
30
|
"cli:stop": "node dist/cli/index.js daemon stop",
|
|
31
31
|
"sync-version": "node scripts/sync-version.mjs",
|
|
32
|
+
"check:release-notes": "node scripts/check-release-notes.mjs",
|
|
32
33
|
"prepare": "husky || true",
|
|
33
34
|
"prepublishOnly": "npm run build && npm test"
|
|
34
35
|
},
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"url": "https://github.com/kdpa-llc/mcp-compression-proxy/issues"
|
|
72
73
|
},
|
|
73
74
|
"dependencies": {
|
|
74
|
-
"@modelcontextprotocol/sdk": "^1.0
|
|
75
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
75
76
|
"ajv": "^8.17.1",
|
|
76
77
|
"pino": "^10.3.1",
|
|
77
78
|
"pino-pretty": "^13.0.0"
|
|
@@ -81,10 +82,10 @@
|
|
|
81
82
|
"@commitlint/config-conventional": "^21.2.2",
|
|
82
83
|
"@eslint/js": "^10.0.1",
|
|
83
84
|
"@jest/globals": "^30.4.1",
|
|
84
|
-
"@semantic-release/changelog": "^
|
|
85
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
85
86
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
86
87
|
"@semantic-release/exec": "^7.1.0",
|
|
87
|
-
"@semantic-release/git": "^
|
|
88
|
+
"@semantic-release/git": "^11.0.1",
|
|
88
89
|
"@semantic-release/github": "^12.0.2",
|
|
89
90
|
"@semantic-release/npm": "^13.1.2",
|
|
90
91
|
"@semantic-release/release-notes-generator": "^14.1.0",
|