@kaitranntt/ccs 7.31.1 → 7.32.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/cliproxy/cliproxy-executor.d.ts.map +1 -1
- package/dist/cliproxy/cliproxy-executor.js +60 -12
- package/dist/cliproxy/cliproxy-executor.js.map +1 -1
- package/dist/cliproxy/index.d.ts +6 -0
- package/dist/cliproxy/index.d.ts.map +1 -1
- package/dist/cliproxy/index.js +11 -1
- package/dist/cliproxy/index.js.map +1 -1
- package/dist/cliproxy/tool-name-mapper.d.ts +103 -0
- package/dist/cliproxy/tool-name-mapper.d.ts.map +1 -0
- package/dist/cliproxy/tool-name-mapper.js +149 -0
- package/dist/cliproxy/tool-name-mapper.js.map +1 -0
- package/dist/cliproxy/tool-name-sanitizer.d.ts +65 -0
- package/dist/cliproxy/tool-name-sanitizer.d.ts.map +1 -0
- package/dist/cliproxy/tool-name-sanitizer.js +118 -0
- package/dist/cliproxy/tool-name-sanitizer.js.map +1 -0
- package/dist/cliproxy/tool-sanitization-proxy.d.ts +70 -0
- package/dist/cliproxy/tool-sanitization-proxy.d.ts.map +1 -0
- package/dist/cliproxy/tool-sanitization-proxy.js +453 -0
- package/dist/cliproxy/tool-sanitization-proxy.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Name Sanitizer
|
|
3
|
+
*
|
|
4
|
+
* Sanitizes MCP tool names to comply with Gemini API constraints:
|
|
5
|
+
* - Max 64 characters
|
|
6
|
+
* - Must start with letter or underscore
|
|
7
|
+
* - Only a-z A-Z 0-9 _ . : - / allowed
|
|
8
|
+
*
|
|
9
|
+
* Strategies:
|
|
10
|
+
* 1. Remove duplicate segments (e.g., gitmcp__foo__foo → gitmcp__foo)
|
|
11
|
+
* 2. Smart truncate with hash suffix if still >64 chars
|
|
12
|
+
*
|
|
13
|
+
* Note: Hash collision risk is ~1 in 16M with 6-char MD5 prefix.
|
|
14
|
+
* Acceptable for typical MCP tool counts (<1000 tools).
|
|
15
|
+
*/
|
|
16
|
+
/** Maximum tool name length allowed by Gemini API */
|
|
17
|
+
export declare const GEMINI_MAX_TOOL_NAME_LENGTH = 64;
|
|
18
|
+
/** Result of sanitization operation */
|
|
19
|
+
export interface SanitizeResult {
|
|
20
|
+
/** The sanitized tool name */
|
|
21
|
+
sanitized: string;
|
|
22
|
+
/** Whether the name was changed */
|
|
23
|
+
changed: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a tool name is valid for Gemini API.
|
|
27
|
+
*
|
|
28
|
+
* Requirements:
|
|
29
|
+
* - Length <= 64 characters
|
|
30
|
+
* - Starts with letter or underscore
|
|
31
|
+
* - Contains only valid characters: a-z A-Z 0-9 _ . : -
|
|
32
|
+
*/
|
|
33
|
+
export declare function isValidToolName(name: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Remove consecutive duplicate segments separated by '__'.
|
|
36
|
+
*
|
|
37
|
+
* Examples:
|
|
38
|
+
* - 'gitmcp__foo__foo' → 'gitmcp__foo'
|
|
39
|
+
* - 'a__b__c__b__c' → 'a__b__c'
|
|
40
|
+
* - 'no_dupes' → 'no_dupes'
|
|
41
|
+
*/
|
|
42
|
+
export declare function removeDuplicateSegments(name: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Smart truncate a name to fit within maxLen.
|
|
45
|
+
* Preserves start of name and appends hash suffix for uniqueness.
|
|
46
|
+
*
|
|
47
|
+
* Format: <prefix>_<6-char-hash>
|
|
48
|
+
*
|
|
49
|
+
* @param name The name to truncate
|
|
50
|
+
* @param maxLen Maximum allowed length (default: 64)
|
|
51
|
+
*/
|
|
52
|
+
export declare function smartTruncate(name: string, maxLen?: number): string;
|
|
53
|
+
/**
|
|
54
|
+
* Sanitize a tool name to comply with Gemini API constraints.
|
|
55
|
+
*
|
|
56
|
+
* Process:
|
|
57
|
+
* 1. Remove duplicate segments (always, as duplicates are likely unintentional)
|
|
58
|
+
* 2. Truncate with hash if >64 chars
|
|
59
|
+
* 3. Return result with changed flag
|
|
60
|
+
*
|
|
61
|
+
* @param name The original tool name
|
|
62
|
+
* @returns Sanitization result with sanitized name and changed flag
|
|
63
|
+
*/
|
|
64
|
+
export declare function sanitizeToolName(name: string): SanitizeResult;
|
|
65
|
+
//# sourceMappingURL=tool-name-sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-name-sanitizer.d.ts","sourceRoot":"","sources":["../../src/cliproxy/tool-name-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,qDAAqD;AACrD,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAK9C,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQrD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAY5D;AAUD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAoC,GAAG,MAAM,CAWhG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAmB7D"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool Name Sanitizer
|
|
4
|
+
*
|
|
5
|
+
* Sanitizes MCP tool names to comply with Gemini API constraints:
|
|
6
|
+
* - Max 64 characters
|
|
7
|
+
* - Must start with letter or underscore
|
|
8
|
+
* - Only a-z A-Z 0-9 _ . : - / allowed
|
|
9
|
+
*
|
|
10
|
+
* Strategies:
|
|
11
|
+
* 1. Remove duplicate segments (e.g., gitmcp__foo__foo → gitmcp__foo)
|
|
12
|
+
* 2. Smart truncate with hash suffix if still >64 chars
|
|
13
|
+
*
|
|
14
|
+
* Note: Hash collision risk is ~1 in 16M with 6-char MD5 prefix.
|
|
15
|
+
* Acceptable for typical MCP tool counts (<1000 tools).
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.sanitizeToolName = exports.smartTruncate = exports.removeDuplicateSegments = exports.isValidToolName = exports.GEMINI_MAX_TOOL_NAME_LENGTH = void 0;
|
|
19
|
+
const crypto_1 = require("crypto");
|
|
20
|
+
/** Maximum tool name length allowed by Gemini API */
|
|
21
|
+
exports.GEMINI_MAX_TOOL_NAME_LENGTH = 64;
|
|
22
|
+
/** Valid characters pattern for Gemini tool names */
|
|
23
|
+
const VALID_CHARS_REGEX = /^[a-zA-Z_][a-zA-Z0-9_.:/-]*$/;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a tool name is valid for Gemini API.
|
|
26
|
+
*
|
|
27
|
+
* Requirements:
|
|
28
|
+
* - Length <= 64 characters
|
|
29
|
+
* - Starts with letter or underscore
|
|
30
|
+
* - Contains only valid characters: a-z A-Z 0-9 _ . : -
|
|
31
|
+
*/
|
|
32
|
+
function isValidToolName(name) {
|
|
33
|
+
if (!name || name.length === 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (name.length > exports.GEMINI_MAX_TOOL_NAME_LENGTH) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return VALID_CHARS_REGEX.test(name);
|
|
40
|
+
}
|
|
41
|
+
exports.isValidToolName = isValidToolName;
|
|
42
|
+
/**
|
|
43
|
+
* Remove consecutive duplicate segments separated by '__'.
|
|
44
|
+
*
|
|
45
|
+
* Examples:
|
|
46
|
+
* - 'gitmcp__foo__foo' → 'gitmcp__foo'
|
|
47
|
+
* - 'a__b__c__b__c' → 'a__b__c'
|
|
48
|
+
* - 'no_dupes' → 'no_dupes'
|
|
49
|
+
*/
|
|
50
|
+
function removeDuplicateSegments(name) {
|
|
51
|
+
const segments = name.split('__');
|
|
52
|
+
const deduped = [];
|
|
53
|
+
for (const segment of segments) {
|
|
54
|
+
// Only add if different from previous segment
|
|
55
|
+
if (deduped.length === 0 || deduped[deduped.length - 1] !== segment) {
|
|
56
|
+
deduped.push(segment);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return deduped.join('__');
|
|
60
|
+
}
|
|
61
|
+
exports.removeDuplicateSegments = removeDuplicateSegments;
|
|
62
|
+
/**
|
|
63
|
+
* Generate a short hash from a string for truncation suffix.
|
|
64
|
+
* Uses first 6 characters of MD5 hash (16M combinations).
|
|
65
|
+
*/
|
|
66
|
+
function generateShortHash(input) {
|
|
67
|
+
return (0, crypto_1.createHash)('md5').update(input).digest('hex').slice(0, 6);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Smart truncate a name to fit within maxLen.
|
|
71
|
+
* Preserves start of name and appends hash suffix for uniqueness.
|
|
72
|
+
*
|
|
73
|
+
* Format: <prefix>_<6-char-hash>
|
|
74
|
+
*
|
|
75
|
+
* @param name The name to truncate
|
|
76
|
+
* @param maxLen Maximum allowed length (default: 64)
|
|
77
|
+
*/
|
|
78
|
+
function smartTruncate(name, maxLen = exports.GEMINI_MAX_TOOL_NAME_LENGTH) {
|
|
79
|
+
if (name.length <= maxLen) {
|
|
80
|
+
return name;
|
|
81
|
+
}
|
|
82
|
+
// Format: prefix + '_' + 6-char hash = 7 chars for suffix
|
|
83
|
+
const hash = generateShortHash(name);
|
|
84
|
+
const prefixLen = maxLen - 7; // 7 = '_' (1) + hash (6)
|
|
85
|
+
const prefix = name.slice(0, prefixLen);
|
|
86
|
+
return `${prefix}_${hash}`;
|
|
87
|
+
}
|
|
88
|
+
exports.smartTruncate = smartTruncate;
|
|
89
|
+
/**
|
|
90
|
+
* Sanitize a tool name to comply with Gemini API constraints.
|
|
91
|
+
*
|
|
92
|
+
* Process:
|
|
93
|
+
* 1. Remove duplicate segments (always, as duplicates are likely unintentional)
|
|
94
|
+
* 2. Truncate with hash if >64 chars
|
|
95
|
+
* 3. Return result with changed flag
|
|
96
|
+
*
|
|
97
|
+
* @param name The original tool name
|
|
98
|
+
* @returns Sanitization result with sanitized name and changed flag
|
|
99
|
+
*/
|
|
100
|
+
function sanitizeToolName(name) {
|
|
101
|
+
// Step 1: Always try to remove duplicate segments
|
|
102
|
+
// Duplicates like gitmcp__foo__foo are likely unintentional from MCP naming
|
|
103
|
+
let sanitized = removeDuplicateSegments(name);
|
|
104
|
+
// Step 2: Truncate if still too long
|
|
105
|
+
if (sanitized.length > exports.GEMINI_MAX_TOOL_NAME_LENGTH) {
|
|
106
|
+
sanitized = smartTruncate(sanitized);
|
|
107
|
+
}
|
|
108
|
+
// Step 3: If still invalid after sanitization, apply truncation to original
|
|
109
|
+
if (!isValidToolName(sanitized)) {
|
|
110
|
+
sanitized = smartTruncate(name);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
sanitized,
|
|
114
|
+
changed: sanitized !== name,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
exports.sanitizeToolName = sanitizeToolName;
|
|
118
|
+
//# sourceMappingURL=tool-name-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-name-sanitizer.js","sourceRoot":"","sources":["../../src/cliproxy/tool-name-sanitizer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH,mCAAoC;AAEpC,qDAAqD;AACxC,QAAA,2BAA2B,GAAG,EAAE,CAAC;AAE9C,qDAAqD;AACrD,MAAM,iBAAiB,GAAG,8BAA8B,CAAC;AAUzD;;;;;;;GAOG;AACH,SAAgB,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,mCAA2B,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AARD,0CAQC;AAED;;;;;;;GAOG;AACH,SAAgB,uBAAuB,CAAC,IAAY;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,8CAA8C;QAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAZD,0DAYC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,IAAA,mBAAU,EAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,IAAY,EAAE,SAAiB,mCAA2B;IACtF,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0DAA0D;IAC1D,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,yBAAyB;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAExC,OAAO,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;AAC7B,CAAC;AAXD,sCAWC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,kDAAkD;IAClD,4EAA4E;IAC5E,IAAI,SAAS,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAE9C,qCAAqC;IACrC,IAAI,SAAS,CAAC,MAAM,GAAG,mCAA2B,EAAE,CAAC;QACnD,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,4EAA4E;IAC5E,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,SAAS;QACT,OAAO,EAAE,SAAS,KAAK,IAAI;KAC5B,CAAC;AACJ,CAAC;AAnBD,4CAmBC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Sanitization Proxy
|
|
3
|
+
*
|
|
4
|
+
* HTTP proxy that intercepts Claude CLI → CLIProxy requests to:
|
|
5
|
+
* 1. Sanitize MCP tool names exceeding Gemini's 64-char limit
|
|
6
|
+
* 2. Forward sanitized requests to upstream
|
|
7
|
+
* 3. Restore original names in responses
|
|
8
|
+
*
|
|
9
|
+
* Follows CodexReasoningProxy pattern for consistency.
|
|
10
|
+
*/
|
|
11
|
+
export interface ToolSanitizationProxyConfig {
|
|
12
|
+
/** Upstream CLIProxy URL */
|
|
13
|
+
upstreamBaseUrl: string;
|
|
14
|
+
/** Enable verbose logging */
|
|
15
|
+
verbose?: boolean;
|
|
16
|
+
/** Log warnings when sanitization occurs */
|
|
17
|
+
warnOnSanitize?: boolean;
|
|
18
|
+
/** Request timeout in milliseconds */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class ToolSanitizationProxy {
|
|
22
|
+
private server;
|
|
23
|
+
private port;
|
|
24
|
+
private readonly config;
|
|
25
|
+
private readonly logFilePath;
|
|
26
|
+
private readonly debugMode;
|
|
27
|
+
constructor(config: ToolSanitizationProxyConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Initialize log file path and ensure directory exists.
|
|
30
|
+
*/
|
|
31
|
+
private initLogFile;
|
|
32
|
+
/**
|
|
33
|
+
* Write log entry to file (always) and console (if CCS_DEBUG=1).
|
|
34
|
+
*/
|
|
35
|
+
private writeLog;
|
|
36
|
+
private log;
|
|
37
|
+
private warn;
|
|
38
|
+
/**
|
|
39
|
+
* Start the proxy server on an ephemeral port.
|
|
40
|
+
* @returns The assigned port number
|
|
41
|
+
*/
|
|
42
|
+
start(): Promise<number>;
|
|
43
|
+
/**
|
|
44
|
+
* Stop the proxy server.
|
|
45
|
+
*/
|
|
46
|
+
stop(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get the port the proxy is listening on.
|
|
49
|
+
*/
|
|
50
|
+
getPort(): number | null;
|
|
51
|
+
private readBody;
|
|
52
|
+
private handleRequest;
|
|
53
|
+
private buildForwardHeaders;
|
|
54
|
+
private getRequestFn;
|
|
55
|
+
private forwardRaw;
|
|
56
|
+
/**
|
|
57
|
+
* Forward JSON request and buffer response for tool name restoration.
|
|
58
|
+
*/
|
|
59
|
+
private forwardJsonBuffered;
|
|
60
|
+
/**
|
|
61
|
+
* Forward JSON request and stream response with tool name restoration.
|
|
62
|
+
* Handles SSE (Server-Sent Events) format.
|
|
63
|
+
*/
|
|
64
|
+
private forwardJsonStreaming;
|
|
65
|
+
/**
|
|
66
|
+
* Process a single SSE event, restoring tool names if present.
|
|
67
|
+
*/
|
|
68
|
+
private processSSEEvent;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=tool-sanitization-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-sanitization-proxy.d.ts","sourceRoot":"","sources":["../../src/cliproxy/tool-sanitization-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,MAAM,WAAW,2BAA2B;IAC1C,4BAA4B;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;gBAExB,MAAM,EAAE,2BAA2B;IAW/C;;OAEG;IACH,OAAO,CAAC,WAAW;IAoBnB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,GAAG;IAMX,OAAO,CAAC,IAAI;IAMZ;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAmB9B;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB,OAAO,CAAC,QAAQ;YAqBF,aAAa;IA6E3B,OAAO,CAAC,mBAAmB;IAmC3B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IA+BlB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAsE3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsE5B;;OAEG;IACH,OAAO,CAAC,eAAe;CAqDxB"}
|