Skip to main content
Glama
coreymhudson

MCP Sequence Simulation Server

by coreymhudson

simulate_phylogeny

Simulate phylogenetic trees and sequence evolution for DNA or protein sequences using evolutionary models and substitution patterns.

Instructions

Simulate phylogenetic tree and sequence evolution

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
rootSequenceYesRoot sequence for phylogenetic simulation
treeStructureNoNewick format tree or 'random' for random tree
numTaxaNoNumber of taxa for random tree generation
mutationRateNoMutation rate per branch length unit
branchLengthVariationNoVariation in branch lengths (0-1)
molecularClockNoUse molecular clock (uniform rates)
substitutionModelNoSubstitution model: 'JC69', 'K80', 'HKY85', or 'GTR'
seedNoRandom seed for reproducible results (optional)
outputFormatNoOutput format: 'fasta', 'nexus', or 'phylip'

Implementation Reference

  • Main handler function that executes the simulate_phylogeny tool, including tree generation, sequence evolution, and output formatting
    async handler({ rootSequence, treeStructure = "random", numTaxa = 5, mutationRate = 0.1, branchLengthVariation = 0.2, molecularClock = true, substitutionModel = "JC69", seed, outputFormat = "fasta" }: { rootSequence: string; treeStructure?: string; numTaxa?: number; mutationRate?: number; branchLengthVariation?: number; molecularClock?: boolean; substitutionModel?: string; seed?: number; outputFormat?: string; }) { const generator = new SequenceGenerator(seed); const isDNA = /^[ATGC]+$/i.test(rootSequence.replace(/\s/g, '')); let tree: PhyloNode; if (treeStructure === "random") { tree = generateRandomTree(numTaxa, generator); } else { tree = parseNewick(treeStructure); } if (!molecularClock) { addBranchLengthVariation(tree, branchLengthVariation, generator); } const sequences = evolveSequencesOnTree(tree, rootSequence, mutationRate, substitutionModel, generator, isDNA); let output = ''; const taxaNames = Object.keys(sequences); switch (outputFormat) { case 'fasta': output = Object.entries(sequences).map(([name, seq]) => `>${name}\n${seq}` ).join('\n\n'); break; case 'nexus': output = generateNexusFormat(sequences, tree); break; case 'phylip': output = generatePhylipFormat(sequences); break; } const treeStats = calculateTreeStats(tree); const sequenceStats = calculateSequenceDivergence(sequences, rootSequence); return { content: [{ type: "text", text: JSON.stringify({ parameters: { numTaxa: taxaNames.length, mutationRate, substitutionModel, molecularClock, seed: seed || "random" }, treeStatistics: treeStats, sequenceStatistics: sequenceStats, newickTree: treeToNewick(tree), sequences: outputFormat === 'fasta' ? sequences : undefined, formattedOutput: output }, null, 2) }] }; }
  • Tool definition containing input schema with all parameters for phylogenetic simulation including rootSequence, treeStructure, numTaxa, mutationRate, substitutionModel, etc.
    export const simulatePhylogeny = { definition: { name: "simulate_phylogeny", description: "Simulate phylogenetic tree and sequence evolution", inputSchema: { type: "object", properties: { rootSequence: { type: "string", description: "Root sequence for phylogenetic simulation" }, treeStructure: { type: "string", description: "Newick format tree or 'random' for random tree", }, numTaxa: { type: "number", description: "Number of taxa for random tree generation", minimum: 2 }, mutationRate: { type: "number", description: "Mutation rate per branch length unit", minimum: 0 }, branchLengthVariation: { type: "number", description: "Variation in branch lengths (0-1)", minimum: 0, maximum: 1 }, molecularClock: { type: "boolean", description: "Use molecular clock (uniform rates)", default: true }, substitutionModel: { type: "string", description: "Substitution model: 'JC69', 'K80', 'HKY85', or 'GTR'", enum: ["JC69", "K80", "HKY85", "GTR"] }, seed: { type: "number", description: "Random seed for reproducible results (optional)" }, outputFormat: { type: "string", description: "Output format: 'fasta', 'nexus', or 'phylip'", enum: ["fasta", "nexus", "phylip"] } }, required: ["rootSequence"] }, },
  • src/server.ts:26-37 (registration)
    Tool registration in ListToolsRequestSchema handler that exposes the simulate_phylogeny tool definition
    server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ generateDNA.definition, generateProtein.definition, mutateSequence.definition, evolveSequence.definition, simulatePhylogeny.definition, simulateFastq.definition, ], }; });
  • src/server.ts:94-105 (registration)
    Handler routing for simulate_phylogeny tool call in CallToolRequestSchema
    case "simulate_phylogeny": return await simulatePhylogeny.handler(args as { rootSequence: string; treeStructure?: string; numTaxa?: number; mutationRate?: number; branchLengthVariation?: number; molecularClock?: boolean; substitutionModel?: string; seed?: number; outputFormat?: string; });
  • SequenceGenerator utility class used by the handler for random number generation with optional seed, providing methods for DNA/protein generation and mutation
    export class SequenceGenerator { public rng: () => number; constructor(seed?: number) { if (seed !== undefined) { let s = seed; this.rng = () => { s = Math.sin(s) * 10000; return s - Math.floor(s); }; } else { this.rng = Math.random; } } generateRandomDNA(length: number, gcContent: number = 0.5): string { const gcProb = gcContent / 2; const atProb = (1 - gcContent) / 2; let sequence = ''; for (let i = 0; i < length; i++) { const rand = this.rng(); if (rand < atProb) { sequence += 'A'; } else if (rand < atProb * 2) { sequence += 'T'; } else if (rand < atProb * 2 + gcProb) { sequence += 'G'; } else { sequence += 'C'; } } return sequence; } generateRandomProtein(length: number): string { let sequence = ''; for (let i = 0; i < length; i++) { const index = Math.floor(this.rng() * AMINO_ACIDS.length); sequence += AMINO_ACIDS[index]; } return sequence; } mutateDNA(sequence: string, params: MutationParameters): string { const { substitutionRate = 0.01, insertionRate = 0.001, deletionRate = 0.001, transitionBias = 2.0 } = params; let mutated = sequence.split(''); for (let i = 0; i < mutated.length; i++) { if (this.rng() < substitutionRate) { const currentBase = mutated[i]; let newBase: string; if (this.rng() < transitionBias / (transitionBias + 1)) { newBase = this.getTransition(currentBase); } else { newBase = this.getTransversion(currentBase); } mutated[i] = newBase; } if (this.rng() < insertionRate) { const randomBase = DNA_BASES[Math.floor(this.rng() * DNA_BASES.length)]; mutated.splice(i, 0, randomBase); i++; } if (this.rng() < deletionRate && mutated.length > 1) { mutated.splice(i, 1); i--; } } return mutated.join(''); } private getTransition(base: string): string { switch (base) { case 'A': return 'G'; case 'G': return 'A'; case 'T': return 'C'; case 'C': return 'T'; default: return base; } } private getTransversion(base: string): string { const transversions: Record<string, string[]> = { 'A': ['T', 'C'], 'T': ['A', 'G'], 'G': ['T', 'C'], 'C': ['A', 'G'] }; const options = transversions[base] || [base]; return options[Math.floor(this.rng() * options.length)]; } evolveSequence(sequence: string, params: EvolutionParameters): string[] { let population = Array(params.populationSize).fill(sequence); const generations = [sequence]; for (let gen = 0; gen < params.generations; gen++) { population = population.map(seq => this.mutateDNA(seq, { substitutionRate: params.mutationRate }) ); if (params.selectionPressure) { population.sort((a, b) => this.calculateFitness(b) - this.calculateFitness(a)); population = population.slice(0, Math.floor(params.populationSize / 2)); population = population.concat(population); } generations.push(population[0]); } return generations; } private calculateFitness(sequence: string): number { const gcContent = (sequence.match(/[GC]/g) || []).length / sequence.length; return Math.abs(gcContent - 0.5); } }

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/coreymhudson/mcp-sequence-simulation'

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