sourcesyncai-mcp
Version:
[](https://smithery.ai/server/@pbteja1998/sourcesyncai-mcp)
405 lines (404 loc) • 12.6 kB
JavaScript
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 };
}
};