Skip to main content
Glama

Video Metadata MCP Server

server.ts7.72 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { DatabaseManager } from "./db/DatabaseManager"; import z from "zod"; import { QueryOptions, videoMetadataDao, VideoMetadataFilters, } from "./db/VideoMetadataDao"; const VideoMetadataSchema = z.object({ title: z .string() .describe("Title of the video, e.g. 'LaLiga : Barcelona vs Real Madrid'."), gameType: z.string().describe("Type of game, e.g. 'football', 'basketball'."), teams: z .array(z.string()) .describe( "Array of team names involved in the match, e.g. ['Barcelona', 'Real Madrid']." ), score: z.string().optional().describe("Score of the match, e.g. '2-1'."), winner: z .string() .optional() .describe("Winner of the match, e.g. 'Barcelona'."), videoUrl: z .string() .url() .optional() .describe("URL of the video, e.g. 'https://example.com/video/123'."), description: z.string().optional().describe("Description of the video."), tags: z .record(z.any()) .optional() .describe("Tags associated with the video."), matchDate: z .string() .datetime() .optional() .describe("Date of the match, e.g. '2022-01-01T00:00:00Z'."), venue: z.string().optional().describe("Venue of the match, e.g. 'Camp Nou'."), league: z .string() .optional() .describe("League of the match, e.g. 'La Liga'."), season: z.string().optional().describe("Season of the match, e.g. '2022'."), }); const SearchFiltersSchema = z.object({ gameType: z .string() .optional() .describe("Filter by game type, e.g. 'football', 'basketball'."), teams: z .union([z.string(), z.array(z.string())]) .optional() .describe("Filter by team name(s) e.g. 'Celtics', ['Celtics', 'Knicks']."), league: z .string() .optional() .describe("Filter by league name, e.g. 'NBA', 'Premier League'."), season: z .string() .optional() .describe("Filter by season, e.g. '2021', '2022'."), winner: z .string() .optional() .describe("Filter by winner team name, e.g. 'Arsenal'."), venue: z .string() .optional() .describe("Filter by venue name, e.g. 'Wembley Stadium'."), tags: z .record(z.any()) .optional() .describe( "Filter by tags., e.g. { 'highlight': true, 'full_match': false }." ), matchDateStart: z .string() .datetime() .optional() .describe("Filter by match start date, e.g. '2022-01-01T00:00:00Z'."), matchDateEnd: z .string() .datetime() .optional() .describe("Filter by match end date, e.g. '2022-12-31T23:59:59Z'."), }); const SearchQuerySchema = z.object({ limit: z .number() .int() .positive() .optional() .describe("Maximum number of results to return."), offset: z .number() .int() .min(0) .optional() .describe("Number of results to skip before starting to return results."), orderBy: z .string() .optional() .describe("Field to order results by, e.g. 'matchDate', 'title'."), orderDirection: z .enum(["asc", "desc"]) .optional() .describe( "Direction to order results, either 'asc' for ascending or 'desc' for descending." ), select: z .array(z.string()) .optional() .describe( "Fields to select in the results, e.g. ['title', 'gameType', 'teams']." ), }); export class Server { private _server: McpServer; private _db: DatabaseManager; constructor(db: DatabaseManager) { if (!db) { throw new Error("DatabaseManager instance is required."); } this._db = db; this._server = new McpServer({ name: "sports-metadata-server", version: "1.0.0", }); } async initialize() { this.registerTools(); } async shutdown() { await this._server.close(); } private registerTools() { this._server.tool( "list_unique_game_types", "Lists all unique game types in the video library (e.g. 'football', 'basketball').", {}, async () => { const result = await videoMetadataDao.getUniqueGameTypes(this._db); return { content: [ { type: "text", text: result.join(", "), }, ], }; } ); this._server.tool( "list_unique_leagues", "Lists all unique leagues in the video library (e.g. 'NBA', 'Premier League').", {}, async () => { const result = await videoMetadataDao.getUniqueLeagues(this._db); return { content: [ { type: "text", text: result.join(", "), }, ], }; } ); this._server.tool( "list_unique_seasons", "Lists all unique seasons in the video library (e.g. '2021', '2022').", {}, async () => { const result = await videoMetadataDao.getUniqueSeasons(this._db); return { content: [ { type: "text", text: result.join(", "), }, ], }; } ); this._server.tool( "search_videos", "Searches for videos based on various filters, e.g. 'football', 'Barcelona vs Real Madrid'.", { term: z .string() .describe("Search term to filter videos, e.g 'barcelona'."), ...SearchFiltersSchema.shape, ...SearchQuerySchema.shape, }, async (request) => { console.log("Search term:", request.term); const validatedFilters = SearchFiltersSchema.parse(request); const filter: VideoMetadataFilters = { ...validatedFilters, dateRange: { start: validatedFilters.matchDateStart ? new Date(validatedFilters.matchDateStart) : undefined, end: validatedFilters.matchDateEnd ? new Date(validatedFilters.matchDateEnd) : undefined, }, }; const validatedQuery = SearchQuerySchema.parse(request); const queryOpts: QueryOptions = { ...validatedQuery, }; const result = await videoMetadataDao.searchVideoMetadata( this._db, request.term, filter, queryOpts ); return { content: [ { type: "text", text: JSON.stringify(result, null), }, ], }; } ); this._server.tool( "get_video_metadata_by_filters", "Retrieves video metadata based on the provided filters.", { ...SearchFiltersSchema.shape, ...SearchQuerySchema.shape, }, async (request) => { const validatedFilters = SearchFiltersSchema.parse(request); const filter: VideoMetadataFilters = { ...validatedFilters, dateRange: { start: validatedFilters.matchDateStart ? new Date(validatedFilters.matchDateStart) : undefined, end: validatedFilters.matchDateEnd ? new Date(validatedFilters.matchDateEnd) : undefined, }, }; const validatedQuery = SearchQuerySchema.parse(request); const queryOpts: QueryOptions = { ...validatedQuery, }; const result = await videoMetadataDao.getVideoMetadataByFilters( this._db, filter, queryOpts ); return { content: [ { type: "text", text: JSON.stringify(result, null), }, ], }; } ); } get server() { return this._server; } }

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/stich-studios/metadata-mcp'

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