Skip to main content
Glama
bbernstein
by bbernstein
relationship-tools.ts6.42 kB
import { z } from 'zod'; import { LacyLightsGraphQLClient } from '../services/graphql-client-simple'; const GetFixtureUsageSchema = z.object({ fixtureId: z.string().describe('Fixture ID to get usage for') }); const GetSceneUsageSchema = z.object({ sceneId: z.string().describe('Scene ID to get usage for') }); const CompareScenesSchema = z.object({ sceneId1: z.string().describe('First scene ID'), sceneId2: z.string().describe('Second scene ID') }); export class RelationshipTools { constructor(private graphqlClient: LacyLightsGraphQLClient) {} /** * Get fixture usage information - shows which scenes and cues use this fixture */ async getFixtureUsage(args: z.infer<typeof GetFixtureUsageSchema>) { const { fixtureId } = GetFixtureUsageSchema.parse(args); try { const usage = await this.graphqlClient.getFixtureUsage(fixtureId); return { fixture: { id: usage.fixtureId, name: usage.fixtureName }, scenes: { count: usage.scenes.length, list: usage.scenes.map(scene => ({ id: scene.id, name: scene.name, description: scene.description, fixtureCount: scene.fixtureCount, createdAt: scene.createdAt, updatedAt: scene.updatedAt })) }, cues: { count: usage.cues.length, list: usage.cues.map(cue => ({ cueId: cue.cueId, cueNumber: cue.cueNumber, cueName: cue.cueName, cueListId: cue.cueListId, cueListName: cue.cueListName })) }, summary: { totalScenes: usage.scenes.length, totalCues: usage.cues.length, isUsed: usage.scenes.length > 0 || usage.cues.length > 0 }, message: usage.scenes.length > 0 || usage.cues.length > 0 ? `Fixture "${usage.fixtureName}" is used in ${usage.scenes.length} scene(s) and ${usage.cues.length} cue(s)` : `Fixture "${usage.fixtureName}" is not currently used in any scenes or cues` }; } catch (error) { throw new Error(`Failed to get fixture usage: ${error}`); } } /** * Get scene usage information - shows which cues use this scene */ async getSceneUsage(args: z.infer<typeof GetSceneUsageSchema>) { const { sceneId } = GetSceneUsageSchema.parse(args); try { const usage = await this.graphqlClient.getSceneUsage(sceneId); return { scene: { id: usage.sceneId, name: usage.sceneName }, cues: { count: usage.cues.length, list: usage.cues.map(cue => ({ cueId: cue.cueId, cueNumber: cue.cueNumber, cueName: cue.cueName, cueListId: cue.cueListId, cueListName: cue.cueListName })) }, summary: { totalCues: usage.cues.length, isUsed: usage.cues.length > 0, uniqueCueLists: [...new Set(usage.cues.map(c => c.cueListId))].length }, message: usage.cues.length > 0 ? `Scene "${usage.sceneName}" is used in ${usage.cues.length} cue(s) across ${[...new Set(usage.cues.map(c => c.cueListId))].length} cue list(s)` : `Scene "${usage.sceneName}" is not currently used in any cues` }; } catch (error) { throw new Error(`Failed to get scene usage: ${error}`); } } /** * Compare two scenes to identify differences */ async compareScenes(args: z.infer<typeof CompareScenesSchema>) { const { sceneId1, sceneId2 } = CompareScenesSchema.parse(args); try { const comparison = await this.graphqlClient.compareScenes(sceneId1, sceneId2); // Categorize differences const valuesChanged = comparison.differences.filter(d => d.differenceType === 'VALUES_CHANGED'); const onlyInScene1 = comparison.differences.filter(d => d.differenceType === 'ONLY_IN_SCENE1'); const onlyInScene2 = comparison.differences.filter(d => d.differenceType === 'ONLY_IN_SCENE2'); return { scene1: { id: comparison.scene1.id, name: comparison.scene1.name, description: comparison.scene1.description, fixtureCount: comparison.scene1.fixtureCount }, scene2: { id: comparison.scene2.id, name: comparison.scene2.name, description: comparison.scene2.description, fixtureCount: comparison.scene2.fixtureCount }, comparison: { identicalFixtures: comparison.identicalFixtureCount, differentFixtures: comparison.differentFixtureCount, totalDifferences: comparison.differences.length }, differences: { valuesChanged: { count: valuesChanged.length, fixtures: valuesChanged.map(d => ({ fixtureId: d.fixtureId, fixtureName: d.fixtureName, scene1Values: d.scene1Values, scene2Values: d.scene2Values })) }, onlyInScene1: { count: onlyInScene1.length, fixtures: onlyInScene1.map(d => ({ fixtureId: d.fixtureId, fixtureName: d.fixtureName, values: d.scene1Values })) }, onlyInScene2: { count: onlyInScene2.length, fixtures: onlyInScene2.map(d => ({ fixtureId: d.fixtureId, fixtureName: d.fixtureName, values: d.scene2Values })) } }, summary: { areIdentical: comparison.differences.length === 0, similarityPercentage: comparison.scene1.fixtureCount && comparison.scene2.fixtureCount ? Math.round((comparison.identicalFixtureCount / Math.max(comparison.scene1.fixtureCount, comparison.scene2.fixtureCount)) * 100) : 0 }, message: comparison.differences.length === 0 ? `Scenes "${comparison.scene1.name}" and "${comparison.scene2.name}" are identical` : `Found ${comparison.differences.length} difference(s) between scenes: ${valuesChanged.length} value changes, ${onlyInScene1.length} fixtures only in scene 1, ${onlyInScene2.length} fixtures only in scene 2` }; } catch (error) { throw new Error(`Failed to compare scenes: ${error}`); } } }

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/bbernstein/lacylights-mcp'

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