Files
obsidian-mcp/src/server.ts
Peter.Morton 23e307a7a9 fix: tools/list handler now returns proper tool metadata
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>
2026-03-22 12:43:54 -05:00

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,
};
}