Skip to main content
Glama

Obsidian MCP Server - Enhanced

by BoweyLou
logic.ts7.15 kB
/** * @fileoverview Business logic for the Obsidian Dataview Query tool. * Handles execution of Dataview DQL queries via the Obsidian REST API. * @module obsidianDataviewQueryTool/logic */ import { z } from "zod"; import { logger, RequestContext } from "../../../utils/index.js"; import { ObsidianRestApiService } from "../../../services/obsidianRestAPI/index.js"; import { ComplexSearchResult } from "../../../services/obsidianRestAPI/types.js"; /** * Input validation schema for the Dataview query tool. */ export const DataviewQueryInputSchema = z.object({ query: z .string() .min(1, "Query cannot be empty") .refine( (query) => { const trimmed = query.trim().toUpperCase(); return trimmed.startsWith("TABLE") || trimmed.startsWith("LIST") || trimmed.startsWith("TASK") || trimmed.startsWith("CALENDAR"); }, "Query must start with TABLE, LIST, TASK, or CALENDAR (Dataview DQL keywords)" ), format: z.enum(["table", "list", "raw"]).default("table"), }); /** * Type definition for validated input. */ export type DataviewQueryInput = z.infer<typeof DataviewQueryInputSchema>; /** * Interface for formatted query results. */ export interface DataviewQueryResponse { success: boolean; resultCount: number; query: string; format: string; data: string | object; executionTime?: string; error?: string; } /** * Formats Dataview query results based on the requested format. */ function formatQueryResults( results: ComplexSearchResult[], format: string, query: string ): DataviewQueryResponse { const resultCount = results.length; if (resultCount === 0) { return { success: true, resultCount: 0, query, format, data: format === "raw" ? [] : "No results found for the query.", }; } switch (format) { case "raw": return { success: true, resultCount, query, format, data: results, }; case "list": const listItems = results.map((result, index) => { const title = result.filename || `Result ${index + 1}`; // Note: ComplexSearchResult only has filename and result fields const content = result.result ? (typeof result.result === "string" ? result.result.substring(0, 100) : JSON.stringify(result.result).substring(0, 100)) : ""; const suffix = content.length > 100 ? "..." : ""; return `• **${title}**${content ? ` - ${content}${suffix}` : ""}`; }); return { success: true, resultCount, query, format, data: `Query Results (${resultCount} items):\n\n${listItems.join("\n")}`, }; case "table": default: // Create a formatted table view const tableHeader = "| File | Result |\n|------|--------|"; const tableRows = results.map((result) => { const filename = result.filename || "Unknown"; const resultData = result.result ? (typeof result.result === "string" ? result.result.substring(0, 80).replace(/\|/g, "\\|").replace(/\n/g, " ") : JSON.stringify(result.result).substring(0, 80).replace(/\|/g, "\\|").replace(/\n/g, " ")) : "N/A"; return `| ${filename} | ${resultData}${resultData.length >= 80 ? "..." : ""} |`; }); const tableData = `Query Results (${resultCount} items):\n\n${tableHeader}\n${tableRows.join("\n")}`; return { success: true, resultCount, query, format, data: tableData, }; } } /** * Executes a Dataview DQL query against the Obsidian vault. * * @param obsidianService - The Obsidian REST API service instance * @param input - The validated input containing the query and format * @param context - Request context for logging and error handling * @returns Promise resolving to formatted query results */ export async function executeDataviewQuery( obsidianService: ObsidianRestApiService, input: DataviewQueryInput, context: RequestContext, ): Promise<DataviewQueryResponse> { const startTime = Date.now(); try { logger.info("Executing Dataview DQL query", { ...context, operation: "dataviewQuery", queryLength: input.query.length, format: input.format, queryPreview: input.query.substring(0, 100), }); // Execute the Dataview query using the existing searchComplex method const results = await obsidianService.searchComplex( input.query, "application/vnd.olrapi.dataview.dql+txt", context ); const executionTime = `${Date.now() - startTime}ms`; logger.info("Dataview query executed successfully", { ...context, operation: "dataviewQuery", resultCount: results.length, executionTime, }); // Format the results based on the requested format const formattedResult = formatQueryResults(results, input.format, input.query); formattedResult.executionTime = executionTime; return formattedResult; } catch (error) { const executionTime = `${Date.now() - startTime}ms`; // Handle specific Dataview-related errors if (error instanceof Error) { // Check for common Dataview errors if (error.message.includes("Dataview") || error.message.includes("DQL")) { logger.warning("Dataview query execution failed - likely plugin not installed or query syntax error", { ...context, operation: "dataviewQuery", error: error.message, executionTime, }); return { success: false, resultCount: 0, query: input.query, format: input.format, data: "Dataview query failed. Please ensure:\n" + "1. The Dataview plugin is installed and enabled in Obsidian\n" + "2. Your query syntax is valid DQL\n" + "3. The query type (TABLE/LIST/TASK) matches your data\n\n" + `Error: ${error.message}`, executionTime, error: error.message, }; } } // Re-throw for other types of errors (authentication, network, etc.) throw error; } } /** * Main logic function for the Obsidian Dataview Query tool. * Validates input, executes the query, and returns formatted results. * * @param input - The input arguments from the MCP client * @param context - Request context for logging and error handling * @param obsidianService - The Obsidian REST API service instance * @returns Promise resolving to the tool execution result */ export async function obsidianDataviewQueryLogic( input: DataviewQueryInput, context: RequestContext, obsidianService: ObsidianRestApiService, ): Promise<DataviewQueryResponse> { logger.debug("Processing Dataview query input", { ...context, operation: "dataviewQuery", hasQuery: !!input.query, format: input.format, }); // Execute the query const result = await executeDataviewQuery(obsidianService, input, context); return result; }

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/BoweyLou/obsidian-mcp-server-enhanced'

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