FreeAgent MCP Server

MIT License
1
  • Linux
  • Apple
  • src
import axios, { AxiosInstance } from 'axios'; import { FreeAgentConfig, Timeslip, TimeslipAttributes, TimeslipsResponse, TimeslipResponse } from './types.js'; export class FreeAgentClient { private axiosInstance: AxiosInstance; private config: FreeAgentConfig; constructor(config: FreeAgentConfig) { this.config = config; this.axiosInstance = axios.create({ baseURL: 'https://api.freeagent.com/v2', headers: { 'Authorization': `Bearer ${config.accessToken}`, 'Content-Type': 'application/json' } }); // Add response interceptor for token refresh this.axiosInstance.interceptors.response.use( response => response, async error => { if (error.response?.status === 401) { await this.refreshToken(); error.config.headers['Authorization'] = `Bearer ${this.config.accessToken}`; return this.axiosInstance.request(error.config); } return Promise.reject(error); } ); } private async refreshToken() { try { const response = await axios.post('https://api.freeagent.com/v2/token_endpoint', { grant_type: 'refresh_token', refresh_token: this.config.refreshToken, client_id: this.config.clientId, client_secret: this.config.clientSecret }); this.config.accessToken = response.data.access_token; this.config.refreshToken = response.data.refresh_token; this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${this.config.accessToken}`; console.error('[Auth] Successfully refreshed access token'); } catch (error) { console.error('[Auth] Failed to refresh token:', error); throw error; } } async listTimeslips(params?: { from_date?: string; to_date?: string; updated_since?: string; view?: 'all' | 'unbilled' | 'running'; user?: string; task?: string; project?: string; nested?: boolean; }): Promise<Timeslip[]> { try { console.error('[API] Fetching timeslips with params:', params); const response = await this.axiosInstance.get<TimeslipsResponse>('/timeslips', { params }); return response.data.timeslips; } catch (error) { console.error('[API] Failed to fetch timeslips:', error); throw error; } } async getTimeslip(id: string): Promise<Timeslip> { try { console.error('[API] Fetching timeslip:', id); const response = await this.axiosInstance.get<TimeslipResponse>(`/timeslips/${id}`); return response.data.timeslip; } catch (error) { console.error('[API] Failed to fetch timeslip:', error); throw error; } } async createTimeslip(timeslip: TimeslipAttributes): Promise<Timeslip> { try { console.error('[API] Creating timeslip:', timeslip); const response = await this.axiosInstance.post<TimeslipResponse>('/timeslips', { timeslip }); return response.data.timeslip; } catch (error) { console.error('[API] Failed to create timeslip:', error); throw error; } } async createTimeslips(timeslips: TimeslipAttributes[]): Promise<Timeslip[]> { try { console.error('[API] Creating multiple timeslips:', timeslips); const response = await this.axiosInstance.post<TimeslipsResponse>('/timeslips', { timeslips }); return response.data.timeslips; } catch (error) { console.error('[API] Failed to create timeslips:', error); throw error; } } async updateTimeslip(id: string, timeslip: Partial<TimeslipAttributes>): Promise<Timeslip> { try { console.error('[API] Updating timeslip:', id, timeslip); const response = await this.axiosInstance.put<TimeslipResponse>(`/timeslips/${id}`, { timeslip }); return response.data.timeslip; } catch (error) { console.error('[API] Failed to update timeslip:', error); throw error; } } async deleteTimeslip(id: string): Promise<void> { try { console.error('[API] Deleting timeslip:', id); await this.axiosInstance.delete(`/timeslips/${id}`); } catch (error) { console.error('[API] Failed to delete timeslip:', error); throw error; } } async startTimer(id: string): Promise<Timeslip> { try { console.error('[API] Starting timer for timeslip:', id); const response = await this.axiosInstance.post<TimeslipResponse>(`/timeslips/${id}/timer`); return response.data.timeslip; } catch (error) { console.error('[API] Failed to start timer:', error); throw error; } } async stopTimer(id: string): Promise<Timeslip> { try { console.error('[API] Stopping timer for timeslip:', id); const response = await this.axiosInstance.delete<TimeslipResponse>(`/timeslips/${id}/timer`); return response.data.timeslip; } catch (error) { console.error('[API] Failed to stop timer:', error); throw error; } } }