diff --git a/src/clients/base-client.ts b/src/clients/base-client.ts index f176bc9..6e43fbd 100644 --- a/src/clients/base-client.ts +++ b/src/clients/base-client.ts @@ -1,6 +1,5 @@ -// src/clients/base-client.ts import axios, { AxiosInstance, AxiosError } from 'axios'; -import { TokenConfigLoader, TokenConfig } from '../config.js'; +import { TokenConfigLoader } from '../config.js'; export interface ClientOptions { timeout?: number; @@ -28,7 +27,7 @@ export class BaseClient { maxRedirects: 5, }); this.axiosInstance.interceptors.request.use(cfg => { - cfg.headers['User-Agent'] = 'Bitbucket-MCP-Server/1.0.0'; + cfg.headers['User-Agent'] = `Bitbucket-MCP-Server/${this._getVersion()}`; return cfg; }); this.axiosInstance.interceptors.response.use( @@ -49,29 +48,55 @@ export class BaseClient { private async _handleResponseError(error: AxiosError): Promise { const status = error.response?.status; if (status === 401) { - console.error('🔐 Bitbucket API Authentication Error (401)'); + console.error('Bitbucket API Authentication Error (401)'); console.error(` Token source: ${this.tokenSource || 'unknown'}`); console.error(` URL: ${error.config?.url}`); + console.error(` Method: ${error.config?.method?.toUpperCase()}`); + const data = error.response?.data; + if (typeof data === 'object' && data !== null) { + console.error(' Response:', JSON.stringify(data, null, 2)); + } else if (data) { + console.error(` Response: ${data}`); + } } else if (status === 403) { - console.error('🚫 Bitbucket API Forbidden (403)'); + console.error('Bitbucket API Forbidden (403)'); + console.error(` Token source: ${this.tokenSource || 'unknown'}`); console.error(` URL: ${error.config?.url}`); } else if (status === 429) { - const retryAfter = error.response?.headers['retry-after']; - const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000; - console.log(`Rate limited. Retrying in ${delay}ms...`); - await this._sleep(delay); + console.warn(`Rate limited (429). URL: ${error.config?.url}`); } throw error; } - protected _sleep(ms: number): Promise { + private _sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } - protected formatError(error: any): string { - if (error?.response?.status) { - return `HTTP ${error.response.status}: ${error.message}`; + private _getVersion(): string { + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const fs = require('fs'); + // eslint-disable-next-line @typescript-eslint/no-require-imports + const path = require('path'); + const pkgPath = path.join(process.cwd(), 'package.json'); + if (fs.existsSync(pkgPath)) { + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + return pkg.version || '1.0.0'; + } + return '1.0.0'; + } catch { + return '1.0.0'; } - return error?.message || 'Unknown error'; + } + + protected formatError(error: unknown): string { + if (error && typeof error === 'object' && 'response' in error) { + const axiosError = error as { response?: { status?: number }; message?: string }; + if (axiosError.response?.status) { + return `HTTP ${axiosError.response.status}: ${axiosError.message || 'Unknown error'}`; + } + } + if (error instanceof Error) return error.message; + return String(error) || 'Unknown error'; } }