Skip to main content
Glama

Consumer Rights Wiki MCP Server

index.ts14 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import axios from 'axios'; const WIKI_BASE_URL = 'https://consumerrights.wiki'; const API_ENDPOINT = `${WIKI_BASE_URL}/api.php`; interface WikiResponse { batchcomplete?: string; query?: any; parse?: any; error?: { code: string; info: string; }; } class ConsumerRightsWikiServer { private server: Server; constructor() { this.server = new Server( { name: 'consumer-rights-wiki-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); } private async makeApiRequest(params: Record<string, string>): Promise<WikiResponse> { try { const response = await axios.get(API_ENDPOINT, { params: { format: 'json', ...params, }, }); return response.data; } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to make API request: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'search_wiki', description: 'Search for articles in the Consumer Rights Wiki', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query', }, limit: { type: 'number', description: 'Number of results to return (default: 10, max: 50)', default: 10, }, }, required: ['query'], }, }, { name: 'get_page_content', description: 'Get the full content of a specific wiki page', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the wiki page', }, section: { type: 'number', description: 'Optional section number to retrieve', }, }, required: ['title'], }, }, { name: 'get_page_info', description: 'Get metadata about a wiki page', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the wiki page', }, }, required: ['title'], }, }, { name: 'get_recent_changes', description: 'Get recent changes to the wiki', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Number of recent changes to return (default: 10, max: 50)', default: 10, }, namespace: { type: 'number', description: 'Filter by namespace (0 = main articles)', }, }, }, }, { name: 'get_categories', description: 'Get all categories or pages in a specific category', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category name (without Category: prefix). If empty, lists all categories.', }, limit: { type: 'number', description: 'Number of results to return (default: 20, max: 50)', default: 20, }, }, }, }, { name: 'get_page_sections', description: 'Get the section structure of a wiki page', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the wiki page', }, }, required: ['title'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'search_wiki': return this.searchWiki(request.params.arguments); case 'get_page_content': return this.getPageContent(request.params.arguments); case 'get_page_info': return this.getPageInfo(request.params.arguments); case 'get_recent_changes': return this.getRecentChanges(request.params.arguments); case 'get_categories': return this.getCategories(request.params.arguments); case 'get_page_sections': return this.getPageSections(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); } private async searchWiki(args: any) { const { query, limit = 10 } = args; const data = await this.makeApiRequest({ action: 'query', list: 'search', srsearch: query, srlimit: Math.min(limit, 50).toString(), srprop: 'size|wordcount|timestamp|snippet', }); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const results = data.query?.search || []; return { content: [ { type: 'text', text: JSON.stringify({ query: query, totalResults: data.query?.searchinfo?.totalhits || 0, results: results.map((result: any) => ({ title: result.title, size: result.size, wordcount: result.wordcount, timestamp: result.timestamp, snippet: result.snippet.replace(/<[^>]*>/g, ''), // Remove HTML tags url: `${WIKI_BASE_URL}/${result.title.replace(/ /g, '_')}`, })), }, null, 2), }, ], }; } private async getPageContent(args: any) { const { title, section } = args; const params: Record<string, string> = { action: 'parse', page: title, prop: 'text|sections|categories|links|externallinks', disablelimitreport: '1', disableeditsection: '1', }; if (section !== undefined) { params.section = section.toString(); } const data = await this.makeApiRequest(params); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const parse = data.parse; if (!parse) { throw new McpError(ErrorCode.InvalidRequest, `Page '${title}' not found`); } // Extract text content and remove HTML const htmlContent = parse.text['*']; const textContent = htmlContent .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '') .replace(/<[^>]*>/g, '') .replace(/&nbsp;/g, ' ') .replace(/&amp;/g, '&') .replace(/&lt;/g, '<') .replace(/&gt;/g, '>') .replace(/&quot;/g, '"') .replace(/&#039;/g, "'") .replace(/\n\s*\n/g, '\n\n') .trim(); return { content: [ { type: 'text', text: JSON.stringify({ title: parse.title, pageid: parse.pageid, content: textContent, categories: parse.categories?.map((cat: any) => cat['*']) || [], sections: parse.sections || [], internalLinks: parse.links?.map((link: any) => link['*']) || [], externalLinks: parse.externallinks || [], url: `${WIKI_BASE_URL}/${title.replace(/ /g, '_')}`, }, null, 2), }, ], }; } private async getPageInfo(args: any) { const { title } = args; const data = await this.makeApiRequest({ action: 'query', titles: title, prop: 'info|revisions|categories', inprop: 'protection|talkid|watched|watchers|visitingwatchers|notificationtimestamp|subjectid|url|readable|preload|displaytitle', rvprop: 'timestamp|user|comment|size', rvlimit: '1', }); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const pages = data.query?.pages; const pageId = Object.keys(pages)[0]; const pageInfo = pages[pageId]; if (pageInfo.missing) { throw new McpError(ErrorCode.InvalidRequest, `Page '${title}' not found`); } return { content: [ { type: 'text', text: JSON.stringify({ title: pageInfo.title, pageid: pageInfo.pageid, namespace: pageInfo.ns, length: pageInfo.length, touched: pageInfo.touched, lastrevid: pageInfo.lastrevid, counter: pageInfo.counter, fullurl: pageInfo.fullurl, editurl: pageInfo.editurl, canonicalurl: pageInfo.canonicalurl, readable: pageInfo.readable, categories: pageInfo.categories?.map((cat: any) => cat.title) || [], lastRevision: pageInfo.revisions?.[0] || null, protection: pageInfo.protection || [], }, null, 2), }, ], }; } private async getRecentChanges(args: any) { const { limit = 10, namespace } = args; const params: Record<string, string> = { action: 'query', list: 'recentchanges', rcprop: 'title|timestamp|user|comment|sizes|flags', rclimit: Math.min(limit, 50).toString(), }; if (namespace !== undefined) { params.rcnamespace = namespace.toString(); } const data = await this.makeApiRequest(params); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const changes = data.query?.recentchanges || []; return { content: [ { type: 'text', text: JSON.stringify({ recentChanges: changes.map((change: any) => ({ title: change.title, timestamp: change.timestamp, user: change.user, comment: change.comment, oldSize: change.oldlen, newSize: change.newlen, sizeChange: change.newlen - change.oldlen, type: change.type, isNew: change.new === '', isMinor: change.minor === '', isBot: change.bot === '', url: `${WIKI_BASE_URL}/${change.title.replace(/ /g, '_')}`, })), }, null, 2), }, ], }; } private async getCategories(args: any) { const { category, limit = 20 } = args; if (category) { // Get pages in a specific category const data = await this.makeApiRequest({ action: 'query', list: 'categorymembers', cmtitle: `Category:${category}`, cmlimit: Math.min(limit, 50).toString(), cmprop: 'ids|title|timestamp', }); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const members = data.query?.categorymembers || []; return { content: [ { type: 'text', text: JSON.stringify({ category: `Category:${category}`, members: members.map((member: any) => ({ title: member.title, pageid: member.pageid, timestamp: member.timestamp, url: `${WIKI_BASE_URL}/${member.title.replace(/ /g, '_')}`, })), }, null, 2), }, ], }; } else { // List all categories const data = await this.makeApiRequest({ action: 'query', list: 'allcategories', aclimit: Math.min(limit, 50).toString(), acprop: 'size', }); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const categories = data.query?.allcategories || []; return { content: [ { type: 'text', text: JSON.stringify({ categories: categories.map((cat: any) => ({ name: cat['*'], size: cat.size, url: `${WIKI_BASE_URL}/Category:${cat['*'].replace(/ /g, '_')}`, })), }, null, 2), }, ], }; } } private async getPageSections(args: any) { const { title } = args; const data = await this.makeApiRequest({ action: 'parse', page: title, prop: 'sections', }); if (data.error) { throw new McpError(ErrorCode.InternalError, data.error.info); } const sections = data.parse?.sections || []; return { content: [ { type: 'text', text: JSON.stringify({ title: data.parse?.title, sections: sections.map((section: any) => ({ index: section.index, level: parseInt(section.level), line: section.line, number: section.number, anchor: section.anchor, byteoffset: section.byteoffset, })), }, null, 2), }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Consumer Rights Wiki MCP server running on stdio'); } } const server = new ConsumerRightsWikiServer(); server.run().catch(console.error);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Cicatriiz/consumer-rights-wiki-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server