Issue: The registerTool method was receiving description and inputSchema parameters but marking them as unused (_description, _inputSchema) and not storing them with the handler. This caused tools/list to return tools without their description and inputSchema properties. Fix: Remove underscore prefixes and explicitly set these properties on the handler object before storing it in the tools Map. Now tools/list will properly return: - name: Tool name - description: Tool description (from registerTool call) - inputSchema: Tool input schema (from registerTool call) This ensures MCP clients can see proper tool documentation and schemas. Files changed: - src/server.ts: Fixed registerTool to preserve metadata Build: ✅ 0 errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
162 lines
3.9 KiB
TypeScript
162 lines
3.9 KiB
TypeScript
/**
|
|
* MCP Server for Obsidian CLI Bundle
|
|
* Constitutional Principle I: MCP Protocol Compliance
|
|
* Constitutional Principle VI: Stdio Transport Standard
|
|
*/
|
|
|
|
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 { logger } from './utils/logger.js';
|
|
import { createErrorResponse } from './utils/error-handler.js';
|
|
import { ToolOutput } from './utils/types.js';
|
|
|
|
/**
|
|
* MCP Server instance
|
|
*/
|
|
export class ObsidianMCPServer {
|
|
private server: Server;
|
|
private tools: Map<string, ToolHandler> = new Map();
|
|
|
|
constructor() {
|
|
this.server = new Server(
|
|
{
|
|
name: 'obsidian-mcp',
|
|
version: '1.0.0',
|
|
},
|
|
{
|
|
capabilities: {
|
|
tools: {},
|
|
},
|
|
}
|
|
);
|
|
|
|
this.setupHandlers();
|
|
}
|
|
|
|
/**
|
|
* Register a tool handler
|
|
*/
|
|
registerTool(
|
|
name: string,
|
|
description: string,
|
|
inputSchema: Record<string, unknown>,
|
|
handler: ToolHandler
|
|
): void {
|
|
// Ensure handler has description and inputSchema
|
|
handler.description = description;
|
|
handler.inputSchema = inputSchema;
|
|
|
|
this.tools.set(name, handler);
|
|
logger.debug('Registered tool', { name });
|
|
}
|
|
|
|
/**
|
|
* Setup MCP protocol handlers
|
|
*/
|
|
private setupHandlers(): void {
|
|
// Handle tools/list request
|
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
logger.debug('Handling tools/list request');
|
|
|
|
const toolsList = Array.from(this.tools.entries()).map(([name, handler]) => ({
|
|
name,
|
|
description: handler.description || `Tool: ${name}`,
|
|
inputSchema: handler.inputSchema || {
|
|
type: 'object',
|
|
properties: {},
|
|
},
|
|
}));
|
|
|
|
logger.info('Returning tools list', { count: toolsList.length });
|
|
|
|
return {
|
|
tools: toolsList,
|
|
};
|
|
});
|
|
|
|
// Handle tools/call request
|
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
const { name, arguments: args } = request.params;
|
|
|
|
logger.info('Handling tool call', { tool: name });
|
|
|
|
try {
|
|
const handler = this.tools.get(name);
|
|
|
|
if (!handler) {
|
|
throw new Error(`Tool not found: ${name}`);
|
|
}
|
|
|
|
// Execute tool handler
|
|
const result = await handler.execute(args || {});
|
|
|
|
logger.debug('Tool call succeeded', { tool: name });
|
|
|
|
// MCP expects a specific response format
|
|
return {
|
|
content: result.content,
|
|
isError: result.isError || false,
|
|
};
|
|
} catch (error) {
|
|
logger.error('Tool call failed', {
|
|
tool: name,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
|
|
const errorResponse = createErrorResponse(error);
|
|
return {
|
|
content: errorResponse.content,
|
|
isError: true,
|
|
};
|
|
}
|
|
});
|
|
|
|
logger.info('MCP server handlers configured');
|
|
}
|
|
|
|
/**
|
|
* Connect to stdio transport
|
|
*/
|
|
async connect(): Promise<void> {
|
|
const transport = new StdioServerTransport();
|
|
await this.server.connect(transport);
|
|
logger.info('MCP server connected to stdio transport');
|
|
}
|
|
|
|
/**
|
|
* Close server connection
|
|
*/
|
|
async close(): Promise<void> {
|
|
await this.server.close();
|
|
logger.info('MCP server closed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tool handler interface
|
|
*/
|
|
export interface ToolHandler {
|
|
description: string;
|
|
inputSchema: Record<string, unknown>;
|
|
execute(args: Record<string, unknown>): Promise<ToolOutput>;
|
|
}
|
|
|
|
/**
|
|
* Create a tool handler
|
|
*/
|
|
export function createToolHandler(
|
|
description: string,
|
|
inputSchema: Record<string, unknown>,
|
|
execute: (args: Record<string, unknown>) => Promise<ToolOutput>
|
|
): ToolHandler {
|
|
return {
|
|
description,
|
|
inputSchema,
|
|
execute,
|
|
};
|
|
}
|