|
1 | 1 | import { z } from "zod"; |
| 2 | +import { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; |
2 | 3 | import { ConnectorManager } from "../connectors/manager.js"; |
3 | 4 | import { normalizeSourceId } from "./normalize-id.js"; |
4 | 5 | import { executeSqlSchema } from "../tools/execute-sql.js"; |
@@ -29,6 +30,7 @@ export interface ToolMetadata { |
29 | 30 | name: string; |
30 | 31 | description: string; |
31 | 32 | schema: Record<string, z.ZodType<any>>; |
| 33 | + annotations: ToolAnnotations; |
32 | 34 | } |
33 | 35 |
|
34 | 36 | /** |
@@ -79,19 +81,42 @@ export function zodToParameters(schema: Record<string, z.ZodType<any>>): ToolPar |
79 | 81 | export function getToolMetadataForSource(sourceId: string): ToolMetadata { |
80 | 82 | const sourceIds = ConnectorManager.getAvailableSourceIds(); |
81 | 83 | const sourceConfig = ConnectorManager.getSourceConfig(sourceId); |
| 84 | + const executeOptions = ConnectorManager.getCurrentExecuteOptions(sourceId); |
82 | 85 | const dbType = sourceConfig?.type || "database"; |
83 | 86 |
|
84 | 87 | // Determine tool name based on single vs multi-source configuration |
85 | 88 | const toolName = sourceId === "default" ? "execute_sql" : `execute_sql_${normalizeSourceId(sourceId)}`; |
86 | 89 |
|
87 | | - // Determine description |
| 90 | + // Determine title (human-readable display name) |
88 | 91 | const isDefault = sourceIds[0] === sourceId; |
89 | | - const description = `Execute a SQL query on the '${sourceId}' ${dbType} database${isDefault ? " (default)" : ""}`; |
| 92 | + const title = isDefault |
| 93 | + ? `Execute SQL (${dbType})` |
| 94 | + : `Execute SQL on ${sourceId} (${dbType})`; |
| 95 | + |
| 96 | + // Determine description with more context |
| 97 | + const readonlyNote = executeOptions.readonly ? " [READ-ONLY MODE]" : ""; |
| 98 | + const maxRowsNote = executeOptions.maxRows ? ` (limited to ${executeOptions.maxRows} rows)` : ""; |
| 99 | + const description = `Execute SQL queries on the '${sourceId}' ${dbType} database${isDefault ? " (default)" : ""}${readonlyNote}${maxRowsNote}`; |
| 100 | + |
| 101 | + // Build annotations object with all standard MCP hints |
| 102 | + const isReadonly = executeOptions.readonly === true; |
| 103 | + const annotations = { |
| 104 | + title, |
| 105 | + readOnlyHint: isReadonly, |
| 106 | + destructiveHint: !isReadonly, // Can be destructive if not readonly |
| 107 | + // In readonly mode, queries are more predictable (though still not strictly idempotent due to data changes) |
| 108 | + // In write mode, queries are definitely not idempotent |
| 109 | + idempotentHint: false, |
| 110 | + // In readonly mode, it's safer to operate on arbitrary tables (just reading) |
| 111 | + // In write mode, operating on arbitrary tables is more dangerous |
| 112 | + openWorldHint: isReadonly, |
| 113 | + }; |
90 | 114 |
|
91 | 115 | return { |
92 | 116 | name: toolName, |
93 | 117 | description, |
94 | 118 | schema: executeSqlSchema, |
| 119 | + annotations, |
95 | 120 | }; |
96 | 121 | } |
97 | 122 |
|
|
0 commit comments