avr-docs-mcp
Version:
MCP server for AVR documentation
293 lines • 10.5 kB
JavaScript
import axios from 'axios';
export class WikiService {
client;
baseUrl;
apiKey;
logLevel;
constructor() {
const baseUrl = process.env.WIKI_JS_BASE_URL;
const apiKey = process.env.WIKI_JS_API_KEY;
this.logLevel = process.env.LOG_LEVEL || 'info';
if (!baseUrl) {
throw new Error('WIKI_JS_BASE_URL environment variable is required');
}
if (!apiKey) {
throw new Error('WIKI_JS_API_KEY environment variable is required');
}
this.baseUrl = baseUrl;
this.apiKey = apiKey;
// Remove trailing slash from base URL
this.baseUrl = this.baseUrl.replace(/\/$/, '');
this.client = axios.create({
baseURL: `${this.baseUrl}/graphql`,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
timeout: 10000,
});
this.log('info', `WikiService initialized with base URL: ${this.baseUrl}`);
}
log(level, message) {
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
const currentLevel = levels[this.logLevel] || 1;
const messageLevel = levels[level] || 1;
if (messageLevel >= currentLevel) {
const timestamp = new Date().toISOString();
console.error(`[${timestamp}] [${level.toUpperCase()}] WikiService: ${message}`);
}
}
/**
* Execute GraphQL query
*/
async executeQuery(query, variables = {}) {
try {
this.log('debug', `Executing GraphQL query: ${query.substring(0, 100)}...`);
this.log('debug', `Variables: ${JSON.stringify(variables)}`);
const response = await this.client.post('', {
query,
variables,
});
if (response.data.errors) {
this.log('error', `GraphQL errors: ${JSON.stringify(response.data.errors)}`);
throw new Error(`GraphQL errors: ${JSON.stringify(response.data.errors)}`);
}
return response.data.data;
}
catch (error) {
if (axios.isAxiosError(error)) {
this.log('error', `GraphQL request failed: ${error.response?.data?.message || error.message}`);
if (error.response?.data) {
this.log('error', `Response data: ${JSON.stringify(error.response.data)}`);
}
throw new Error(`GraphQL request failed: ${error.response?.data?.message || error.message}`);
}
throw error;
}
}
/**
* Search for pages in Wiki.JS
*/
async searchPages(query, page = 1, limit = 10) {
try {
this.log('debug', `Searching pages with query: "${query}", page: ${page}, limit: ${limit}`);
const graphqlQuery = `
query SearchPages($query: String!) {
pages {
search(query: $query) {
results {
id
title
description
path
locale
}
totalHits
}
}
}
`;
const result = await this.executeQuery(graphqlQuery, {
query,
});
const searchData = result.pages.search;
this.log('info', `Search completed. Found ${searchData.totalHits} pages`);
// Simulate pagination since Wiki.JS search doesn't support it natively
const startIndex = (page - 1) * limit;
const endIndex = startIndex + limit;
const paginatedResults = searchData.results.slice(startIndex, endIndex);
return {
pages: paginatedResults.map((page) => ({
id: page.id,
title: page.title,
description: page.description || '',
path: page.path,
locale: page.locale,
content: '', // Search results don't include content
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
publishedAt: new Date().toISOString(),
author: {
id: '0',
name: 'Unknown',
email: ''
},
tags: []
})),
total: searchData.totalHits,
page,
limit,
};
}
catch (error) {
this.log('error', `Search failed: ${error instanceof Error ? error.message : String(error)}`);
throw new Error(`Wiki.JS search failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* List all pages in Wiki.JS
*/
async listPages(page = 1, limit = 20) {
try {
this.log('debug', `Listing pages, page: ${page}, limit: ${limit}`);
const graphqlQuery = `
query ListPages {
pages {
list {
id
title
description
path
locale
contentType
isPublished
isPrivate
createdAt
updatedAt
tags
}
}
}
`;
const result = await this.executeQuery(graphqlQuery);
const allPages = result.pages.list;
// Simulate pagination since Wiki.JS list doesn't support it natively
const startIndex = (page - 1) * limit;
const endIndex = startIndex + limit;
const paginatedPages = allPages.slice(startIndex, endIndex);
this.log('info', `Page listing completed. Found ${allPages.length} pages`);
return {
pages: paginatedPages.map((page) => ({
id: page.id.toString(),
title: page.title,
description: page.description || '',
path: page.path,
locale: page.locale,
content: '', // List doesn't include content
createdAt: page.createdAt,
updatedAt: page.updatedAt,
publishedAt: page.isPublished ? page.updatedAt : undefined,
author: {
id: '0',
name: 'Unknown',
email: ''
},
tags: Array.isArray(page.tags) ? page.tags.map((tag) => tag.title) : []
})),
total: allPages.length,
page,
limit,
};
}
catch (error) {
this.log('error', `Page listing failed: ${error instanceof Error ? error.message : String(error)}`);
throw new Error(`Wiki.JS page listing failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Get a specific page by ID or path
*/
async getPage(pageId) {
try {
this.log('debug', `Getting page with ID/path: ${pageId}`);
// Check if pageId is numeric (ID) or string (path)
const isNumeric = /^\d+$/.test(pageId);
let graphqlQuery;
let variables;
if (isNumeric) {
// Get by ID
graphqlQuery = `
query GetPageById($id: Int!) {
pages {
single(id: $id) {
id
title
description
path
locale
content
contentType
isPublished
isPrivate
createdAt
updatedAt
tags {
id
title
}
authorId
authorName
authorEmail
}
}
}
`;
variables = { id: parseInt(pageId) };
}
else {
// Get by path
graphqlQuery = `
query GetPageByPath($path: String!, $locale: String!) {
pages {
singleByPath(path: $path, locale: $locale) {
id
title
description
path
locale
content
contentType
isPublished
isPrivate
createdAt
updatedAt
tags {
id
title
}
authorId
authorName
authorEmail
}
}
}
`;
variables = { path: pageId, locale: 'en' };
}
const result = await this.executeQuery(graphqlQuery, variables);
const page = isNumeric ? result.pages.single : result.pages.singleByPath;
if (!page) {
throw new Error(`Page not found: ${pageId}`);
}
this.log('info', `Page retrieved successfully: ${page.title}`);
return {
id: page.id.toString(),
title: page.title,
description: page.description || '',
path: page.path,
locale: page.locale,
content: page.content,
createdAt: page.createdAt,
updatedAt: page.updatedAt,
publishedAt: page.isPublished ? page.updatedAt : undefined,
author: {
id: page.authorId.toString(),
name: page.authorName || 'Unknown',
email: page.authorEmail || ''
},
tags: Array.isArray(page.tags) ? page.tags.map((tag) => tag.title) : []
};
}
catch (error) {
this.log('error', `Page retrieval failed: ${error instanceof Error ? error.message : String(error)}`);
throw new Error(`Wiki.JS page retrieval failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Get a specific page by path
*/
async getPageByPath(path) {
return this.getPage(path);
}
}
//# sourceMappingURL=wikiService.js.map