Summary
The tool definitions registered via BaseTool.register() in src/utils/base-tool.ts don't include MCP annotations (readOnlyHint, destructiveHint). These are optional hints in the MCP spec that help LLMs understand the impact of each tool.
Currently server.tool() is called with only name, description, schema, and execute — no annotations are passed.
Suggested annotations
| Tool |
Annotation |
Reason |
create-ui |
destructiveHint: false |
Creates new components but doesn't delete/modify existing ones |
refine-ui |
destructiveHint: false |
Modifies existing components — has side effects but isn't data-destructive |
fetch-ui |
readOnlyHint: true |
Fetches component examples, no side effects |
logo-search |
readOnlyHint: true |
Searches for logos, no side effects |
Implementation
The @modelcontextprotocol/sdk supports annotations as a parameter to server.tool(). Update BaseTool:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
export abstract class BaseTool {
abstract name: string;
abstract description: string;
abstract schema: z.ZodObject<any>;
annotations?: ToolAnnotations;
register(server: McpServer) {
server.tool(
this.name,
this.description,
{ ...this.schema.shape },
this.execute.bind(this)
);
// Note: McpServer.tool() may need to be called with
// annotations depending on SDK version
}
abstract execute(args: z.infer<typeof this.schema>): Promise<{
content: Array<{ type: "text"; text: string }>;
}>;
}
Then each tool subclass sets its annotations:
export class FetchUiTool extends BaseTool {
name = "fetch-ui";
description = "...";
annotations = { readOnlyHint: true };
// ...
}
Why this matters
Annotations help LLMs distinguish between tools that just fetch information (fetch-ui, logo-search) and tools that create or modify artifacts (create-ui, refine-ui). This is especially useful in agentic workflows where the LLM is deciding which tools to call autonomously.
Findings from the MCP Quality Benchmark. You can check tool definitions with the MCP Validator.
Summary
The tool definitions registered via
BaseTool.register()insrc/utils/base-tool.tsdon't include MCP annotations (readOnlyHint,destructiveHint). These are optional hints in the MCP spec that help LLMs understand the impact of each tool.Currently
server.tool()is called with onlyname,description,schema, andexecute— no annotations are passed.Suggested annotations
create-uidestructiveHint: falserefine-uidestructiveHint: falsefetch-uireadOnlyHint: truelogo-searchreadOnlyHint: trueImplementation
The
@modelcontextprotocol/sdksupports annotations as a parameter toserver.tool(). UpdateBaseTool:Then each tool subclass sets its annotations:
Why this matters
Annotations help LLMs distinguish between tools that just fetch information (
fetch-ui,logo-search) and tools that create or modify artifacts (create-ui,refine-ui). This is especially useful in agentic workflows where the LLM is deciding which tools to call autonomously.Findings from the MCP Quality Benchmark. You can check tool definitions with the MCP Validator.