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);
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. While 'simulate' implies generation rather than modification of existing data, it doesn't address critical behavioral aspects: whether this is a read-only operation, what computational resources it requires, whether it's deterministic with the seed parameter, or what the output looks like beyond format options.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at just 5 words: 'Simulate phylogenetic tree and sequence evolution'. It's front-loaded with the core functionality and contains zero wasted words or redundant information. Every word earns its place in communicating the tool's purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with 9 parameters, no annotations, and no output schema, the description is inadequate. It doesn't explain what the tool returns (sequences? trees? both?), doesn't provide behavioral context about computational requirements or limitations, and doesn't help users understand when to choose this over sibling tools. The 100% schema coverage helps, but the description itself lacks completeness for such a sophisticated simulation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema. The baseline of 3 is appropriate since the schema does the heavy lifting, though the description doesn't compensate with any higher-level explanation of parameter relationships or typical usage patterns.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Simulate phylogenetic tree and sequence evolution'. This specifies both the tree simulation and sequence evolution components, making it more specific than just 'simulate phylogeny'. However, it doesn't explicitly differentiate from sibling tools like 'evolve_sequence' or 'mutate_sequence', which might have overlapping functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With sibling tools like 'evolve_sequence', 'generate_dna_sequence', and 'mutate_sequence', there's no indication of how this tool differs in application or which scenarios warrant its use over those alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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