Clojars-MCP-Server

by Bigsy
Verified
  • src
#!/usr/bin/env node 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'; interface ClojarsResponse { group_name: string; jar_name: string; latest_release: string; latest_version: string; } const isValidDependencyArgs = (args: any): args is { dependency: string } => typeof args === 'object' && args !== null && typeof args.dependency === 'string' && args.dependency.includes('/'); const isValidVersionCheckArgs = (args: any): args is { dependency: string; version: string } => typeof args === 'object' && args !== null && typeof args.dependency === 'string' && args.dependency.includes('/') && typeof args.version === 'string'; class ClojarsServer { private server: Server; private axiosInstance; constructor() { this.server = new Server( { name: 'clojars-deps-server', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); this.axiosInstance = axios.create({ baseURL: 'https://repo.clojars.org', headers: { 'Accept': 'application/json', }, }); this.setupToolHandlers(); // Error handling this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'get_clojars_latest_version', description: 'Get the latest version of a Clojars dependency (Maven artifact)', inputSchema: { type: 'object', properties: { dependency: { type: 'string', description: 'Clojars dependency name in format "group/artifact" (e.g. "metosin/reitit")', }, }, required: ['dependency'], }, }, { name: 'check_clojars_version_exists', description: 'Check if a specific version of a Clojars dependency exists', inputSchema: { type: 'object', properties: { dependency: { type: 'string', description: 'Clojars dependency name in format "group/artifact" (e.g. "metosin/reitit")', }, version: { type: 'string', description: 'Version to check (e.g. "0.7.2")', }, }, required: ['dependency', 'version'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === 'get_clojars_latest_version') { if (!isValidDependencyArgs(request.params.arguments)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid dependency format. Expected "group/artifact" (e.g. "metosin/reitit")' ); } const [group, artifact] = request.params.arguments.dependency.split('/'); try { const response = await this.axiosInstance.get<string>( `/${group.replace(/\./g, '/')}/${artifact}/maven-metadata.xml` ); // Extract latest version from XML const versionMatch = response.data.match(/<latest>(.*?)<\/latest>/); const releaseMatch = response.data.match(/<release>(.*?)<\/release>/); const latestVersion = releaseMatch?.[1] || versionMatch?.[1]; if (!latestVersion) { throw new Error('Could not find version information in metadata'); } return { content: [ { type: 'text', text: JSON.stringify( { dependency: `${group}/${artifact}`, latest_version: latestVersion }, null, 2 ), }, ], }; } catch (error) { if (axios.isAxiosError(error)) { const message = error.response?.status === 404 ? `Dependency ${group}/${artifact} not found on Clojars` : `Clojars API error: ${error.message}`; return { content: [ { type: 'text', text: message, }, ], isError: true, }; } throw error; } } else if (request.params.name === 'check_clojars_version_exists') { if (!isValidVersionCheckArgs(request.params.arguments)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments. Expected "dependency" in format "group/artifact" and "version"' ); } const [group, artifact] = request.params.arguments.dependency.split('/'); const version = request.params.arguments.version; try { const response = await this.axiosInstance.get<string>( `/${group.replace(/\./g, '/')}/${artifact}/maven-metadata.xml` ); // Extract all versions from XML const versionsMatch = response.data.match(/<version>(.*?)<\/version>/g); const versions = versionsMatch?.map(v => v.replace(/<\/?version>/g, '')) || []; const exists = versions.includes(version); return { content: [ { type: 'text', text: JSON.stringify( { dependency: `${group}/${artifact}`, version: version, exists: exists }, null, 2 ), }, ], }; } catch (error) { if (axios.isAxiosError(error)) { const message = error.response?.status === 404 ? `Dependency ${group}/${artifact} not found on Clojars` : `Clojars API error: ${error.message}`; return { content: [ { type: 'text', text: message, }, ], isError: true, }; } throw error; } } else { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Clojars MCP server running on stdio'); } } const server = new ClojarsServer(); server.run().catch(console.error);