@@ -11,7 +11,6 @@ import type { HttpConfig, ObConfig } from "../config.js";
1111import {
1212 describeTable ,
1313 estimateErExportPlanStats ,
14- findColumns ,
1514 findRelatedTables ,
1615 getTableDdlMetadata ,
1716 getTableStats ,
@@ -87,8 +86,6 @@ import {
8786import { registerMcpResources } from "./resources.js" ;
8887import {
8988 catalogSearchResult ,
90- columnSearchRows ,
91- dedupeColumnSearchResults ,
9289 dedupeSearchResults ,
9390 glossarySearchResults ,
9491 relatedTableEvidence ,
@@ -108,7 +105,7 @@ import {
108105 sqlParamSchema ,
109106 tableDocumentFormatSchema
110107} from "./schemas.js" ;
111- import { errorHints , toGatewayLimitedResult , toLimitedResult , toResult } from "./tool-runtime.js" ;
108+ import { toGatewayLimitedResult , toLimitedResult , toResult } from "./tool-runtime.js" ;
112109import {
113110 buildErExportCommand ,
114111 defaultErHtmlTitle ,
@@ -145,6 +142,7 @@ import { registerQueryTemplateTools } from "./tools/query-templates.js";
145142import { registerRecentRowsTools } from "./tools/recent-rows.js" ;
146143import { registerSearchCatalogTools } from "./tools/search-catalog.js" ;
147144import { registerSqlConversionTools } from "./tools/sql-conversion.js" ;
145+ import { registerSqlErrorTools } from "./tools/sql-errors.js" ;
148146import { registerSqlUsageTools } from "./tools/sql-usages.js" ;
149147import { registerTableContextTools } from "./tools/table-context.js" ;
150148import {
@@ -223,6 +221,7 @@ export function createObMcpServer(
223221 registerRecentRowsTools ( registerTool , runtime , context ) ;
224222 registerSearchCatalogTools ( registerTool , runtime , context ) ;
225223 registerSqlConversionTools ( registerTool , runtime , context ) ;
224+ registerSqlErrorTools ( registerTool , runtime , context ) ;
226225 registerSqlUsageTools ( registerTool , runtime , context ) ;
227226
228227 registerObjectMetadataTools ( registerTool , runtime , context ) ;
@@ -641,36 +640,6 @@ export function createObMcpServer(
641640 }
642641 ) ;
643642
644- registerTool (
645- "ob_explain_error" ,
646- {
647- title : "Explain SQL Error" ,
648- description : "Explain a SQL error with policy context and visible metadata hints without executing the SQL." ,
649- inputSchema : {
650- ...profileInput ,
651- sql : z . string ( ) . min ( 1 ) ,
652- errorMessage : z . string ( ) . min ( 1 ) ,
653- schema : z . string ( ) . optional ( ) ,
654- limit : z . number ( ) . int ( ) . positive ( ) . max ( 100 ) . optional ( )
655- }
656- } ,
657- async ( { profile, sql, errorMessage, schema, limit } ) => {
658- const selected = selectAuthorizedProfile ( runtime , profile , context ) ;
659- const effectiveSchema = metadataSchema ( selected . config , schema ) ;
660- if ( effectiveSchema && ! isSchemaAllowed ( effectiveSchema , selected . config . policy ) ) {
661- throw new Error ( `Schema "${ effectiveSchema } " is not allowed by the configured OceanBase MCP access policy.` ) ;
662- }
663- const explanation = await explainSqlErrorWithMetadata (
664- selected ,
665- sql ,
666- errorMessage ,
667- effectiveSchema ,
668- Math . min ( limit ?? 20 , 100 )
669- ) ;
670- return toLimitedResult ( explanation , selected . config ) ;
671- }
672- ) ;
673-
674643 registerTool (
675644 "ob_rewrite_sql" ,
676645 {
@@ -3278,119 +3247,6 @@ function uniqueTableRefs(refs: Array<{ schema?: string; table: string }>): Array
32783247 return output ;
32793248}
32803249
3281- async function explainSqlErrorWithMetadata (
3282- selected : ReturnType < typeof selectRuntimeProfile > ,
3283- sql : string ,
3284- errorMessage : string ,
3285- schema : string | undefined ,
3286- limit : number
3287- ) {
3288- const classification = classifySqlError ( errorMessage ) ;
3289- const policy = explainSqlPolicy ( sql , policyContext ( selected . config ) ) ;
3290- const shape = queryShape ( sql , selected . config . compatibility ) ;
3291- const baseHints = errorHints ( new Error ( errorMessage ) , { sql } ) ;
3292- const candidates = await errorMetadataCandidates ( selected , classification , errorMessage , schema , limit ) ;
3293- return {
3294- sql,
3295- errorClass : classification . kind ,
3296- extractedTerm : classification . term ?? null ,
3297- hints : baseHints ,
3298- policy,
3299- shape,
3300- candidates,
3301- warnings : candidates . truncated ? [ "Metadata candidates were truncated by the requested limit." ] : [ ] ,
3302- executed : false
3303- } ;
3304- }
3305-
3306- function classifySqlError ( errorMessage : string ) : { kind : string ; term ?: string } {
3307- const lower = errorMessage . toLowerCase ( ) ;
3308- const column =
3309- / u n k n o w n c o l u m n [ ' " ` ] ? ( [ ^ ' " ` \s ] + ) [ ' " ` ] ? / i. exec ( errorMessage ) ?. [ 1 ] ??
3310- / i n v a l i d i d e n t i f i e r [ ^ ' " ` ] * [ ' " ` ] ( [ ^ ' " ` ] + ) [ ' " ` ] / i. exec ( errorMessage ) ?. [ 1 ] ??
3311- / o r a - 0 0 9 0 4 : \s * [ " ' ] ? ( [ ^ " ' \s ] + ) [ " ' ] ? / i. exec ( errorMessage ) ?. [ 1 ] ;
3312- if ( column || / u n k n o w n c o l u m n | i n v a l i d i d e n t i f i e r | o r a - 0 0 9 0 4 | 1 0 5 4 / . test ( lower ) ) {
3313- return { kind : "unknown_column" , term : column } ;
3314- }
3315- const table =
3316- / o r a - 0 0 9 4 2 : \s * [ " ' ] ? ( [ ^ " ' \s : ] + ) [ " ' ] ? / i. exec ( errorMessage ) ?. [ 1 ] ??
3317- / t a b l e [ ' " ` ] ? ( [ ^ ' " ` \s ] + ) [ ' " ` ] ? .* (?: d o e s n ' t e x i s t | n o t e x i s t | u n k n o w n ) / i. exec ( errorMessage ) ?. [ 1 ] ??
3318- / u n k n o w n t a b l e [ ' " ` ] ? ( [ ^ ' " ` \s ] + ) [ ' " ` ] ? / i. exec ( errorMessage ) ?. [ 1 ] ??
3319- / (?: t a b l e | v i e w ) [ ' " ` ] ? ( [ ^ ' " ` \s ] + ) [ ' " ` ] ? d o e s n o t e x i s t / i. exec ( errorMessage ) ?. [ 1 ] ;
3320- if ( table || / u n k n o w n t a b l e | t a b l e .* d o e s n ' t e x i s t | o r a - 0 0 9 4 2 | 1 1 4 6 / . test ( lower ) ) {
3321- return { kind : "unknown_table" , term : table } ;
3322- }
3323- if ( / n o t a l l o w e d | b l o c k e d | s e l e c t \* | o b _ d e n i e d _ c o l u m n s | p o l i c y / i. test ( errorMessage ) ) {
3324- return { kind : "policy" } ;
3325- }
3326- if ( / l i m i t | o f f s e t | f e t c h | r o w n u m | s y n t a x | o r a - 0 0 9 3 3 | 1 0 6 4 / i. test ( errorMessage ) ) {
3327- return { kind : "dialect_or_pagination" } ;
3328- }
3329- if ( / p e r m i s s i o n | p r i v i l e g e | a c c e s s d e n i e d | o r a - 0 1 0 3 1 | 1 0 4 4 | 1 1 4 2 / i. test ( errorMessage ) ) {
3330- return { kind : "permission" } ;
3331- }
3332- return { kind : "unknown" } ;
3333- }
3334-
3335- async function errorMetadataCandidates (
3336- selected : ReturnType < typeof selectRuntimeProfile > ,
3337- classification : { kind : string ; term ?: string } ,
3338- errorMessage : string ,
3339- schema : string | undefined ,
3340- limit : number
3341- ) {
3342- const term = classification . term ?? fallbackErrorSearchTerm ( errorMessage ) ;
3343- if ( ! term ) {
3344- return { columns : [ ] , tables : [ ] , truncated : false } ;
3345- }
3346- if ( classification . kind === "unknown_column" ) {
3347- const rows = filterColumnMetadata (
3348- await withQuerySlot ( selected , ( ) =>
3349- findColumns (
3350- selected . pool ,
3351- selected . config . compatibility ,
3352- { query : term , schema } ,
3353- limit ,
3354- selected . config . queryTimeoutMs
3355- )
3356- ) ,
3357- selected . config . policy
3358- ) ;
3359- const columns = dedupeColumnSearchResults ( rows . flatMap ( ( row ) =>
3360- columnSearchRows ( row , term , undefined , selected . config )
3361- ) ) . slice ( 0 , limit ) ;
3362- return { columns, tables : [ ] , truncated : rows . length >= limit } ;
3363- }
3364- if ( classification . kind === "unknown_table" ) {
3365- const rows = filterColumnMetadata (
3366- await withQuerySlot ( selected , ( ) =>
3367- searchCatalog (
3368- selected . pool ,
3369- selected . config . compatibility ,
3370- term ,
3371- schema ,
3372- limit ,
3373- selected . config . queryTimeoutMs
3374- )
3375- ) ,
3376- selected . config . policy
3377- ) ;
3378- const searchResults = dedupeSearchResults ( rows . flatMap ( ( row ) =>
3379- catalogSearchResult ( row , term , selected . config )
3380- ) ) ;
3381- return {
3382- columns : [ ] ,
3383- tables : tableCandidatesFromSearchResults ( searchResults , selected . config , limit ) ,
3384- truncated : rows . length >= limit
3385- } ;
3386- }
3387- return { columns : [ ] , tables : [ ] , truncated : false } ;
3388- }
3389-
3390- function fallbackErrorSearchTerm ( errorMessage : string ) : string | undefined {
3391- return / [ ' " ` ] ( [ A - Z a - z _ ] [ \w $ ] * ) [ ' " ` ] / . exec ( errorMessage ) ?. [ 1 ] ;
3392- }
3393-
33943250async function rewriteReadOnlySql (
33953251 selected : ReturnType < typeof selectRuntimeProfile > ,
33963252 sql : string ,
0 commit comments