#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Import tool handlers
import { searchDocs } from "./tools/search.js";
import { getExample } from "./tools/examples.js";
import { validateSpec } from "./tools/validate.js";
// Create server instance
const server = new Server(
{
name: "vegalite-mcp-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "search_docs",
description: "Search through Vega-Lite documentation for information about charts, encodings, marks, and more",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query (e.g., 'bar chart', 'color encoding', 'scale domain')",
},
},
required: ["query"],
additionalProperties: false,
},
},
{
name: "get_example",
description: "Retrieve Vega-Lite example specifications by category or type",
inputSchema: {
type: "object",
properties: {
category: {
type: "string",
description: "Example category (e.g., 'bar', 'line', 'scatter', 'area', 'histogram', 'heatmap', 'interactive')",
},
search: {
type: "string",
description: "Optional search term to filter examples within the category",
},
},
required: ["category"],
additionalProperties: false,
},
},
{
name: "validate_spec",
description: "Validate a Vega-Lite specification and check for errors",
inputSchema: {
type: "object",
properties: {
spec: {
type: "object",
description: "Vega-Lite JSON specification to validate",
},
},
required: ["spec"],
additionalProperties: false,
},
},
{
name: "get_schema_info",
description: "Get information about Vega-Lite schema properties and structure",
inputSchema: {
type: "object",
properties: {
property: {
type: "string",
description: "Schema property to get information about (e.g., 'mark', 'encoding', 'data', 'transform')",
},
},
additionalProperties: false,
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "search_docs": {
if (!args?.query) {
throw new Error("Query parameter is required");
}
const results = await searchDocs(args.query as string);
return {
content: [
{
type: "text",
text: JSON.stringify(results, null, 2),
},
],
};
}
case "get_example": {
if (!args?.category) {
throw new Error("Category parameter is required");
}
const examples = await getExample(
args.category as string,
args.search as string | undefined
);
return {
content: [
{
type: "text",
text: JSON.stringify(examples, null, 2),
},
],
};
}
case "validate_spec": {
if (!args?.spec) {
throw new Error("Spec parameter is required");
}
const validation = await validateSpec(args.spec as Record<string, unknown>);
return {
content: [
{
type: "text",
text: JSON.stringify(validation, null, 2),
},
],
};
}
case "get_schema_info": {
return {
content: [
{
type: "text",
text: JSON.stringify({
message: "Schema info tool - to be implemented",
property: args?.property || "all",
}, null, 2),
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: errorMessage,
tool: name,
}, null, 2),
},
],
isError: true,
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Vega-Lite MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});