UNPKG

sourcesyncai-mcp

Version:

[![smithery badge](https://smithery.ai/badge/@pbteja1998/sourcesyncai-mcp)](https://smithery.ai/server/@pbteja1998/sourcesyncai-mcp)

405 lines (404 loc) 12.6 kB
import wretch from 'wretch'; import FormDataAddon from 'wretch/addons/formData'; import QueryStringAddon from 'wretch/addons/queryString'; // Constants const DEFAULT_API_URL = process.env.SOURCESYNC_API_URL || 'https://api.sourcesync.ai'; const DEFAULT_API_KEY = process.env.SOURCESYNC_API_KEY || ''; const DEFAULT_REQUEST_TIMEOUT = 30000; // 30 seconds // WretchClient utility with middleware and addons export const WretchClient = (baseURL) => wretch(baseURL) .middlewares([ (next) => async (url, opts) => { const response = await next(url, opts); try { Reflect.get(response, 'type', response); } catch { Object.defineProperty(response, 'type', { get: () => 'default', }); } return response; }, ]) .addon(FormDataAddon) .addon(QueryStringAddon); // Error class for SourceSync API errors export class SourceSyncError extends Error { status; error; details; constructor(message, details) { super(message); this.name = 'SourceSyncError'; this.status = details?.status; this.error = details?.error; this.details = details?.details; } } // Helper function to log API errors export function logApiError(error) { console.error('API Error:', { status: error?.status, message: error?.message, details: error?.json || error?.text || error, }); } // Main API client class export class SourceSyncApiClient { client; baseUrl; apiKey; defaultNamespaceId = null; constructor({ apiKey = DEFAULT_API_KEY, baseUrl = DEFAULT_API_URL, } = {}) { this.baseUrl = baseUrl; this.apiKey = apiKey; this.client = WretchClient(baseUrl) .headers({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'Accept': 'application/json', }) // Error handling .catcher(400, (error) => { logApiError(error.json); throw new SourceSyncError('Bad Request', { status: 400, error: error.json, }); }) .catcher(401, (error) => { logApiError(error.json); throw new SourceSyncError('Unauthorized - Invalid API key', { status: 401, error: error.json, }); }) .catcher(403, (error) => { logApiError(error.json); throw new SourceSyncError('Forbidden - Insufficient permissions', { status: 403, error: error.json, }); }) .catcher(404, (error) => { logApiError(error.json); throw new SourceSyncError('Not Found', { status: 404, error: error.json, }); }) .catcher(409, (error) => { logApiError(error.json); throw new SourceSyncError('Conflict', { status: 409, error: error.json, }); }) .catcher(422, (error) => { logApiError(error.json); throw new SourceSyncError('Validation Error', { status: 422, error: error.json, }); }) .catcher(429, (error) => { logApiError(error.json); throw new SourceSyncError('Rate Limit Exceeded', { status: 429, error: error.json, }); }) .catcher(500, (error) => { logApiError(error.json); throw new SourceSyncError('Internal Server Error', { status: 500, error: error.json, }); }); } // Helper method to add tenant ID to requests withTenant(tenantId) { if (!tenantId) { return this.client; } return this.client.headers({ 'X-Tenant-ID': tenantId, }); } // Get namespace ID with fallback to default getNamespaceId(providedNamespaceId) { if (providedNamespaceId) { return providedNamespaceId; } if (this.defaultNamespaceId) { return this.defaultNamespaceId; } throw new SourceSyncError('No namespace ID provided and no default namespace ID set'); } // Get org ID with fallback to default getOrgId(providedOrgId) { if (providedOrgId) { return providedOrgId; } throw new SourceSyncError('No organization ID provided'); } // API methods async validateApiKey() { return this.client .url('/v1/namespaces') .get() .json(); } async createNamespace(params) { return this.withTenant(params.tenantId) .url('/v1/namespaces') .json(params) .post() .json(); } async listNamespaces() { return this.client .url('/v1/namespaces') .get() .json(); } async getNamespace(namespaceId) { return this.client .url(`/v1/namespaces/${namespaceId}`) .get() .json(); } async updateNamespace(namespaceId, params) { return this.withTenant(params.tenantId) .url(`/v1/namespaces/${namespaceId}`) .json(params) .patch() .json(); } async deleteNamespace(namespaceId) { return this.client .url(`/v1/namespaces/${namespaceId}`) .delete() .json(); } async ingestText(params) { return this.withTenant(params.tenantId) .url('/v1/ingest/text') .json({ namespaceId: params.namespaceId, ingestConfig: params.ingestConfig }) .post() .json(); } async ingestFile(params) { const formData = new FormData(); formData.append('namespaceId', params.namespaceId); formData.append('file', params.file); if (params.metadata) { formData.append('metadata', JSON.stringify(params.metadata)); } if (params.chunkConfig) { formData.append('chunkConfig', JSON.stringify(params.chunkConfig)); } return this.withTenant(params.tenantId) .url('/v1/ingest/file') .formData(formData) .post() .json(); } async ingestUrls(params) { return this.withTenant(params.tenantId) .url('/v1/ingest/urls') .json({ namespaceId: params.namespaceId, ingestConfig: params.ingestConfig }) .post() .json(); } async ingestSitemap(params) { return this.withTenant(params.tenantId) .url('/v1/ingest/sitemap') .json({ namespaceId: params.namespaceId, ingestConfig: params.ingestConfig }) .post() .json(); } async ingestWebsite(params) { return this.withTenant(params.tenantId) .url('/v1/ingest/website') .json({ namespaceId: params.namespaceId, ingestConfig: params.ingestConfig }) .post() .json(); } async ingestConnector(params) { return this.withTenant(params.tenantId) .url('/v1/ingest/connector') .json({ namespaceId: params.namespaceId, ingestConfig: params.ingestConfig }) .post() .json(); } async getIngestJobRunStatus(params) { return this.withTenant(params.tenantId) .url(`/v1/ingest-job-runs/${params.ingestJobRunId}`) .query({ namespaceId: params.namespaceId }) .get() .json(); } async fetchDocuments(params) { return this.withTenant(params.tenantId) .url('/v1/documents') .query({ namespaceId: params.namespaceId, filterConfig: JSON.stringify(params.filterConfig), includeConfig: params.includeConfig ? JSON.stringify(params.includeConfig) : undefined, pagination: params.pagination ? JSON.stringify(params.pagination) : undefined }) .get() .json(); } async updateDocuments(params) { return this.withTenant(params.tenantId) .url('/v1/documents') .json({ namespaceId: params.namespaceId, filterConfig: params.filterConfig, data: params.data }) .patch() .json(); } async deleteDocuments(params) { return this.withTenant(params.tenantId) .url('/v1/documents') .json({ namespaceId: params.namespaceId, filterConfig: params.filterConfig }) .delete() .json(); } async resyncDocuments(params) { return this.withTenant(params.tenantId) .url('/v1/documents/resync') .json({ namespaceId: params.namespaceId, filterConfig: params.filterConfig }) .post() .json(); } async semanticSearch(params) { return this.withTenant(params.tenantId) .url('/v1/search/semantic') .json({ namespaceId: params.namespaceId, query: params.query, topK: params.topK, scoreThreshold: params.scoreThreshold, filter: params.filter, searchType: params.searchType || 'SEMANTIC' }) .post() .json(); } async hybridSearch(params) { return this.withTenant(params.tenantId) .url('/v1/search/hybrid') .json({ namespaceId: params.namespaceId, query: params.query, topK: params.topK, scoreThreshold: params.scoreThreshold, filter: params.filter, hybridConfig: params.hybridConfig, searchType: params.searchType || 'HYBRID' }) .post() .json(); } async createConnection(params) { return this.withTenant(params.tenantId) .url('/v1/connections') .json({ namespaceId: params.namespaceId, name: params.name, connector: params.connector, clientRedirectUrl: params.clientRedirectUrl }) .post() .json(); } async listConnections(params) { return this.withTenant(params.tenantId) .url('/v1/connections') .query({ namespaceId: params.namespaceId, connector: params.connector }) .get() .json(); } async getConnection(params) { return this.withTenant(params.tenantId) .url(`/v1/connections/${params.connectionId}`) .query({ namespaceId: params.namespaceId }) .get() .json(); } async updateConnection(params) { return this.withTenant(params.tenantId) .url(`/v1/connections/${params.connectionId}`) .json({ namespaceId: params.namespaceId, name: params.name, clientRedirectUrl: params.clientRedirectUrl }) .patch() .json(); } async revokeConnection(params) { return this.withTenant(params.tenantId) .url(`/v1/connections/${params.connectionId}/revoke`) .json({ namespaceId: params.namespaceId }) .post() .json(); } } // Create and export a default API client instance export const apiClient = new SourceSyncApiClient(); /** * API client for SourceSyncAI * This is a placeholder implementation */ export const apiClientPlaceholder = { // Add API client methods here async get(endpoint, params) { console.log(`GET ${endpoint}`, params); return { success: true }; }, async post(endpoint, data) { console.log(`POST ${endpoint}`, data); return { success: true }; }, async put(endpoint, data) { console.log(`PUT ${endpoint}`, data); return { success: true }; }, async delete(endpoint, params) { console.log(`DELETE ${endpoint}`, params); return { success: true }; } };