import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// 1. DEFINITIONS (Knowledge Base)
const EVENT_DEFINITIONS: Record<string, any> = {
"game-started": {
description: "Contains the full roster and player IDs for the match.",
structure: {
type: "game-started",
payload: {
teams: [{ id: "string", name: "string", roster: [{ id: "string", role: "string" }] }]
}
}
},
"ward-placed": {
description: "Triggered when a player places a ward.",
structure: {
type: "ward-placed",
payload: {
wardType: "sight|vision",
position: { x: "number", y: "number" },
placer: { id: "string" }
}
}
}
};
// 2. SERVER SETUP
const server = new Server(
{
name: "grid-esports-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 3. TOOL LISTING
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "explore_graphql_schema",
description: "Introspects the GRID.gg Central Data GraphQL API to find available fields and types.",
inputSchema: {
type: "object",
properties: {
typeName: { type: "string", description: "The GraphQL type to inspect (e.g., 'Series')." },
},
},
},
{
name: "get_event_definition",
description: "Returns the JSON structure for a specific event type from the File Download API.",
inputSchema: {
type: "object",
properties: {
eventType: { type: "string", description: "The event name (e.g., 'game-started')." },
},
required: ["eventType"],
},
},
{
name: "execute_central_query",
description: "Executes a live GraphQL query against the GRID Central Data API.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "The GraphQL query string." },
variables: { type: "object", description: "Optional variables." }
},
required: ["query"]
}
},
{
name: "list_match_files",
description: "Lists all downloadable replay files for a specific Series ID.",
inputSchema: {
type: "object",
properties: {
seriesId: { type: "string", description: "The ID of the series/match." }
},
required: ["seriesId"]
}
}
],
};
});
// 4. TOOL EXECUTION
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const apiKey = process.env.GRID_API_KEY;
// TOOL 1: REAL GRAPHQL INTROSPECTION
if (name === "explore_graphql_schema") {
const typeName = args?.typeName as string || "Query";
if (!apiKey) throw new Error("GRID_API_KEY environment variable is missing.");
const query = `
query IntrospectType {
__type(name: "${typeName}") {
name
fields {
name
description
args { name type { name kind } }
type { name kind ofType { name kind } }
}
}
}
`;
try {
const response = await fetch("https://api-op.grid.gg/central-data/graphql", {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
body: JSON.stringify({ query })
});
const result = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(result.data?.__type || "Type not found", null, 2) }]
};
} catch (error: any) {
return { isError: true, content: [{ type: "text", text: `Error: ${error.message}` }] };
}
}
// TOOL 2: EVENT DEFINITIONS
if (name === "get_event_definition") {
const eventType = args?.eventType as string;
const def = EVENT_DEFINITIONS[eventType];
return {
content: [{ type: "text", text: def ? JSON.stringify(def, null, 2) : "Unknown Event Type" }],
};
}
// TOOL 3: EXECUTE LIVE QUERY
if (name === "execute_central_query") {
const { query, variables } = args as any;
if (!apiKey) throw new Error("GRID_API_KEY environment variable is missing.");
try {
const response = await fetch("https://api-op.grid.gg/central-data/graphql", {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
body: JSON.stringify({ query, variables })
});
const result = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (err: any) {
return { isError: true, content: [{ type: "text", text: err.message }] };
}
}
// TOOL 4: LIST FILES
if (name === "list_match_files") {
const seriesId = args?.seriesId as string;
if (!apiKey) throw new Error("GRID_API_KEY environment variable is missing.");
try {
const response = await fetch(`https://api.grid.gg/file-download/list/${seriesId}`, {
method: "GET",
headers: { "x-api-key": apiKey }
});
const result = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (err: any) {
return { isError: true, content: [{ type: "text", text: err.message }] };
}
}
throw new Error(`Tool not found: ${name}`);
});
// 5. START SERVER
const transport = new StdioServerTransport();
await server.connect(transport);