Skip to main content
Glama
index.ts4.95 kB
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import path from 'path'; import fs from 'fs/promises'; interface PipelineInfo { name: string; path: string; config?: string; modules?: string[]; } const server = new McpServer({ name: "nf-core-mcp", version: "1.0.0" }); // Tool to list all nf-core pipelines server.tool( "list-pipelines", {}, async () => { try { const pipelines = ['rnaseq', 'sarek', 'modules', 'tools']; const pipelineDetails = await Promise.all( pipelines.map(async (name) => { const configPath = path.join(process.cwd(), name, "nextflow.config"); try { const config = await fs.readFile(configPath, 'utf-8'); return { name, hasConfig: true, configPath }; } catch { return { name, hasConfig: false }; } }) ); return { content: [{ type: "text", text: JSON.stringify(pipelineDetails, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error listing pipelines: ${error}` }], isError: true }; } } ); // Tool to get pipeline modules server.tool( "get-pipeline-modules", { pipeline: z.string() }, async ({ pipeline }) => { try { const modulesPath = path.join(process.cwd(), pipeline, "modules.json"); const modulesContent = await fs.readFile(modulesPath, 'utf-8'); const modules = JSON.parse(modulesContent); return { content: [{ type: "text", text: JSON.stringify(modules, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error getting pipeline modules: ${error}` }], isError: true }; } } ); // Tool to search across pipeline files server.tool( "search-pipelines", { query: z.string(), pipeline: z.string().optional() }, async ({ query, pipeline }) => { try { const searchPath = pipeline ? path.join(process.cwd(), pipeline) : process.cwd(); const results: string[] = []; async function searchDir(dir: string) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory() && !entry.name.startsWith('.')) { await searchDir(fullPath); } else if (entry.isFile() && (entry.name.endsWith('.nf') || entry.name.endsWith('.config') || entry.name.endsWith('.json'))) { try { const content = await fs.readFile(fullPath, 'utf-8'); if (content.includes(query)) { results.push(fullPath); } } catch (err) { console.error(`Error reading ${fullPath}: ${err}`); } } } } await searchDir(searchPath); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error searching pipelines: ${error}` }], isError: true }; } } ); // Resource to get pipeline configuration server.resource( "pipeline-config", new ResourceTemplate("pipeline://{name}/config", { list: undefined }), async (uri: URL, params: Record<string, unknown>) => { try { const name = params.name as string; const configPath = path.join(process.cwd(), name, "nextflow.config"); const config = await fs.readFile(configPath, 'utf-8'); return { contents: [{ uri: uri.href, text: config }] }; } catch (error) { throw new Error(`Failed to read pipeline config: ${error}`); } } ); // Resource to get pipeline workflow server.resource( "pipeline-workflow", new ResourceTemplate("pipeline://{name}/workflow", { list: undefined }), async (uri: URL, params: Record<string, unknown>) => { try { const name = params.name as string; const mainPath = path.join(process.cwd(), name, "main.nf"); const workflow = await fs.readFile(mainPath, 'utf-8'); return { contents: [{ uri: uri.href, text: workflow }] }; } catch (error) { throw new Error(`Failed to read pipeline workflow: ${error}`); } } ); // Start the server const transport = new StdioServerTransport(); server.connect(transport);

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/wjlim/nf-core_mcp'

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