Skip to main content
Glama

play

Execute MIDI music playback with customizable BPM and instrument tracks, generating audio output from composed scores.

Instructions

Plays a music score

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bpmYesThe BPM of the song
midiOuputNameNoThe MIDI output name to use. Don't add unless requested.
tracksYesArray of tracks to play. - If you want to make it a drum track, set the instrumentName to 'drums'. and Use the 'drums' proprerty in notation. - Otherwise, use the 'note' property in 'notes'. - Unless asked otherwise, add 10 bars assuming 4/4 time signature. - Unless asked otherwise, make sure that each has the same number of bars. - Sometimes you make some tracks longer or shorter than others. Avoid that.

Implementation Reference

  • The handler function for the MCP 'play' tool. Initializes a new Midi instance, calls init(), plays the score using the provided input, and returns a text response.
    handler: async ( input: PlayMusicMcpToolInput ): Promise<{ content: { type: "text"; text: string }[] }> => { const midi = new Midi(); await midi.init(); await midi.playScore({ bpm: input.bpm, midiOuputName: input.midiOuputName, tracks: input.tracks as any, }); return { content: [ { type: "text", text: "Playing music score", }, ], }; },
  • Zod input schema defining the structure for bpm, midiOutputName, and tracks including instruments, channels, notes, drums, and durations.
    const inputSchema = { bpm: z.number().describe("The BPM of the song"), midiOuputName: z .string() .optional() .describe("The MIDI output name to use. Don't add unless requested."), tracks: z .array( z.object({ instrumentName: z .enum([ "drums", ...(Object.keys(Instruments) as [string, ...string[]]), ]) .describe( `The instrument of the track. Available instruments: ${Object.keys( Instruments ).join( ", " )}. If you want to make it a drum track, set the instrumentName to 'drums'.` ), channel: z .number() .optional() .describe( "The MIDI channel of the track. Don't add unless requested." ), notes: z .array( z.object({ note: z .array(z.string()) .nullable() .optional() .describe( "The notes to play. Use array for chords. Or put one element for single note. Use an empty array for rest/silence" ), drums: z .array(z.string()) .nullable() .optional() .describe( "The drums to play. Use array for multiple drums. Use an empty array for rest/silence. Available drums: " + Object.keys(Drums).join(", ") ), noteDuration: z .string() .describe( "The note duration, e.g.: 1/32, 1/16, 1/8, 1/4, 1/2, 1" ), }) ) .describe( `"Array of notes or drums to play with timing. - If you want to play a drum track, use the drums property. - If you want to play a non-drum instrument, use the note property. Example: [{note: ['C2', 'E2'], noteDuration: '1/8'}, {note: [], noteDuration: '1/8'}, {note: ['G2'], noteDuration: '1/8'}, {note: null, noteDuration: '1/8'}]. Or [{drums: ['bass_drum_1', 'closed_hi_hat'], noteDuration: '1/8'}, {drums: ['closed_hi_hat'], noteDuration: '1/8'}, {drums: ['closed_hi_hat', 'acoustic_snare'], noteDuration: '1/8'}, {drums: ['closed_hi_hat'], noteDuration: '1/8'}]. An array could be a bar or several bars. -------------------------------------------------------------------------------- IMPORTANT FOR FOR DRUM TRACKS: - notes are played one after another. - we wait until the whole duration of a note before we play another one. - So don't do this: "notes" :[{"drums: ["bass_drum_1", "closed_hi_hat"], noteDuration: '1/4'},{"drums: [ "closed_hi_hat"], noteDuration: '1/4'} {drums: ['closed_hi_hat', 'acoustic_snare'], noteDuration: '1/4'}...].` ), }) ) .describe( `Array of tracks to play. - If you want to make it a drum track, set the instrumentName to 'drums'. and Use the 'drums' proprerty in notation. - Otherwise, use the 'note' property in 'notes'. - Unless asked otherwise, add 10 bars assuming 4/4 time signature. - Unless asked otherwise, make sure that each has the same number of bars. - Sometimes you make some tracks longer or shorter than others. Avoid that. ` ), };
  • Registers the 'play' tool (via PlayMusicMcpTool) with the MCP server, providing name, metadata including inputSchema, and handler.
    server.registerTool( PlayMusicMcpTool.name, { title: PlayMusicMcpTool.title, description: PlayMusicMcpTool.description, inputSchema: PlayMusicMcpTool.inputSchema, }, PlayMusicMcpTool.handler );
  • The playScore method in the Midi class, which is called by the tool handler. It opens the MIDI output, sets BPM, and concurrently plays all tracks.
    public async playScore(score: Score) { await this.openMidiOut(score.midiOuputName); this.setBpm(score.bpm); return Promise.all(score.tracks.map((track) => this.playTrack(track))); }

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/mikeborozdin/vibe-composer-midi-mcp'

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