add_notes_to_clip
Add MIDI notes to a specific clip in Ableton Live using clip ID and note details like pitch, duration, and velocity for precise music production.
Instructions
Add notes to clip by clip id
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| clip_id | Yes | ||
| notes | Yes | [array] the notes to add. |
Implementation Reference
- src/tools/clip-tools.ts:85-99 (handler)The main execution handler for the 'add_notes_to_clip' tool. It retrieves the clip by ID, creates a note snapshot for undo support, sets the new notes on the clip, and returns success.@tool({ name: 'add_notes_to_clip', description: 'Add notes to clip by clip id', enableSnapshot: true, paramsSchema: { notes: z.array(NOTE).describe('[array] the notes to add.'), clip_id: z.string() } }) async addClipNotes({ notes, clip_id, historyId }: { notes: Note[], clip_id: string, historyId: number }) { const clip = getClipById(clip_id) await createNoteSnapshot(clip, historyId) await clip.setNotes(notes) return Result.ok() }
- src/types/zod-types.ts:25-32 (schema)Zod schema for individual NOTE objects, used in the tool's input schema as z.array(NOTE).export const NOTE = createZodSchema<Note>({ pitch: z.number().min(0).max(127).describe('[int] the MIDI note number, 0...127, 60 is C3.'), time: z.number().describe('[float] the note start time in beats of absolute clip time.'), duration: z.number().describe('[float] the note length in beats.'), velocity: z.number().min(0).max(127).default(100) .describe('[float] the note velocity, 0 ... 127 (100 by default).'), muted: z.boolean().default(false).describe('[bool] true = the note is deactivated (false by default).') })
- src/main.ts:39-42 (registration)The ClipTools class containing the add_notes_to_clip handler is registered here in the tools array for the MCP server.await startMcp({ // Register tool classes, make decorators available tools: [BrowserTools, ClipTools, DeviceTools, HistoryTools, SongTools, TrackTools, ExtraTools, ApplicationTools] })
- src/main.ts:8-8 (registration)Import of the ClipTools class that defines the tool.import ClipTools from './tools/clip-tools.js'
- src/tools/clip-tools.ts:5-99 (helper)Imports of helper functions used in the handler: getClipById to retrieve clip, createNoteSnapshot for undo history.import { batchModifyClipProp, getClipProps, NoteToNoteExtended } from '../utils/obj-utils.js' import { Result } from '../utils/common.js' import { getClipById } from '../utils/obj-utils.js' import { createNoteSnapshot, getNotes, removeNotesExtended, replaceClipNotesExtended } from '../utils/clip-utils.js' import { ableton } from '../ableton.js' class ClipTools { @tool({ name: 'get_clip_properties', description: 'Get clip properties by clip id. To get specific properties, set the corresponding property name to true in the properties parameter.', paramsSchema: { clip_id: z.string(), properties: ClipGettableProp, } }) async getClipInfoById({ clip_id, properties }: { clip_id: string, properties: z.infer<typeof ClipGettableProp> }) { const clip = getClipById(clip_id) return await getClipProps(clip, properties) } @tool({ name: 'get_clip_notes', description: 'Get clip notes by clip id. Returns NoteExtended array for Live 11+ and Note array for Live 10 and below', paramsSchema: { clip_id: z.string(), from_pitch: z.number().min(0).max(127), from_time: z.number(), time_span: z.number(), pitch_span: z.number(), } }) async getClipNotes({ clip_id, from_pitch, from_time, time_span, pitch_span }: { clip_id: string, from_pitch: number, from_time: number, time_span: number, pitch_span: number }) { const clip = getClipById(clip_id) const notes = await getNotes(clip, from_pitch, pitch_span, from_time, time_span) return Result.data(notes) } @tool({ name: 'remove_clip_notes', description: 'Remove clip notes by clip id', enableSnapshot: true, paramsSchema: { clip_id: z.string(), from_pitch: z.number().min(0).max(127), pitch_span: z.number().describe('The number of semitones to remove. Must be a value greater than 0.'), from_time: z.number(), time_span: z.number().describe('The number of beats to remove. Must be a value greater than 0.'), } }) async removeClipNotes({ clip_id, from_pitch, pitch_span, from_time, time_span, historyId }: { clip_id: string from_pitch: number pitch_span: number from_time: number time_span: number historyId: number }) { const clip = getClipById(clip_id) await createNoteSnapshot(clip, historyId) await removeNotesExtended(clip, from_pitch, pitch_span, from_time, time_span) return Result.ok() } @tool({ name: 'remove_notes_by_ids', description: 'Remove notes by clip id and note ids', enableSnapshot: true, paramsSchema: { clip_id: z.string(), note_ids: z.array(z.number()).describe('note ids, get from get_clip_notes'), } }) async removeClipNotesById({ clip_id, note_ids, historyId }: { clip_id: string, note_ids: number[], historyId: number }) { const clip = getClipById(clip_id) await createNoteSnapshot(clip, historyId) await clip.removeNotesById(note_ids) return Result.ok() } @tool({ name: 'add_notes_to_clip', description: 'Add notes to clip by clip id', enableSnapshot: true, paramsSchema: { notes: z.array(NOTE).describe('[array] the notes to add.'), clip_id: z.string() } }) async addClipNotes({ notes, clip_id, historyId }: { notes: Note[], clip_id: string, historyId: number }) { const clip = getClipById(clip_id) await createNoteSnapshot(clip, historyId) await clip.setNotes(notes) return Result.ok() }