@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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Authentication strategies for MCP servers.
3
+ *
4
+ * Strategy Pattern: each auth type implements AuthStrategy interface.
5
+ * Covers all 5 auth methods used across the 53 servers:
6
+ * - API Key (Bearer token)
7
+ * - Basic Auth (username:password)
8
+ * - OAuth2 Client Credentials
9
+ * - HMAC/Signature-based
10
+ * - No Auth (pass-through)
11
+ */
12
+ export interface AuthStrategy {
13
+ readonly type: string;
14
+ authenticate(request: RequestInit): Promise<RequestInit>;
15
+ /** Called when server returns 401 — clear cached credentials */
16
+ invalidate?(): void;
17
+ }
18
+ /** Bearer token or custom header API key auth */
19
+ export declare class ApiKeyStrategy implements AuthStrategy {
20
+ private readonly key;
21
+ private readonly header;
22
+ private readonly prefix;
23
+ readonly type = "api_key";
24
+ constructor(key: string, header?: string, prefix?: string);
25
+ authenticate(req: RequestInit): Promise<RequestInit>;
26
+ }
27
+ /** HTTP Basic Authentication (base64 encoded) */
28
+ export declare class BasicAuthStrategy implements AuthStrategy {
29
+ readonly type = "basic";
30
+ private readonly encoded;
31
+ constructor(username: string, password: string);
32
+ authenticate(req: RequestInit): Promise<RequestInit>;
33
+ }
34
+ /** OAuth2 Client Credentials flow with auto-refresh and concurrent request dedup */
35
+ export declare class OAuthStrategy implements AuthStrategy {
36
+ private readonly config;
37
+ readonly type = "oauth";
38
+ private token;
39
+ private expiresAt;
40
+ private refreshPromise;
41
+ constructor(config: {
42
+ tokenUrl: string;
43
+ clientId: string;
44
+ clientSecret: string;
45
+ /** Safety margin in ms before token expiry (default: 60s) */
46
+ expiryBuffer?: number;
47
+ });
48
+ invalidate(): void;
49
+ authenticate(req: RequestInit): Promise<RequestInit>;
50
+ private getToken;
51
+ private refresh;
52
+ }
53
+ /** Dual auth: tries Bearer token first, falls back to Basic */
54
+ export declare class DualAuthStrategy implements AuthStrategy {
55
+ readonly type = "dual";
56
+ private readonly strategy;
57
+ constructor(config: {
58
+ token?: string;
59
+ login?: string;
60
+ password?: string;
61
+ });
62
+ authenticate(req: RequestInit): Promise<RequestInit>;
63
+ }
64
+ /** No-auth pass-through for public APIs (CBR, CBU) */
65
+ export declare class NoAuthStrategy implements AuthStrategy {
66
+ readonly type = "none";
67
+ authenticate(req: RequestInit): Promise<RequestInit>;
68
+ }
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACzD,gEAAgE;IAChE,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB;AAED,iDAAiD;AACjD,qBAAa,cAAe,YAAW,YAAY;IAI/C,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,QAAQ,CAAC,IAAI,aAAa;gBAGP,GAAG,EAAE,MAAM,EACX,MAAM,SAAkB,EACxB,MAAM,SAAW;IAG9B,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CAK3D;AAED,iDAAiD;AACjD,qBAAa,iBAAkB,YAAW,YAAY;IACpD,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAIxC,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CAK3D;AAED,oFAAoF;AACpF,qBAAa,aAAc,YAAW,YAAY;IAO9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,cAAc,CAAgC;gBAGnC,MAAM,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,6DAA6D;QAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;IAGH,UAAU,IAAI,IAAI;IAKZ,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;YAO5C,QAAQ;YAiBR,OAAO;CA4BtB;AAED,+DAA+D;AAC/D,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,UAAU;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;gBAE5B,MAAM,EAAE;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAYK,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CAG3D;AAED,sDAAsD;AACtD,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,UAAU;IAEjB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CAG3D"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Authentication strategies for MCP servers.
3
+ *
4
+ * Strategy Pattern: each auth type implements AuthStrategy interface.
5
+ * Covers all 5 auth methods used across the 53 servers:
6
+ * - API Key (Bearer token)
7
+ * - Basic Auth (username:password)
8
+ * - OAuth2 Client Credentials
9
+ * - HMAC/Signature-based
10
+ * - No Auth (pass-through)
11
+ */
12
+ /** Bearer token or custom header API key auth */
13
+ export class ApiKeyStrategy {
14
+ key;
15
+ header;
16
+ prefix;
17
+ type = "api_key";
18
+ constructor(key, header = "Authorization", prefix = "Bearer") {
19
+ this.key = key;
20
+ this.header = header;
21
+ this.prefix = prefix;
22
+ }
23
+ async authenticate(req) {
24
+ const headers = new Headers(req.headers);
25
+ headers.set(this.header, `${this.prefix} ${this.key}`);
26
+ return { ...req, headers };
27
+ }
28
+ }
29
+ /** HTTP Basic Authentication (base64 encoded) */
30
+ export class BasicAuthStrategy {
31
+ type = "basic";
32
+ encoded;
33
+ constructor(username, password) {
34
+ this.encoded = Buffer.from(`${username}:${password}`).toString("base64");
35
+ }
36
+ async authenticate(req) {
37
+ const headers = new Headers(req.headers);
38
+ headers.set("Authorization", `Basic ${this.encoded}`);
39
+ return { ...req, headers };
40
+ }
41
+ }
42
+ /** OAuth2 Client Credentials flow with auto-refresh and concurrent request dedup */
43
+ export class OAuthStrategy {
44
+ config;
45
+ type = "oauth";
46
+ token = null;
47
+ expiresAt = 0;
48
+ refreshPromise = null;
49
+ constructor(config) {
50
+ this.config = config;
51
+ }
52
+ invalidate() {
53
+ this.token = null;
54
+ this.expiresAt = 0;
55
+ }
56
+ async authenticate(req) {
57
+ const token = await this.getToken();
58
+ const headers = new Headers(req.headers);
59
+ headers.set("Authorization", `Bearer ${token}`);
60
+ return { ...req, headers };
61
+ }
62
+ async getToken() {
63
+ const buffer = this.config.expiryBuffer ?? 60_000;
64
+ if (this.token && Date.now() < this.expiresAt - buffer) {
65
+ return this.token;
66
+ }
67
+ // Deduplicate concurrent refresh requests
68
+ if (this.refreshPromise) {
69
+ return this.refreshPromise;
70
+ }
71
+ this.refreshPromise = this.refresh();
72
+ try {
73
+ return await this.refreshPromise;
74
+ }
75
+ finally {
76
+ this.refreshPromise = null;
77
+ }
78
+ }
79
+ async refresh() {
80
+ const body = new URLSearchParams({
81
+ grant_type: "client_credentials",
82
+ client_id: this.config.clientId,
83
+ client_secret: this.config.clientSecret,
84
+ });
85
+ const response = await fetch(this.config.tokenUrl, {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
88
+ body: body.toString(),
89
+ });
90
+ if (!response.ok) {
91
+ const text = await response.text();
92
+ throw new Error(`OAuth token error (HTTP ${response.status}). Проверьте client_id и client_secret. ${text}`);
93
+ }
94
+ const data = (await response.json());
95
+ this.token = data.access_token;
96
+ this.expiresAt = Date.now() + data.expires_in * 1000;
97
+ return this.token;
98
+ }
99
+ }
100
+ /** Dual auth: tries Bearer token first, falls back to Basic */
101
+ export class DualAuthStrategy {
102
+ type = "dual";
103
+ strategy;
104
+ constructor(config) {
105
+ if (config.token) {
106
+ this.strategy = new ApiKeyStrategy(config.token);
107
+ }
108
+ else if (config.login && config.password) {
109
+ this.strategy = new BasicAuthStrategy(config.login, config.password);
110
+ }
111
+ else {
112
+ throw new Error("Auth not configured. Provide token OR login+password.");
113
+ }
114
+ }
115
+ async authenticate(req) {
116
+ return this.strategy.authenticate(req);
117
+ }
118
+ }
119
+ /** No-auth pass-through for public APIs (CBR, CBU) */
120
+ export class NoAuthStrategy {
121
+ type = "none";
122
+ async authenticate(req) {
123
+ return req;
124
+ }
125
+ }
126
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Base HTTP client with retry, timeout, and exponential backoff.
3
+ *
4
+ * Consolidates the retry/timeout/error pattern duplicated across all 53 servers
5
+ * into a single reusable class.
6
+ */
7
+ import type { AuthStrategy } from "./auth/index.js";
8
+ import type { Logger } from "./logging.js";
9
+ export interface BaseClientOptions {
10
+ baseUrl: string;
11
+ timeout?: number;
12
+ maxRetries?: number;
13
+ auth?: AuthStrategy;
14
+ logger?: Logger;
15
+ /** Custom headers applied to every request */
16
+ headers?: Record<string, string>;
17
+ }
18
+ export interface RequestOptions {
19
+ method?: string;
20
+ path: string;
21
+ body?: unknown;
22
+ params?: Record<string, string>;
23
+ headers?: Record<string, string>;
24
+ /** Override timeout for this specific request */
25
+ timeout?: number;
26
+ }
27
+ export declare class ApiError extends Error {
28
+ readonly status: number;
29
+ readonly body?: string | undefined;
30
+ readonly headers?: Record<string, string> | undefined;
31
+ readonly code?: string | undefined;
32
+ constructor(status: number, message: string, body?: string | undefined, headers?: Record<string, string> | undefined, code?: string | undefined);
33
+ }
34
+ export declare class BaseHttpClient {
35
+ protected readonly baseUrl: string;
36
+ protected readonly timeout: number;
37
+ protected readonly maxRetries: number;
38
+ protected readonly auth?: AuthStrategy;
39
+ protected readonly logger?: Logger;
40
+ protected readonly defaultHeaders: Record<string, string>;
41
+ constructor(options: BaseClientOptions);
42
+ get(path: string, params?: Record<string, string>): Promise<unknown>;
43
+ post(path: string, body?: unknown): Promise<unknown>;
44
+ put(path: string, body?: unknown): Promise<unknown>;
45
+ delete(path: string): Promise<unknown>;
46
+ request(opts: RequestOptions): Promise<unknown>;
47
+ }
48
+ /**
49
+ * Token bucket rate limiter.
50
+ * Used by MoySklad (45 req/3s) and similar APIs with strict rate limits.
51
+ */
52
+ export declare class TokenBucketLimiter {
53
+ private readonly maxTokens;
54
+ private readonly refillMs;
55
+ private tokens;
56
+ private lastRefill;
57
+ constructor(maxTokens: number, refillMs: number);
58
+ acquire(): Promise<void>;
59
+ }
60
+ /**
61
+ * Extended client with token bucket rate limiting.
62
+ */
63
+ export declare class RateLimitedClient extends BaseHttpClient {
64
+ private readonly limiter;
65
+ constructor(options: BaseClientOptions & {
66
+ bucketMax: number;
67
+ bucketRefillMs: number;
68
+ });
69
+ request(opts: RequestOptions): Promise<unknown>;
70
+ }
71
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Base HTTP client with retry, timeout, and exponential backoff.
3
+ *
4
+ * Consolidates the retry/timeout/error pattern duplicated across all 53 servers
5
+ * into a single reusable class.
6
+ */
7
+ export class ApiError extends Error {
8
+ status;
9
+ body;
10
+ headers;
11
+ code;
12
+ constructor(status, message, body, headers, code) {
13
+ super(message);
14
+ this.status = status;
15
+ this.body = body;
16
+ this.headers = headers;
17
+ this.code = code;
18
+ this.name = "ApiError";
19
+ }
20
+ }
21
+ export class BaseHttpClient {
22
+ baseUrl;
23
+ timeout;
24
+ maxRetries;
25
+ auth;
26
+ logger;
27
+ defaultHeaders;
28
+ constructor(options) {
29
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
30
+ this.timeout = options.timeout ?? 15_000;
31
+ this.maxRetries = options.maxRetries ?? 3;
32
+ this.auth = options.auth;
33
+ this.logger = options.logger;
34
+ this.defaultHeaders = {
35
+ "Content-Type": "application/json",
36
+ ...options.headers,
37
+ };
38
+ }
39
+ async get(path, params) {
40
+ return this.request({ method: "GET", path, params });
41
+ }
42
+ async post(path, body) {
43
+ return this.request({ method: "POST", path, body });
44
+ }
45
+ async put(path, body) {
46
+ return this.request({ method: "PUT", path, body });
47
+ }
48
+ async delete(path) {
49
+ return this.request({ method: "DELETE", path });
50
+ }
51
+ async request(opts) {
52
+ const method = opts.method ?? "GET";
53
+ const query = opts.params
54
+ ? "?" + new URLSearchParams(opts.params).toString()
55
+ : "";
56
+ const url = opts.path.startsWith("http")
57
+ ? `${opts.path}${query}`
58
+ : `${this.baseUrl}${opts.path}${query}`;
59
+ const requestTimeout = opts.timeout ?? this.timeout;
60
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
61
+ const controller = new AbortController();
62
+ const timer = setTimeout(() => controller.abort(), requestTimeout);
63
+ try {
64
+ let reqInit = {
65
+ method,
66
+ headers: { ...this.defaultHeaders, ...opts.headers },
67
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
68
+ signal: controller.signal,
69
+ };
70
+ // Apply auth strategy
71
+ if (this.auth) {
72
+ reqInit = await this.auth.authenticate(reqInit);
73
+ }
74
+ const response = await fetch(url, reqInit);
75
+ clearTimeout(timer);
76
+ if (response.ok) {
77
+ const text = await response.text();
78
+ if (!text)
79
+ return null;
80
+ try {
81
+ return JSON.parse(text);
82
+ }
83
+ catch {
84
+ return text;
85
+ }
86
+ }
87
+ // Auth expired — invalidate and retry
88
+ if ((response.status === 401 || response.status === 403) &&
89
+ attempt < this.maxRetries &&
90
+ this.auth?.invalidate) {
91
+ this.auth.invalidate();
92
+ this.logger?.warn("Auth token expired, refreshing", {
93
+ attempt,
94
+ status: response.status,
95
+ });
96
+ continue;
97
+ }
98
+ const errorBody = await response.text();
99
+ // Retryable server errors
100
+ if ((response.status === 429 || response.status >= 500) &&
101
+ attempt < this.maxRetries) {
102
+ const delay = Math.min(1000 * 2 ** (attempt - 1), 8000);
103
+ this.logger?.warn("Retryable error, backing off", {
104
+ status: response.status,
105
+ delay,
106
+ attempt,
107
+ path: opts.path,
108
+ });
109
+ await new Promise((r) => setTimeout(r, delay));
110
+ continue;
111
+ }
112
+ // Build response headers map for ApiError
113
+ const respHeaders = {};
114
+ response.headers.forEach((v, k) => {
115
+ respHeaders[k] = v;
116
+ });
117
+ throw new ApiError(response.status, `HTTP ${response.status}: ${response.statusText}`, errorBody, respHeaders);
118
+ }
119
+ catch (error) {
120
+ clearTimeout(timer);
121
+ if (error instanceof ApiError)
122
+ throw error;
123
+ if (error instanceof DOMException &&
124
+ error.name === "AbortError") {
125
+ if (attempt < this.maxRetries) {
126
+ this.logger?.warn("Request timeout, retrying", {
127
+ attempt,
128
+ path: opts.path,
129
+ });
130
+ continue;
131
+ }
132
+ throw new ApiError(0, `Таймаут запроса (${requestTimeout / 1000}с). API не ответил вовремя.`);
133
+ }
134
+ throw error;
135
+ }
136
+ }
137
+ throw new ApiError(0, "Все попытки запроса исчерпаны");
138
+ }
139
+ }
140
+ /**
141
+ * Token bucket rate limiter.
142
+ * Used by MoySklad (45 req/3s) and similar APIs with strict rate limits.
143
+ */
144
+ export class TokenBucketLimiter {
145
+ maxTokens;
146
+ refillMs;
147
+ tokens;
148
+ lastRefill;
149
+ constructor(maxTokens, refillMs) {
150
+ this.maxTokens = maxTokens;
151
+ this.refillMs = refillMs;
152
+ this.tokens = maxTokens;
153
+ this.lastRefill = Date.now();
154
+ }
155
+ async acquire() {
156
+ const now = Date.now();
157
+ const elapsed = now - this.lastRefill;
158
+ if (elapsed >= this.refillMs) {
159
+ this.tokens = this.maxTokens;
160
+ this.lastRefill = now;
161
+ }
162
+ if (this.tokens > 0) {
163
+ this.tokens--;
164
+ return;
165
+ }
166
+ const waitMs = this.refillMs - (Date.now() - this.lastRefill);
167
+ if (waitMs > 0) {
168
+ await new Promise((r) => setTimeout(r, waitMs));
169
+ }
170
+ this.tokens = this.maxTokens - 1;
171
+ this.lastRefill = Date.now();
172
+ }
173
+ }
174
+ /**
175
+ * Extended client with token bucket rate limiting.
176
+ */
177
+ export class RateLimitedClient extends BaseHttpClient {
178
+ limiter;
179
+ constructor(options) {
180
+ super(options);
181
+ this.limiter = new TokenBucketLimiter(options.bucketMax, options.bucketRefillMs);
182
+ }
183
+ async request(opts) {
184
+ await this.limiter.acquire();
185
+ return super.request(opts);
186
+ }
187
+ }
188
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Centralized error handling for MCP tool responses.
3
+ *
4
+ * Key insight from MCP spec: tool execution errors with `isError: true`
5
+ * are injected back into the LLM context — the model can self-recover.
6
+ * Protocol-level errors go to UI and are thrown. This distinction is critical.
7
+ */
8
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
9
+ export type ErrorCategory = "validation" | "auth" | "rate_limit" | "not_found" | "server_error" | "timeout" | "unknown";
10
+ export interface ApiErrorInfo {
11
+ status: number;
12
+ message?: string;
13
+ headers?: Record<string, string>;
14
+ code?: string;
15
+ }
16
+ /**
17
+ * Converts any error into an MCP-compliant CallToolResult with `isError: true`
18
+ * and a next-action suggestion so the LLM can self-recover.
19
+ */
20
+ export declare function createToolError(error: unknown): CallToolResult;
21
+ /**
22
+ * Wraps a tool handler with automatic error handling.
23
+ * Returns `isError: true` result instead of throwing.
24
+ */
25
+ export declare function withErrorHandling<T = Record<string, unknown>>(handler: (params: T) => Promise<CallToolResult>): (params: T) => Promise<CallToolResult>;
26
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGzE,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,WAAW,GACX,cAAc,GACd,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CA8F9D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,GAC9C,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,CAQxC"}
package/dist/errors.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Centralized error handling for MCP tool responses.
3
+ *
4
+ * Key insight from MCP spec: tool execution errors with `isError: true`
5
+ * are injected back into the LLM context — the model can self-recover.
6
+ * Protocol-level errors go to UI and are thrown. This distinction is critical.
7
+ */
8
+ import { ZodError } from "zod";
9
+ function isApiError(e) {
10
+ return typeof e === "object" && e !== null && "status" in e;
11
+ }
12
+ function isDOMAbortError(e) {
13
+ return e instanceof DOMException && e.name === "AbortError";
14
+ }
15
+ function categorize(error) {
16
+ if (error instanceof ZodError)
17
+ return "validation";
18
+ if (isDOMAbortError(error))
19
+ return "timeout";
20
+ if (!isApiError(error))
21
+ return "unknown";
22
+ const { status } = error;
23
+ if (status === 401 || status === 403)
24
+ return "auth";
25
+ if (status === 429)
26
+ return "rate_limit";
27
+ if (status === 404)
28
+ return "not_found";
29
+ if (status >= 500)
30
+ return "server_error";
31
+ return "unknown";
32
+ }
33
+ /**
34
+ * Converts any error into an MCP-compliant CallToolResult with `isError: true`
35
+ * and a next-action suggestion so the LLM can self-recover.
36
+ */
37
+ export function createToolError(error) {
38
+ const category = categorize(error);
39
+ switch (category) {
40
+ case "validation": {
41
+ const zodErr = error;
42
+ const details = zodErr.errors
43
+ .map((e) => `${e.path.join(".")}: ${e.message}`)
44
+ .join("; ");
45
+ return {
46
+ isError: true,
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: `Ошибка валидации: ${details}. Исправьте параметры и повторите запрос.`,
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ case "auth":
56
+ return {
57
+ isError: true,
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: `Ошибка авторизации. Токен истёк или неверен. Попросите пользователя проверить и перенастроить API-ключ.`,
62
+ },
63
+ ],
64
+ };
65
+ case "rate_limit": {
66
+ const apiErr = error;
67
+ const retryAfter = apiErr.headers?.["retry-after"] || "60";
68
+ return {
69
+ isError: true,
70
+ content: [
71
+ {
72
+ type: "text",
73
+ text: `Rate limit. Повторите через ${retryAfter}с. Если это 3-й раз подряд — сообщите пользователю о превышении лимита API.`,
74
+ },
75
+ ],
76
+ };
77
+ }
78
+ case "not_found":
79
+ return {
80
+ isError: true,
81
+ content: [
82
+ {
83
+ type: "text",
84
+ text: `Ресурс не найден. Проверьте ID или параметры. Используйте search/list tool для поиска корректного идентификатора.`,
85
+ },
86
+ ],
87
+ };
88
+ case "server_error": {
89
+ const apiErr = error;
90
+ return {
91
+ isError: true,
92
+ content: [
93
+ {
94
+ type: "text",
95
+ text: `Ошибка сервера (HTTP ${apiErr.status}). Внешний API временно недоступен. Повторите запрос через 10-30 секунд.`,
96
+ },
97
+ ],
98
+ };
99
+ }
100
+ case "timeout":
101
+ return {
102
+ isError: true,
103
+ content: [
104
+ {
105
+ type: "text",
106
+ text: `Таймаут запроса. Внешний API не ответил вовремя. Повторите запрос. Если проблема повторяется — сообщите пользователю.`,
107
+ },
108
+ ],
109
+ };
110
+ default: {
111
+ const msg = error instanceof Error ? error.message : "Неизвестная ошибка";
112
+ return {
113
+ isError: true,
114
+ content: [
115
+ {
116
+ type: "text",
117
+ text: `Ошибка: ${msg}. Повторите запрос или попробуйте другой подход.`,
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Wraps a tool handler with automatic error handling.
126
+ * Returns `isError: true` result instead of throwing.
127
+ */
128
+ export function withErrorHandling(handler) {
129
+ return async (params) => {
130
+ try {
131
+ return await handler(params);
132
+ }
133
+ catch (error) {
134
+ return createToolError(error);
135
+ }
136
+ };
137
+ }
138
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}