ai-knowledge-hub
Version:
MCP server that provides unified access to organizational knowledge across multiple platforms (local docs, Guru, Notion)
129 lines • 4.89 kB
JavaScript
import fetch from 'node-fetch';
/**
* Guru API Service for interacting with Guru's REST API
*/
export class GuruService {
baseUrl = 'https://api.getguru.com/api/v1';
fileBaseUrl = 'https://content.api.getguru.com';
/**
* Get Guru API credentials from environment variables
* Expects GURU_TOKEN in format "username:token" or "collection_id:token"
*/
getCredentials() {
const guruToken = process.env.GURU_TOKEN;
if (guruToken === null || guruToken === undefined || guruToken.length === 0) {
return null;
}
if (!guruToken.includes(':')) {
throw new Error('GURU_TOKEN must be in format "username:token" or "collection_id:token"');
}
const [username, token] = guruToken.split(':', 2);
if (!username || !token) {
throw new Error('Invalid GURU_TOKEN format. Expected "username:token" or "collection_id:token"');
}
return { username, token };
}
/**
* Make authenticated request to Guru API
*/
async makeRequest(url, options = {}) {
const credentials = this.getCredentials();
if (!credentials) {
throw new Error('GURU_TOKEN environment variable is required');
}
const { username, token } = credentials;
const auth = Buffer.from(`${username}:${token}`).toString('base64');
const response = await fetch(url, {
...options,
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
...(options.headers ?? {}),
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Guru API error (${response.status}): ${errorText}`);
}
const jsonResponse = await response.json();
return jsonResponse;
}
/**
* Search for cards using Guru's search API
*/
async searchCards(params = {}) {
const queryParams = new URLSearchParams();
if (params.q !== null && params.q !== undefined && params.q.length > 0) {
queryParams.append('q', params.q);
}
if (params.searchTerms !== null && params.searchTerms !== undefined && params.searchTerms.length > 0) {
queryParams.append('searchTerms', params.searchTerms);
}
if (params.showArchived !== undefined) {
queryParams.append('showArchived', params.showArchived.toString());
}
if (params.maxResults !== undefined && params.maxResults > 0) {
queryParams.append('maxResults', params.maxResults.toString());
}
if (params.sortField !== null && params.sortField !== undefined && params.sortField.length > 0) {
queryParams.append('sortField', params.sortField);
}
if (params.sortOrder !== null && params.sortOrder !== undefined && params.sortOrder.length > 0) {
queryParams.append('sortOrder', params.sortOrder);
}
const url = `${this.baseUrl}/search/query?${queryParams.toString()}`;
return this.makeRequest(url);
}
/**
* Get a specific card by ID
*/
async getCard(cardId) {
const url = `${this.baseUrl}/cards/${cardId}`;
return this.makeRequest(url);
}
/**
* Extract attachment URLs from card content
*/
extractAttachments(htmlContent) {
const attachmentRegex = /https:\/\/content\.api\.getguru\.com\/files\/view\/([a-f0-9-]+)/g;
const attachments = [];
let match;
while ((match = attachmentRegex.exec(htmlContent)) !== null) {
attachments.push({
url: match[0],
fileId: match[1],
});
}
return attachments;
}
/**
* Download attachment by file ID
*/
async downloadAttachment(fileId) {
const credentials = this.getCredentials();
if (!credentials) {
throw new Error('GURU_TOKEN environment variable is required');
}
const { username, token } = credentials;
const auth = Buffer.from(`${username}:${token}`).toString('base64');
const url = `${this.fileBaseUrl}/files/view/${fileId}`;
const response = await fetch(url, {
headers: {
'Authorization': `Basic ${auth}`,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to download attachment (${response.status}): ${errorText}`);
}
return Buffer.from(await response.arrayBuffer());
}
/**
* Get attachments for a specific card
*/
async getCardAttachments(cardId) {
const card = await this.getCard(cardId);
return this.extractAttachments(card.content);
}
}
//# sourceMappingURL=guru.js.map