refactor(mcp): consolidate documentation into mcp.schema.ts

Single file to update when the data model changes:
- EVENT_TIMER_FIELDS and EVENT_WRITABLE_FIELDS (tool JSON schemas) moved from mcp.tools.ts
- ONTIME_SCHEMA_MARKDOWN and ONTIME_DOCS_MARKDOWN moved from mcp.resources.ts
- mcp.tools.ts and mcp.resources.ts now import from mcp.schema.ts
- Removed inline data model notes from the agenda prompt; replaced with a reference
  to the ontime://schema resource
- Fixed inaccurate enum values in schema (Milestone type added, TimerType/EndAction
  corrected to match ontime-types)

https://claude.ai/code/session_01U24MeuUacYXeQhbX3tatEe
This commit is contained in:
Claude
2026-05-09 21:03:03 +00:00
committed by Carlos Valente
parent 71a7f26493
commit 65ef442a9d
4 changed files with 171 additions and 153 deletions
+1 -6
View File
@@ -46,12 +46,7 @@ export function handleGetPrompt(name: string, args: Record<string, string>): Get
content: {
type: 'text',
text: `Convert the following agenda into an Ontime rundown.
Data model:
- Times are milliseconds from midnight. 09:00 = 32400000, 10:30 = 37800000, etc.
- duration = timeEnd - timeStart
- endAction: "load-next" for back-to-back sessions, "none" otherwise
- Ask the user what cue naming and colour conventions they prefer
Read the ontime://schema resource if you need a data model reference.
Steps:
1. Call ontime_get_rundown to see current state and identify an \`after\` anchor if appending.
+1 -112
View File
@@ -4,118 +4,7 @@ import { normalisedToRundownArray } from '../api-data/rundown/rundown.utils.js';
import { getDataProvider } from '../classes/data-provider/DataProvider.js';
import type { ListResourcesResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
// Static Ontime data model reference — agents read this once per session to understand
// the rundown structure, time format, and entry types before issuing tool calls.
const ONTIME_SCHEMA_MARKDOWN = `# Ontime data model
A concise reference for how Ontime structures rundowns, events, and related data.
## Rundown
A rundown is an ordered list of entries rendered as a show schedule. A project can contain multiple rundowns; one is "loaded" at a time.
\`\`\`
Rundown {
id: string
title: string
order: EntryId[] // top-level entry order
flatOrder: EntryId[] // includes entries nested in groups
entries: { [id: EntryId]: OntimeEntry }
revision: number
}
\`\`\`
## Entries
There are three entry types discriminated by \`type\`:
### \`event\` — OntimeEvent (a timed show item)
\`\`\`
{
type: 'event'
id: EntryId
cue: string // human-facing cue label
title: string
note: string
colour: string // hex, e.g. "#4A90D9"
timeStart: number // ms from midnight (09:00 = 32400000)
timeEnd: number // ms from midnight
duration: number // ms (= timeEnd - timeStart)
delay: number // accumulated delay in ms
timerType: 'count-down' | 'count-up' | 'time-to-end' | 'clock'
endAction: 'none' | 'stop' | 'load-next' | 'play-next'
linkStart: boolean // chain start to previous event's end
countToEnd: boolean // timer counts to planned end time
skip: boolean // event is skipped during playback
timeWarning: number // ms before end to trigger 'warning' state
timeDanger: number // ms before end to trigger 'danger' state
custom: { [key: string]: string } // custom field values
triggers: AutomationTrigger[]
}
\`\`\`
### \`delay\` — Delay (schedule shift applied to following events)
\`\`\`
{ type: 'delay', id, duration: number }
\`\`\`
### \`group\` — Group (nested container of entries)
\`\`\`
{ type: 'group', id, title, colour, note, entries: EntryId[], targetDuration?: number }
\`\`\`
## Time format
All time fields are **milliseconds from midnight (local)**. Examples:
- 09:00:00 = 32400000
- 09:30:00 = 34200000
- 14:15:00 = 51300000
- duration of 45 min = 2700000
## Custom fields
Custom fields are project-scoped name/type/colour definitions stored at \`ontime://project/custom-fields\`. Each event stores values at \`event.custom[fieldKey]\`.
## Playback states (runtime only)
\`'stop' | 'play' | 'pause' | 'armed' | 'roll'\`. When playback is not \`stop\`, mutating tools warn that changes are visible immediately.
## Useful resource URIs
- \`ontime://schema\` — this document
- \`ontime://rundown/current\` — the currently loaded rundown (JSON)
- \`ontime://rundowns\` — all rundowns in the project (JSON)
- \`ontime://project/info\` — project metadata (JSON)
- \`ontime://project/custom-fields\` — custom field definitions (JSON)
- \`ontime://docs\` — index of Ontime documentation topics with URLs
## Further reading
Full Ontime documentation: **https://docs.getontime.no**
Read \`ontime://docs\` for a topic index with direct links.
`;
// Curated documentation index — agents read this to find official docs on specific topics.
const ONTIME_DOCS_MARKDOWN = `# Ontime Documentation Index
Main site: https://docs.getontime.no
## Getting started
- Installation & setup: https://docs.getontime.no/installation/
## Core concepts
- Rundown: https://docs.getontime.no/concepts/rundown/
- Timer types (count-down, count-up, time-to-end, clock): https://docs.getontime.no/concepts/timer/
- Time entry format: https://docs.getontime.no/concepts/time-entry/
- Event actions (end action, link start): https://docs.getontime.no/concepts/event-actions/
- Delays and blocks: https://docs.getontime.no/concepts/delays-and-blocks/
## Features
- Custom fields: https://docs.getontime.no/features/custom-fields/
- Automations (triggers, filters, outputs): https://docs.getontime.no/features/automations/
- URL presets / shared views: https://docs.getontime.no/features/url-presets/
- HTTP Integration: https://docs.getontime.no/api/http/
- OSC Integration: https://docs.getontime.no/api/osc/
## API reference
- REST API overview: https://docs.getontime.no/api/
- WebSocket events: https://docs.getontime.no/api/websocket/
`;
import { ONTIME_DOCS_MARKDOWN, ONTIME_SCHEMA_MARKDOWN } from './mcp.schema.js';
export const RESOURCE_DEFINITIONS: ListResourcesResult['resources'] = [
{
+168
View File
@@ -0,0 +1,168 @@
/**
* Single source of truth for Ontime MCP documentation.
*
* Tool field schemas (EVENT_TIMER_FIELDS, EVENT_WRITABLE_FIELDS) and the schema resource
* (ONTIME_SCHEMA_MARKDOWN) are maintained here so that a single edit keeps tool descriptions,
* prompt guidance, and the agent-readable reference document in sync.
*
* Canonical type definitions live in packages/types/src/definitions/core/OntimeEntry.ts.
* Update this file when the data model changes.
*/
// ---- Shared event field JSON schemas ----
// Imported by mcp.tools.ts and spread into tool inputSchema.properties.
export const EVENT_TIMER_FIELDS = {
timerType: {
type: 'string',
enum: ['count-down', 'count-up', 'clock', 'none'],
description: 'count-down: countdown from duration; count-up: elapsed time; clock: wall clock; none: no timer shown',
},
endAction: {
type: 'string',
enum: ['none', 'load-next', 'play-next'],
description: 'Action when event ends: none = stop, load-next = cue next event, play-next = auto-start next event',
},
linkStart: {
type: 'boolean',
description:
"Chain this event's start time to the previous event's end time — changing the first linked event propagates schedule changes to all linked followers",
},
countToEnd: { type: 'boolean', description: 'Timer counts toward the scheduled end time rather than elapsed time' },
timeWarning: { type: 'number', description: 'ms before timeEnd to enter warning state (e.g. 300000 = 5 min)' },
timeDanger: { type: 'number', description: 'ms before timeEnd to enter danger state (e.g. 60000 = 1 min)' },
} as const;
export const EVENT_WRITABLE_FIELDS = {
cue: { type: 'string', description: 'Short free-form cue label — ask the user what naming convention they prefer' },
title: { type: 'string', description: 'Event title shown in the rundown and views' },
note: { type: 'string', description: 'Free-text note for production notes or references' },
colour: {
type: 'string',
description: 'Hex colour (#RRGGBB) for visual grouping — ask the user what colour convention they use',
},
skip: { type: 'boolean', description: 'If true, event is skipped during playback' },
...EVENT_TIMER_FIELDS,
} as const;
// ---- Agent-readable schema document ----
// Served at ontime://schema. Agents read this once per session to orient themselves
// before issuing tool calls.
export const ONTIME_SCHEMA_MARKDOWN = `# Ontime data model
A concise reference for how Ontime structures rundowns, events, and related data.
## Rundown
A rundown is an ordered list of entries rendered as a show schedule. A project can contain multiple rundowns; one is "loaded" at a time.
\`\`\`
Rundown {
id: string
title: string
order: EntryId[] // top-level entry order
flatOrder: EntryId[] // includes entries nested in groups
entries: { [id: EntryId]: OntimeEntry }
revision: number
}
\`\`\`
## Entries
There are four entry types discriminated by \`type\`:
### \`event\` — OntimeEvent (a timed show item)
\`\`\`
{
type: 'event'
id: EntryId
cue: string // human-facing cue label
title: string
note: string
colour: string // hex, e.g. "#4A90D9"
timeStart: number // ms from midnight (09:00 = 32400000)
timeEnd: number // ms from midnight
duration: number // ms (= timeEnd - timeStart)
delay: number // accumulated delay in ms (runtime)
timerType: 'count-down' | 'count-up' | 'clock' | 'none'
endAction: 'none' | 'load-next' | 'play-next'
linkStart: boolean // chain start to previous event's end
countToEnd: boolean // timer counts to planned end time
skip: boolean // event is skipped during playback
timeWarning: number // ms before end to trigger 'warning' state
timeDanger: number // ms before end to trigger 'danger' state
custom: { [key: string]: string } // custom field values
}
\`\`\`
### \`delay\` — OntimeDelay (schedule shift applied to following events)
\`\`\`
{ type: 'delay', id, duration: number }
\`\`\`
### \`group\` — OntimeGroup (nested container of entries)
\`\`\`
{ type: 'group', id, title, colour, note, entries: EntryId[], targetDuration?: number }
\`\`\`
### \`milestone\` — OntimeMilestone (marker with no timer)
\`\`\`
{ type: 'milestone', id, cue, title, note, colour }
\`\`\`
## Time format
All time fields are **milliseconds from midnight (local)**. Examples:
- 09:00:00 = 32400000
- 09:30:00 = 34200000
- 14:15:00 = 51300000
- Duration of 45 min = 2700000
## Custom fields
Custom fields are project-scoped definitions. Get definitions at \`ontime://project/custom-fields\`.
Each event stores values at \`event.custom[fieldKey]\`.
## Playback states (runtime only)
\`'stop' | 'play' | 'pause' | 'armed' | 'roll'\`
When playback is not \`stop\`, mutating tools warn that changes are visible immediately.
## Available resources
- \`ontime://schema\` — this document
- \`ontime://rundown/current\` — the currently loaded rundown (JSON)
- \`ontime://rundowns\` — all rundowns in the project (JSON)
- \`ontime://project/info\` — project metadata (JSON)
- \`ontime://project/custom-fields\` — custom field definitions (JSON)
- \`ontime://docs\` — index of Ontime documentation topics with URLs
## Further reading
Full Ontime documentation: **https://docs.getontime.no**
`;
// ---- Documentation index ----
// Served at ontime://docs. Agents read this when they need to point users to official docs.
export const ONTIME_DOCS_MARKDOWN = `# Ontime Documentation Index
Main site: https://docs.getontime.no
## Getting started
- Installation & setup: https://docs.getontime.no/installation/
## Core concepts
- Rundown: https://docs.getontime.no/concepts/rundown/
- Timer types (count-down, count-up, clock, none): https://docs.getontime.no/concepts/timer/
- Time entry format: https://docs.getontime.no/concepts/time-entry/
- Event actions (end action, link start): https://docs.getontime.no/concepts/event-actions/
- Delays and blocks: https://docs.getontime.no/concepts/delays-and-blocks/
## Features
- Custom fields: https://docs.getontime.no/features/custom-fields/
- Automations (triggers, filters, outputs): https://docs.getontime.no/features/automations/
- URL presets / shared views: https://docs.getontime.no/features/url-presets/
- HTTP Integration: https://docs.getontime.no/api/http/
- OSC Integration: https://docs.getontime.no/api/osc/
## API reference
- REST API overview: https://docs.getontime.no/api/
- WebSocket events: https://docs.getontime.no/api/websocket/
`;
+1 -35
View File
@@ -25,45 +25,11 @@ import { getState } from '../stores/runtimeState.js';
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { deleteRundown, renameRundown, rundownListResponse } from './mcp.service.js';
import { EVENT_TIMER_FIELDS, EVENT_WRITABLE_FIELDS } from './mcp.schema.js';
// Graceful truncation to keep tool responses within typical MCP context windows
const CHARACTER_LIMIT = 25_000;
// ---- Shared event field JSON schemas ----
// Reused across create_event, update_event, create_events_batch, batch_update_events to avoid repetition.
const EVENT_TIMER_FIELDS = {
timerType: {
type: 'string',
enum: ['count-down', 'count-up', 'clock', 'none'],
description: 'count-down: countdown from duration; count-up: elapsed time; clock: wall clock; none: no timer shown',
},
endAction: {
type: 'string',
enum: ['none', 'load-next', 'play-next'],
description: 'Action when event ends: none = stop, load-next = cue next event, play-next = auto-start next event',
},
linkStart: {
type: 'boolean',
description:
"Chain this event's start time to the previous event's end time — changing the first linked event propagates schedule changes to all linked followers",
},
countToEnd: { type: 'boolean', description: 'Timer counts toward the scheduled end time rather than elapsed time' },
timeWarning: { type: 'number', description: 'ms before timeEnd to enter warning state (e.g. 300000 = 5 min)' },
timeDanger: { type: 'number', description: 'ms before timeEnd to enter danger state (e.g. 60000 = 1 min)' },
} as const;
const EVENT_WRITABLE_FIELDS = {
cue: { type: 'string', description: 'Short free-form cue label — ask the user what naming convention they prefer' },
title: { type: 'string', description: 'Event title shown in the rundown and views' },
note: { type: 'string', description: 'Free-text note for production notes or references' },
colour: {
type: 'string',
description: 'Hex colour (#RRGGBB) for visual grouping — ask the user what colour convention they use',
},
skip: { type: 'boolean', description: 'If true, event is skipped during playback' },
...EVENT_TIMER_FIELDS,
} as const;
// ---- MCP tool annotation presets ----
// https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations
const READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;