@theyahia/mcp-core 1.0.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/auth/index.d.ts +69 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +126 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/client.d.ts +71 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +188 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +138 -0
- package/dist/errors.js.map +1 -0
- package/dist/format.d.ts +27 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +83 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/logging.d.ts +22 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +73 -0
- package/dist/logging.js.map +1 -0
- package/dist/sanitize.d.ts +17 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +35 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/server.d.ts +39 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +187 -0
- package/dist/server.js.map +1 -0
- package/dist/testing/smoke.d.ts +37 -0
- package/dist/testing/smoke.d.ts.map +1 -0
- package/dist/testing/smoke.js +79 -0
- package/dist/testing/smoke.js.map +1 -0
- package/package.json +82 -0
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response formatting utilities.
|
|
3
|
+
*
|
|
4
|
+
* Key insight: CSV is 29% more token-efficient than JSON for tabular data.
|
|
5
|
+
* For 1000 rows, this saves ~13,800 tokens.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Formats an array of items for MCP tool responses.
|
|
9
|
+
* Uses CSV for concise lists (3+ items), JSON for detailed or small results.
|
|
10
|
+
* Adds truncation info and cursor hint when results exceed maxItems.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatResponse(items: unknown[], opts?: {
|
|
13
|
+
maxItems?: number;
|
|
14
|
+
format?: "concise" | "detailed";
|
|
15
|
+
cursorField?: string;
|
|
16
|
+
}): string;
|
|
17
|
+
/** Format kopecks to rubles with ₽ symbol (Russian locale) */
|
|
18
|
+
export declare function formatRUB(kopecks: number): string;
|
|
19
|
+
/** Format tiyins to sum with locale (Uzbek) */
|
|
20
|
+
export declare function formatUZS(tiyins: number): string;
|
|
21
|
+
/** Format tenge (Kazakh) */
|
|
22
|
+
export declare function formatKZT(tiyin: number): string;
|
|
23
|
+
/** Format ISO date to Russian long format */
|
|
24
|
+
export declare function formatDate(iso: string): string;
|
|
25
|
+
/** Format number with locale-aware separators */
|
|
26
|
+
export declare function formatNumber(value: number, locale?: string, decimals?: number): string;
|
|
27
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EAAE,EAChB,IAAI,GAAE;IACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GACL,MAAM,CA6BR;AAoBD,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,4BAA4B;AAC5B,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,iDAAiD;AACjD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,MAAM,SAAU,EAChB,QAAQ,SAAI,GACX,MAAM,CAKR"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response formatting utilities.
|
|
3
|
+
*
|
|
4
|
+
* Key insight: CSV is 29% more token-efficient than JSON for tabular data.
|
|
5
|
+
* For 1000 rows, this saves ~13,800 tokens.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Formats an array of items for MCP tool responses.
|
|
9
|
+
* Uses CSV for concise lists (3+ items), JSON for detailed or small results.
|
|
10
|
+
* Adds truncation info and cursor hint when results exceed maxItems.
|
|
11
|
+
*/
|
|
12
|
+
export function formatResponse(items, opts = {}) {
|
|
13
|
+
const { maxItems = 25, format = "concise", cursorField } = opts;
|
|
14
|
+
const total = items.length;
|
|
15
|
+
const page = items.slice(0, maxItems);
|
|
16
|
+
let header = "";
|
|
17
|
+
if (total > maxItems) {
|
|
18
|
+
const cursorHint = cursorField
|
|
19
|
+
? ` Используйте ${cursorField} последнего элемента как cursor для следующей страницы.`
|
|
20
|
+
: " Используйте offset/page для следующей страницы.";
|
|
21
|
+
header = `Показано ${maxItems} из ${total}.${cursorHint}\n`;
|
|
22
|
+
}
|
|
23
|
+
if (page.length === 0) {
|
|
24
|
+
return "Результатов не найдено.";
|
|
25
|
+
}
|
|
26
|
+
if (format === "concise" &&
|
|
27
|
+
page.length > 3 &&
|
|
28
|
+
typeof page[0] === "object" &&
|
|
29
|
+
page[0] !== null) {
|
|
30
|
+
return header + toCSV(page);
|
|
31
|
+
}
|
|
32
|
+
return (header + JSON.stringify(page, null, format === "detailed" ? 2 : 0));
|
|
33
|
+
}
|
|
34
|
+
function toCSV(items) {
|
|
35
|
+
const keys = Object.keys(items[0]);
|
|
36
|
+
const header = keys.join(",");
|
|
37
|
+
const rows = items.map((item) => keys
|
|
38
|
+
.map((k) => {
|
|
39
|
+
const val = item[k];
|
|
40
|
+
if (val === null || val === undefined)
|
|
41
|
+
return "";
|
|
42
|
+
const str = String(val);
|
|
43
|
+
return str.includes(",") || str.includes('"') || str.includes("\n")
|
|
44
|
+
? `"${str.replace(/"/g, '""')}"`
|
|
45
|
+
: str;
|
|
46
|
+
})
|
|
47
|
+
.join(","));
|
|
48
|
+
return [header, ...rows].join("\n");
|
|
49
|
+
}
|
|
50
|
+
/** Format kopecks to rubles with ₽ symbol (Russian locale) */
|
|
51
|
+
export function formatRUB(kopecks) {
|
|
52
|
+
return new Intl.NumberFormat("ru-RU", {
|
|
53
|
+
style: "currency",
|
|
54
|
+
currency: "RUB",
|
|
55
|
+
}).format(kopecks / 100);
|
|
56
|
+
}
|
|
57
|
+
/** Format tiyins to sum with locale (Uzbek) */
|
|
58
|
+
export function formatUZS(tiyins) {
|
|
59
|
+
return new Intl.NumberFormat("uz-UZ", {
|
|
60
|
+
style: "currency",
|
|
61
|
+
currency: "UZS",
|
|
62
|
+
maximumFractionDigits: 0,
|
|
63
|
+
}).format(tiyins / 100);
|
|
64
|
+
}
|
|
65
|
+
/** Format tenge (Kazakh) */
|
|
66
|
+
export function formatKZT(tiyin) {
|
|
67
|
+
return new Intl.NumberFormat("kk-KZ", {
|
|
68
|
+
style: "currency",
|
|
69
|
+
currency: "KZT",
|
|
70
|
+
}).format(tiyin / 100);
|
|
71
|
+
}
|
|
72
|
+
/** Format ISO date to Russian long format */
|
|
73
|
+
export function formatDate(iso) {
|
|
74
|
+
return new Intl.DateTimeFormat("ru-RU", { dateStyle: "long" }).format(new Date(iso));
|
|
75
|
+
}
|
|
76
|
+
/** Format number with locale-aware separators */
|
|
77
|
+
export function formatNumber(value, locale = "ru-RU", decimals = 2) {
|
|
78
|
+
return new Intl.NumberFormat(locale, {
|
|
79
|
+
minimumFractionDigits: decimals,
|
|
80
|
+
maximumFractionDigits: decimals,
|
|
81
|
+
}).format(value);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAgB,EAChB,OAII,EAAE;IAEN,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAChE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;QACrB,MAAM,UAAU,GAAG,WAAW;YAC5B,CAAC,CAAC,gBAAgB,WAAW,yDAAyD;YACtF,CAAC,CAAC,kDAAkD,CAAC;QACvD,MAAM,GAAG,YAAY,QAAQ,OAAO,KAAK,IAAI,UAAU,IAAI,CAAC;IAC9D,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,yBAAyB,CAAC;IACnC,CAAC;IAED,IACE,MAAM,KAAK,SAAS;QACpB,IAAI,CAAC,MAAM,GAAG,CAAC;QACf,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QAC3B,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAChB,CAAC;QACD,OAAO,MAAM,GAAG,KAAK,CAAC,IAAiC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,CACL,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACnE,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,KAAgC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC9B,IAAI;SACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjE,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;YAChC,CAAC,CAAC,GAAG,CAAC;IACV,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CACb,CAAC;IACF,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;QACpC,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;QACpC,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,KAAK;QACf,qBAAqB,EAAE,CAAC;KACzB,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;QACpC,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CACnE,IAAI,IAAI,CAAC,GAAG,CAAC,CACd,CAAC;AACJ,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,MAAM,GAAG,OAAO,EAChB,QAAQ,GAAG,CAAC;IAEZ,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;QACnC,qBAAqB,EAAE,QAAQ;QAC/B,qBAAqB,EAAE,QAAQ;KAChC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @theyahia/mcp-core — shared library for MCP servers
|
|
3
|
+
*
|
|
4
|
+
* Provides: error handling, formatting, logging, HTTP client,
|
|
5
|
+
* auth strategies, server factory with dual transport.
|
|
6
|
+
*/
|
|
7
|
+
export { createToolError, withErrorHandling } from "./errors.js";
|
|
8
|
+
export type { ErrorCategory, ApiErrorInfo } from "./errors.js";
|
|
9
|
+
export { formatResponse, formatRUB, formatUZS, formatKZT, formatDate, formatNumber, } from "./format.js";
|
|
10
|
+
export { createLogger } from "./logging.js";
|
|
11
|
+
export type { Logger, LogLevel } from "./logging.js";
|
|
12
|
+
export { BaseHttpClient, RateLimitedClient, TokenBucketLimiter, ApiError, } from "./client.js";
|
|
13
|
+
export type { BaseClientOptions, RequestOptions } from "./client.js";
|
|
14
|
+
export { ApiKeyStrategy, BasicAuthStrategy, OAuthStrategy, DualAuthStrategy, NoAuthStrategy, } from "./auth/index.js";
|
|
15
|
+
export type { AuthStrategy } from "./auth/index.js";
|
|
16
|
+
export { sanitizeApiResponse, truncateResponse } from "./sanitize.js";
|
|
17
|
+
export { runServer, startStdio, startHttp } from "./server.js";
|
|
18
|
+
export type { ServerConfig, HttpServerConfig } from "./server.js";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG/D,OAAO,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @theyahia/mcp-core — shared library for MCP servers
|
|
3
|
+
*
|
|
4
|
+
* Provides: error handling, formatting, logging, HTTP client,
|
|
5
|
+
* auth strategies, server factory with dual transport.
|
|
6
|
+
*/
|
|
7
|
+
// Errors
|
|
8
|
+
export { createToolError, withErrorHandling } from "./errors.js";
|
|
9
|
+
// Formatting
|
|
10
|
+
export { formatResponse, formatRUB, formatUZS, formatKZT, formatDate, formatNumber, } from "./format.js";
|
|
11
|
+
// Logging
|
|
12
|
+
export { createLogger } from "./logging.js";
|
|
13
|
+
// HTTP Client
|
|
14
|
+
export { BaseHttpClient, RateLimitedClient, TokenBucketLimiter, ApiError, } from "./client.js";
|
|
15
|
+
// Auth Strategies
|
|
16
|
+
export { ApiKeyStrategy, BasicAuthStrategy, OAuthStrategy, DualAuthStrategy, NoAuthStrategy, } from "./auth/index.js";
|
|
17
|
+
// Sanitization
|
|
18
|
+
export { sanitizeApiResponse, truncateResponse } from "./sanitize.js";
|
|
19
|
+
// Server Factory
|
|
20
|
+
export { runServer, startStdio, startHttp } from "./server.js";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,SAAS;AACT,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGjE,aAAa;AACb,OAAO,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc;AACd,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,GACT,MAAM,aAAa,CAAC;AAGrB,kBAAkB;AAClB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,eAAe;AACf,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtE,iBAAiB;AACjB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logging for MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL: When using stdio transport, stdout is reserved for JSON-RPC.
|
|
5
|
+
* ALL logs MUST go to stderr. console.log() will break the MCP protocol.
|
|
6
|
+
*
|
|
7
|
+
* This module provides a lightweight structured logger that writes to stderr
|
|
8
|
+
* with JSON format for easy parsing by log aggregators.
|
|
9
|
+
*/
|
|
10
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
11
|
+
export interface Logger {
|
|
12
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
13
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
14
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
15
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a structured logger for an MCP server.
|
|
19
|
+
* All output goes to stderr to avoid breaking stdio JSON-RPC.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createLogger(serverName: string): Logger;
|
|
22
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAgCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiCvD"}
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logging for MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL: When using stdio transport, stdout is reserved for JSON-RPC.
|
|
5
|
+
* ALL logs MUST go to stderr. console.log() will break the MCP protocol.
|
|
6
|
+
*
|
|
7
|
+
* This module provides a lightweight structured logger that writes to stderr
|
|
8
|
+
* with JSON format for easy parsing by log aggregators.
|
|
9
|
+
*/
|
|
10
|
+
const LEVEL_ORDER = {
|
|
11
|
+
debug: 0,
|
|
12
|
+
info: 1,
|
|
13
|
+
warn: 2,
|
|
14
|
+
error: 3,
|
|
15
|
+
};
|
|
16
|
+
const SENSITIVE_KEYS = new Set([
|
|
17
|
+
"token",
|
|
18
|
+
"password",
|
|
19
|
+
"secret",
|
|
20
|
+
"api_key",
|
|
21
|
+
"apikey",
|
|
22
|
+
"authorization",
|
|
23
|
+
"client_secret",
|
|
24
|
+
"access_token",
|
|
25
|
+
"refresh_token",
|
|
26
|
+
"private_key",
|
|
27
|
+
]);
|
|
28
|
+
/** Recursively mask sensitive fields in log data */
|
|
29
|
+
function maskSensitive(obj) {
|
|
30
|
+
const result = {};
|
|
31
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
32
|
+
if (SENSITIVE_KEYS.has(key.toLowerCase())) {
|
|
33
|
+
result[key] = "***";
|
|
34
|
+
}
|
|
35
|
+
else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
36
|
+
result[key] = maskSensitive(value);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
result[key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a structured logger for an MCP server.
|
|
46
|
+
* All output goes to stderr to avoid breaking stdio JSON-RPC.
|
|
47
|
+
*/
|
|
48
|
+
export function createLogger(serverName) {
|
|
49
|
+
const minLevel = process.env.LOG_LEVEL || "info";
|
|
50
|
+
const minOrder = LEVEL_ORDER[minLevel] ?? LEVEL_ORDER.info;
|
|
51
|
+
function log(level, msg, data) {
|
|
52
|
+
if (LEVEL_ORDER[level] < minOrder)
|
|
53
|
+
return;
|
|
54
|
+
const entry = {
|
|
55
|
+
ts: new Date().toISOString(),
|
|
56
|
+
level,
|
|
57
|
+
server: serverName,
|
|
58
|
+
msg,
|
|
59
|
+
};
|
|
60
|
+
if (data) {
|
|
61
|
+
Object.assign(entry, maskSensitive(data));
|
|
62
|
+
}
|
|
63
|
+
// Always stderr — stdout is reserved for MCP JSON-RPC
|
|
64
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
debug: (msg, data) => log("debug", msg, data),
|
|
68
|
+
info: (msg, data) => log("info", msg, data),
|
|
69
|
+
warn: (msg, data) => log("warn", msg, data),
|
|
70
|
+
error: (msg, data) => log("error", msg, data),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AASF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,OAAO;IACP,UAAU;IACV,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,eAAe;IACf,eAAe;IACf,cAAc;IACd,eAAe;IACf,aAAa;CACd,CAAC,CAAC;AAEH,oDAAoD;AACpD,SAAS,aAAa,CACpB,GAA4B;IAE5B,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChF,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,KAAgC,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,MAAM,QAAQ,GACX,OAAO,CAAC,GAAG,CAAC,SAAkC,IAAI,MAAM,CAAC;IAC5D,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;IAE3D,SAAS,GAAG,CACV,KAAe,EACf,GAAW,EACX,IAA8B;QAE9B,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,QAAQ;YAAE,OAAO;QAE1C,MAAM,KAAK,GAA4B;YACrC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,KAAK;YACL,MAAM,EAAE,UAAU;YAClB,GAAG;SACJ,CAAC;QAEF,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,sDAAsD;QACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;QAC7C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output sanitization for API responses.
|
|
3
|
+
*
|
|
4
|
+
* Protects against prompt injection attacks where malicious content
|
|
5
|
+
* in API responses could manipulate LLM behavior.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Sanitizes text returned from external APIs before passing to LLM.
|
|
9
|
+
* Strips potential prompt injection patterns.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sanitizeApiResponse(text: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Truncates response text if it exceeds maxLength.
|
|
14
|
+
* Appends a note about truncation for the LLM.
|
|
15
|
+
*/
|
|
16
|
+
export declare function truncateResponse(text: string, maxLength?: number): string;
|
|
17
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAe,GACzB,MAAM,CAMR"}
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output sanitization for API responses.
|
|
3
|
+
*
|
|
4
|
+
* Protects against prompt injection attacks where malicious content
|
|
5
|
+
* in API responses could manipulate LLM behavior.
|
|
6
|
+
*/
|
|
7
|
+
/** Patterns that attempt to override LLM instructions */
|
|
8
|
+
const INJECTION_PATTERNS = [
|
|
9
|
+
/\b(ignore|forget|disregard|override)\s+(all\s+)?(previous|prior|above|earlier|system)\s+(instructions?|prompts?|rules?|context|constraints?)/gi,
|
|
10
|
+
/\b(you\s+are\s+now|act\s+as|pretend\s+to\s+be|switch\s+to|new\s+instructions?:)/gi,
|
|
11
|
+
/\bsystem\s*:\s*you\s+(are|must|should|will)/gi,
|
|
12
|
+
/<\/?system>/gi,
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Sanitizes text returned from external APIs before passing to LLM.
|
|
16
|
+
* Strips potential prompt injection patterns.
|
|
17
|
+
*/
|
|
18
|
+
export function sanitizeApiResponse(text) {
|
|
19
|
+
let result = text;
|
|
20
|
+
for (const pattern of INJECTION_PATTERNS) {
|
|
21
|
+
result = result.replace(pattern, "[filtered]");
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Truncates response text if it exceeds maxLength.
|
|
27
|
+
* Appends a note about truncation for the LLM.
|
|
28
|
+
*/
|
|
29
|
+
export function truncateResponse(text, maxLength = 50_000) {
|
|
30
|
+
if (text.length <= maxLength)
|
|
31
|
+
return text;
|
|
32
|
+
return (text.slice(0, maxLength) +
|
|
33
|
+
`\n\n[Truncated: response was ${text.length} chars, showing first ${maxLength}. Use pagination or filters to narrow results.]`);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yDAAyD;AACzD,MAAM,kBAAkB,GAAG;IACzB,gJAAgJ;IAChJ,mFAAmF;IACnF,+CAA+C;IAC/C,eAAe;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,YAAoB,MAAM;IAE1B,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,CACL,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC;QACxB,gCAAgC,IAAI,CAAC,MAAM,yBAAyB,SAAS,iDAAiD,CAC/H,CAAC;AACJ,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server factory with dual transport support (stdio + Streamable HTTP).
|
|
3
|
+
*
|
|
4
|
+
* Consolidates the transport setup pattern from CDEK and MoySklad
|
|
5
|
+
* into a reusable factory. Includes graceful shutdown and health endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import type { Logger } from "./logging.js";
|
|
9
|
+
export interface ServerConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
version: string;
|
|
12
|
+
/** Number of tools (shown in health endpoint) */
|
|
13
|
+
toolCount?: number;
|
|
14
|
+
logger?: Logger;
|
|
15
|
+
}
|
|
16
|
+
export interface HttpServerConfig {
|
|
17
|
+
port: number;
|
|
18
|
+
/** CORS allowed origins. Use ["*"] for development only. */
|
|
19
|
+
corsOrigins?: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Starts the MCP server with stdio transport (default for Claude Desktop/Cursor).
|
|
23
|
+
*/
|
|
24
|
+
export declare function startStdio(server: McpServer, config: ServerConfig): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Starts the MCP server with Streamable HTTP transport.
|
|
27
|
+
* Includes CORS, health endpoint, session management.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startHttp(createServer: () => McpServer, config: ServerConfig & HttpServerConfig): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Detects transport mode from CLI args / env and runs the appropriate server.
|
|
32
|
+
*
|
|
33
|
+
* Usage in server entry points:
|
|
34
|
+
* ```ts
|
|
35
|
+
* runServer(createMyServer, { name: "my-mcp", version: "1.0.0", toolCount: 5 });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function runServer(createServer: () => McpServer, config: ServerConfig): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,YAAY,EAAE,MAAM,SAAS,EAC7B,MAAM,EAAE,YAAY,GAAG,gBAAgB,GACtC,OAAO,CAAC,IAAI,CAAC,CAkJf;AAqBD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,YAAY,EAAE,MAAM,SAAS,EAC7B,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server factory with dual transport support (stdio + Streamable HTTP).
|
|
3
|
+
*
|
|
4
|
+
* Consolidates the transport setup pattern from CDEK and MoySklad
|
|
5
|
+
* into a reusable factory. Includes graceful shutdown and health endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
/**
|
|
10
|
+
* Starts the MCP server with stdio transport (default for Claude Desktop/Cursor).
|
|
11
|
+
*/
|
|
12
|
+
export async function startStdio(server, config) {
|
|
13
|
+
const transport = new StdioServerTransport();
|
|
14
|
+
setupGracefulShutdown(config.logger);
|
|
15
|
+
await server.connect(transport);
|
|
16
|
+
config.logger?.info("Server started (stdio)", {
|
|
17
|
+
tools: config.toolCount,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Starts the MCP server with Streamable HTTP transport.
|
|
22
|
+
* Includes CORS, health endpoint, session management.
|
|
23
|
+
*/
|
|
24
|
+
export async function startHttp(createServer, config) {
|
|
25
|
+
const { randomUUID } = await import("node:crypto");
|
|
26
|
+
let StreamableHTTPServerTransport;
|
|
27
|
+
let isInitializeRequest;
|
|
28
|
+
let express;
|
|
29
|
+
try {
|
|
30
|
+
const sdkServer = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
31
|
+
StreamableHTTPServerTransport = sdkServer.StreamableHTTPServerTransport;
|
|
32
|
+
const types = await import("@modelcontextprotocol/sdk/types.js");
|
|
33
|
+
isInitializeRequest = types.isInitializeRequest;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new Error("Failed to import StreamableHTTPServerTransport. Ensure @modelcontextprotocol/sdk >= 1.12.0");
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
// express is an optional peer dependency — only needed for HTTP transport
|
|
40
|
+
// @ts-expect-error express may not be installed
|
|
41
|
+
express = (await import("express")).default;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new Error("express is required for HTTP transport. Install it: pnpm add express");
|
|
45
|
+
}
|
|
46
|
+
const app = express();
|
|
47
|
+
app.use(express.json());
|
|
48
|
+
// CORS
|
|
49
|
+
const origins = config.corsOrigins ?? ["*"];
|
|
50
|
+
app.use((_req, res, next) => {
|
|
51
|
+
const origin = origins.includes("*") ? "*" : origins[0];
|
|
52
|
+
res.header("Access-Control-Allow-Origin", origin);
|
|
53
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
54
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
|
|
55
|
+
if (_req.method === "OPTIONS")
|
|
56
|
+
return res.sendStatus(204);
|
|
57
|
+
next();
|
|
58
|
+
});
|
|
59
|
+
// Health endpoint
|
|
60
|
+
app.get("/health", (_req, res) => {
|
|
61
|
+
res.json({
|
|
62
|
+
status: "ok",
|
|
63
|
+
server: config.name,
|
|
64
|
+
version: config.version,
|
|
65
|
+
uptime: Math.round(process.uptime()),
|
|
66
|
+
memory_mb: Math.round(process.memoryUsage().rss / 1024 / 1024),
|
|
67
|
+
tools: config.toolCount ?? 0,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// Session management
|
|
71
|
+
const transports = {};
|
|
72
|
+
// MCP POST
|
|
73
|
+
app.post("/mcp", async (req, res) => {
|
|
74
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
75
|
+
try {
|
|
76
|
+
if (sessionId && transports[sessionId]) {
|
|
77
|
+
await transports[sessionId].handleRequest(req, res, req.body);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!sessionId && isInitializeRequest(req.body)) {
|
|
81
|
+
const transport = new StreamableHTTPServerTransport({
|
|
82
|
+
sessionIdGenerator: () => randomUUID(),
|
|
83
|
+
onsessioninitialized: (sid) => {
|
|
84
|
+
transports[sid] = transport;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
transport.onclose = () => {
|
|
88
|
+
const sid = transport.sessionId;
|
|
89
|
+
if (sid)
|
|
90
|
+
delete transports[sid];
|
|
91
|
+
};
|
|
92
|
+
const server = createServer();
|
|
93
|
+
await server.connect(transport);
|
|
94
|
+
await transport.handleRequest(req, res, req.body);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
res.status(400).json({
|
|
98
|
+
jsonrpc: "2.0",
|
|
99
|
+
error: { code: -32000, message: "Bad Request: No valid session ID" },
|
|
100
|
+
id: null,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
config.logger?.error("HTTP request error", {
|
|
105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
106
|
+
});
|
|
107
|
+
if (!res.headersSent) {
|
|
108
|
+
res.status(500).json({
|
|
109
|
+
jsonrpc: "2.0",
|
|
110
|
+
error: { code: -32603, message: "Internal server error" },
|
|
111
|
+
id: null,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// MCP GET (SSE streams)
|
|
117
|
+
app.get("/mcp", async (req, res) => {
|
|
118
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
119
|
+
if (!sessionId || !transports[sessionId]) {
|
|
120
|
+
return res.status(400).send("Invalid or missing session ID");
|
|
121
|
+
}
|
|
122
|
+
await transports[sessionId].handleRequest(req, res);
|
|
123
|
+
});
|
|
124
|
+
// MCP DELETE (session termination)
|
|
125
|
+
app.delete("/mcp", async (req, res) => {
|
|
126
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
127
|
+
if (!sessionId || !transports[sessionId]) {
|
|
128
|
+
return res.status(400).send("Invalid or missing session ID");
|
|
129
|
+
}
|
|
130
|
+
await transports[sessionId].handleRequest(req, res);
|
|
131
|
+
});
|
|
132
|
+
app.listen(config.port, () => {
|
|
133
|
+
config.logger?.info(`HTTP server listening`, {
|
|
134
|
+
port: config.port,
|
|
135
|
+
tools: config.toolCount,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
// Graceful shutdown for HTTP
|
|
139
|
+
const shutdown = async (signal) => {
|
|
140
|
+
config.logger?.info(`${signal} received, shutting down`);
|
|
141
|
+
for (const sid of Object.keys(transports)) {
|
|
142
|
+
await transports[sid].close();
|
|
143
|
+
delete transports[sid];
|
|
144
|
+
}
|
|
145
|
+
process.exit(0);
|
|
146
|
+
};
|
|
147
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
148
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Sets up graceful shutdown handlers for stdio mode.
|
|
152
|
+
*/
|
|
153
|
+
function setupGracefulShutdown(logger) {
|
|
154
|
+
process.on("SIGINT", () => {
|
|
155
|
+
logger?.info("SIGINT received, exiting");
|
|
156
|
+
process.exit(0);
|
|
157
|
+
});
|
|
158
|
+
process.on("SIGTERM", () => {
|
|
159
|
+
logger?.info("SIGTERM received, exiting");
|
|
160
|
+
process.exit(0);
|
|
161
|
+
});
|
|
162
|
+
process.on("unhandledRejection", (err) => {
|
|
163
|
+
logger?.error("Unhandled rejection", {
|
|
164
|
+
error: err instanceof Error ? err.message : String(err),
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Detects transport mode from CLI args / env and runs the appropriate server.
|
|
170
|
+
*
|
|
171
|
+
* Usage in server entry points:
|
|
172
|
+
* ```ts
|
|
173
|
+
* runServer(createMyServer, { name: "my-mcp", version: "1.0.0", toolCount: 5 });
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export async function runServer(createServer, config) {
|
|
177
|
+
const useHttp = process.argv.includes("--http") || !!process.env.HTTP_PORT;
|
|
178
|
+
if (useHttp) {
|
|
179
|
+
const port = parseInt(process.env.HTTP_PORT ?? "3000", 10);
|
|
180
|
+
await startHttp(createServer, { ...config, port });
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const server = createServer();
|
|
184
|
+
await startStdio(server, config);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAiBjF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAiB,EACjB,MAAoB;IAEpB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,wBAAwB,EAAE;QAC5C,KAAK,EAAE,MAAM,CAAC,SAAS;KACxB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,YAA6B,EAC7B,MAAuC;IAEvC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAEnD,IAAI,6BAAkC,CAAC;IACvC,IAAI,mBAAwB,CAAC;IAC7B,IAAI,OAAY,CAAC;IAEjB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAC5B,oDAAoD,CACrD,CAAC;QACF,6BAA6B,GAAG,SAAS,CAAC,6BAA6B,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,oCAAoC,CAAC,CAAC;QACjE,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,0EAA0E;QAC1E,gDAAgD;QAChD,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,OAAO;IACP,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;QAClD,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;QACzE,GAAG,CAAC,MAAM,CACR,8BAA8B,EAC9B,8BAA8B,CAC/B,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC1D,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,kBAAkB;IAClB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAS,EAAE,GAAQ,EAAE,EAAE;QACzC,GAAG,CAAC,IAAI,CAAC;YACP,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;YAC9D,KAAK,EAAE,MAAM,CAAC,SAAS,IAAI,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,qBAAqB;IACrB,MAAM,UAAU,GAAwB,EAAE,CAAC;IAE3C,WAAW;IACX,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;QAC5C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QAEtE,IAAI,CAAC;YACH,IAAI,SAAS,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YAED,IAAI,CAAC,SAAS,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;oBAClD,kBAAkB,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;oBACtC,oBAAoB,EAAE,CAAC,GAAW,EAAE,EAAE;wBACpC,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;oBAC9B,CAAC;iBACF,CAAC,CAAC;gBACH,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE;oBACvB,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC;oBAChC,IAAI,GAAG;wBAAE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;gBAClC,CAAC,CAAC;gBAEF,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;gBAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,kCAAkC,EAAE;gBACpE,EAAE,EAAE,IAAI;aACT,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,oBAAoB,EAAE;gBACzC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE;oBACzD,EAAE,EAAE,IAAI;iBACT,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,wBAAwB;IACxB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QACtE,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,mCAAmC;IACnC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;QAC9C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QACtE,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE;YAC3C,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,SAAS;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;QACxC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,0BAA0B,CAAC,CAAC;QACzD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;YAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,MAAe;IAC5C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,MAAM,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,MAAM,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,GAAG,EAAE,EAAE;QACvC,MAAM,EAAE,KAAK,CAAC,qBAAqB,EAAE;YACnC,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,YAA6B,EAC7B,MAAoB;IAEpB,MAAM,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IAE7D,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,SAAS,CAAC,YAAY,EAAE,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared E2E smoke test utilities for MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* Uses StdioClientTransport from the MCP SDK to connect to a compiled
|
|
5
|
+
* server binary and verify it starts, lists tools, and has quality descriptions.
|
|
6
|
+
*/
|
|
7
|
+
export interface SmokeTestConfig {
|
|
8
|
+
/** Path to the compiled server entry point (e.g., "dist/index.js") */
|
|
9
|
+
serverPath: string;
|
|
10
|
+
/** Expected minimum number of tools */
|
|
11
|
+
expectedToolCount: number;
|
|
12
|
+
/** Environment variables to pass to the server process */
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
/** Timeout in ms (default: 10_000) */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SmokeTestResult {
|
|
18
|
+
connected: boolean;
|
|
19
|
+
toolCount: number;
|
|
20
|
+
tools: Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
descriptionLength: number;
|
|
24
|
+
hasInputSchema: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
errors: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs a smoke test against a compiled MCP server.
|
|
30
|
+
*
|
|
31
|
+
* 1. Spawns the server via StdioClientTransport
|
|
32
|
+
* 2. Connects and sends initialize
|
|
33
|
+
* 3. Lists tools and validates descriptions
|
|
34
|
+
* 4. Closes cleanly
|
|
35
|
+
*/
|
|
36
|
+
export declare function runSmokeTest(config: SmokeTestConfig): Promise<SmokeTestResult>;
|
|
37
|
+
//# sourceMappingURL=smoke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke.d.ts","sourceRoot":"","sources":["../../src/testing/smoke.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC,CAAC;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,eAAe,CAAC,CAyE1B"}
|