import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { BirstClient } from "../../client/birstClient.js";
import { z } from "zod";
// Response types
interface ReportMetadata {
name: string;
fullPath: string;
birstURL?: string;
portalURL?: string;
}
interface DashboardMetadata {
name: string;
fullPath: string;
birstLink?: string;
portalLink?: string;
}
interface BqlMetadata {
bql: string;
columnDescriptions?: string[];
}
interface SearchDataResponse {
columnNames?: string[];
displayNames?: string[];
rows?: string[][];
reports?: ReportMetadata[];
dashboards?: DashboardMetadata[];
message?: string;
bqlMetadata?: BqlMetadata;
traces?: string[];
}
const inputSchema = z.object({
query: z.string().describe(
"Natural language search query (e.g., 'What were total sales last quarter?')"
),
spaceId: z.string().describe("The Birst space ID to search"),
spaceName: z.string().optional().describe("Optional space name"),
maxRows: z.number().optional().default(100).describe(
"Maximum number of rows to return (default: 100)"
),
});
export function registerSearchData(server: McpServer, client: BirstClient): void {
server.tool(
"birst_search_data",
"Search the data warehouse using natural language. Returns data results along with related reports and dashboards.",
inputSchema.shape,
async (args) => {
const { query, spaceId, spaceName, maxRows } = inputSchema.parse(args);
const headers: Record<string, string> = {};
if (spaceName) {
headers["spaceName"] = spaceName;
}
if (maxRows) {
headers["MaxRows"] = String(maxRows);
}
const response = await client.genai<SearchDataResponse>("/search-data-warehouse/", {
method: "POST",
body: {
search: query,
spaceId,
},
headers,
});
// Transform rows to readable format
const columnNames = response.displayNames || response.columnNames || [];
const rows = response.rows?.map((row) => {
const obj: Record<string, string> = {};
row.forEach((value, index) => {
const columnName = columnNames[index] || `column_${index}`;
obj[columnName] = value;
});
return obj;
}) || [];
return {
content: [
{
type: "text" as const,
text: JSON.stringify(
{
success: true,
message: response.message,
bql: response.bqlMetadata?.bql,
rowCount: rows.length,
columns: columnNames,
rows,
relatedReports: response.reports?.map((r) => ({
name: r.name,
path: r.fullPath,
url: r.portalURL || r.birstURL,
})),
relatedDashboards: response.dashboards?.map((d) => ({
name: d.name,
path: d.fullPath,
url: d.portalLink || d.birstLink,
})),
},
null,
2
),
},
],
};
}
);
}