Skip to content

Commit 010104c

Browse files
committed
refactor: move mapper context tool
1 parent d35feea commit 010104c

2 files changed

Lines changed: 265 additions & 210 deletions

File tree

src/mcp/server.ts

Lines changed: 3 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import {
3939
import {
4040
effectiveRowLimit,
4141
enrichQueryOutput,
42-
markdownCell,
4342
maskOutput,
4443
pagedQueryWarnings,
4544
shouldMaskColumnValue
@@ -57,10 +56,6 @@ import {
5756
visibleGlossaryTargets
5857
} from "./business-glossary.js";
5958
import type { McpRequestContext } from "./context.js";
60-
import {
61-
mapperConstraintSummary,
62-
mapperIndexSummary
63-
} from "./metadata-summary.js";
6459
import { metadataSchema } from "./metadata-scope.js";
6560
import { policyContext } from "./policy-context.js";
6661
import {
@@ -136,6 +131,7 @@ import {
136131
lintReadOnlySql,
137132
registerSqlLintTools
138133
} from "./tools/sql-lint.js";
134+
import { registerSqlMapperContextTools } from "./tools/sql-mapper-context.js";
139135
import { registerSqlProjectQueryTools } from "./tools/sql-project-query.js";
140136
import { registerSqlQueryDocTools } from "./tools/sql-query-doc.js";
141137
import { registerSqlValidationTools } from "./tools/sql-validation.js";
@@ -144,8 +140,7 @@ import {
144140
validateSnippetMetadata
145141
} from "./tools/sql-snippet-metadata.js";
146142
import {
147-
resolveProjectQuerySource,
148-
type ProjectQuerySource
143+
resolveProjectQuerySource
149144
} from "./tools/sql-snippet-source.js";
150145
import {
151146
findPrepareQueryCandidates,
@@ -331,66 +326,10 @@ export function createObMcpServer(
331326
registerSqlValidationTools(registerTool, runtime, context);
332327
registerSqlProjectQueryTools(registerTool, runtime, context);
333328
registerSqlQueryDocTools(registerTool, runtime, context);
329+
registerSqlMapperContextTools(registerTool, runtime, context);
334330

335331
registerSqlPrepareTools(registerTool, runtime, context);
336332

337-
registerTool(
338-
"ob_generate_mapper_context",
339-
{
340-
title: "Generate Mapper Context",
341-
description: "Generate read-only table and column context for a MyBatis mapper, SQL file, or explicit table.",
342-
inputSchema: {
343-
...profileInput,
344-
schema: z.string().optional(),
345-
table: z.string().optional(),
346-
sql: z.string().min(1).optional(),
347-
filePath: z.string().min(1).optional(),
348-
line: z.number().int().positive().optional(),
349-
format: z.enum(["json", "markdown"]).optional().default("json"),
350-
maxTables: z.number().int().positive().max(10).optional(),
351-
maxColumns: z.number().int().positive().max(200).optional()
352-
}
353-
},
354-
async ({ profile, schema, table, sql, filePath, line, format = "json", maxTables, maxColumns }) => {
355-
const selected = selectAuthorizedProfile(runtime, profile, context);
356-
const source = sql || filePath ? resolveProjectQuerySource(sql, filePath, line) : undefined;
357-
const policy = source ? explainSqlPolicy(source.sql, policyContext(selected.config)) : undefined;
358-
const maxTableCount = Math.min(maxTables ?? 5, 10);
359-
const allTableRefs = mapperContextTableRefs(
360-
selected.config,
361-
schema,
362-
table,
363-
policy?.tableRefs ?? [],
364-
50
365-
);
366-
const tableRefs = allTableRefs.slice(0, maxTableCount);
367-
if (tableRefs.length === 0) {
368-
throw new Error("Pass table, sql, or filePath so mapper context can identify at least one table.");
369-
}
370-
const tables = [];
371-
for (const ref of tableRefs) {
372-
tables.push(await buildMapperContextTable(selected, ref.schema, ref.table, Math.min(maxColumns ?? 80, 200)));
373-
}
374-
const payload = {
375-
mode: selected.config.compatibility,
376-
source: source?.source,
377-
sql: source?.sql,
378-
policy,
379-
tableCount: tables.length,
380-
tables,
381-
warnings: uniqueStrings([
382-
...(source?.source.warnings ?? []),
383-
...(allTableRefs.length > tableRefs.length ? ["Table list is capped by maxTables."] : [])
384-
]),
385-
markdown: undefined as string | undefined
386-
};
387-
if (format === "markdown") {
388-
payload.markdown = renderMapperContextMarkdown(payload);
389-
}
390-
return toLimitedResult(payload, selected.config);
391-
}
392-
);
393-
394333
registerTool(
395334
"ob_find_unknown_columns_in_sql",
396335
{
@@ -1887,152 +1826,6 @@ function uniqueTableRefs(refs: Array<{ schema?: string; table: string }>): Array
18871826
return output;
18881827
}
18891828

1890-
function mapperContextTableRefs(
1891-
config: ObConfig,
1892-
schema: string | undefined,
1893-
table: string | undefined,
1894-
sqlRefs: Array<{ schema?: string; table: string }>,
1895-
maxTables: number
1896-
): Array<{ schema?: string; table: string }> {
1897-
const refs: Array<{ schema?: string; table: string }> = [];
1898-
if (table?.trim()) {
1899-
refs.push(normalizedSnippetTableRef(config, schema, table));
1900-
}
1901-
for (const ref of sqlRefs) {
1902-
refs.push(normalizedSnippetTableRef(config, ref.schema ?? schema, ref.table));
1903-
}
1904-
return uniqueTableRefs(refs).slice(0, maxTables);
1905-
}
1906-
1907-
async function buildMapperContextTable(
1908-
selected: ReturnType<typeof selectRuntimeProfile>,
1909-
schema: string | undefined,
1910-
table: string,
1911-
maxColumns: number
1912-
) {
1913-
const metadataRef = normalizedSnippetTableRef(selected.config, schema, table);
1914-
assertTableAccess(metadataRef.schema, metadataRef.table, policyContext(selected.config));
1915-
const columns = filterColumnMetadata(
1916-
await withQuerySlot(selected, () =>
1917-
describeTable(
1918-
selected.pool,
1919-
selected.config.compatibility,
1920-
metadataRef.table,
1921-
metadataRef.schema,
1922-
selected.config.queryTimeoutMs
1923-
)
1924-
),
1925-
selected.config.policy
1926-
);
1927-
const constraints = filterColumnMetadata(
1928-
await withQuerySlot(selected, () =>
1929-
listConstraints(
1930-
selected.pool,
1931-
selected.config.compatibility,
1932-
metadataRef.table,
1933-
metadataRef.schema,
1934-
selected.config.queryTimeoutMs
1935-
)
1936-
),
1937-
selected.config.policy
1938-
);
1939-
const indexes = filterColumnMetadata(
1940-
await withQuerySlot(selected, () =>
1941-
listIndexes(
1942-
selected.pool,
1943-
selected.config.compatibility,
1944-
metadataRef.table,
1945-
metadataRef.schema,
1946-
selected.config.queryTimeoutMs
1947-
)
1948-
),
1949-
selected.config.policy
1950-
);
1951-
const compactColumns = compactSchemaColumns(columns, constraints, selected.config, metadataRef.schema, metadataRef.table);
1952-
const columnLimit = Math.max(1, Math.min(maxColumns, 200));
1953-
return {
1954-
schema: metadataRef.schema,
1955-
table: metadataRef.table,
1956-
visibleColumnCount: compactColumns.length,
1957-
columnsTruncated: compactColumns.length > columnLimit,
1958-
columns: compactColumns.slice(0, columnLimit),
1959-
primaryKeys: schemaMapPrimaryKeyColumns(constraints),
1960-
uniqueKeys: uniqueKeyGroups(constraints),
1961-
indexes: mapperIndexSummary(indexes),
1962-
constraints: mapperConstraintSummary(constraints),
1963-
notes: mapperContextNotes(compactColumns)
1964-
};
1965-
}
1966-
1967-
function mapperContextNotes(columns: Array<{ name: string; roles: string[]; masked: boolean }>): string[] {
1968-
const notes: string[] = [];
1969-
const maskedColumns = columns.filter((column) => column.masked).map((column) => column.name);
1970-
const timeColumns = columns.filter((column) => column.roles.includes("time")).map((column) => column.name);
1971-
const statusColumns = columns.filter((column) => column.roles.includes("status")).map((column) => column.name);
1972-
if (maskedColumns.length > 0) {
1973-
notes.push(`Masked columns: ${maskedColumns.slice(0, 10).join(", ")}.`);
1974-
}
1975-
if (timeColumns.length > 0) {
1976-
notes.push(`Likely time columns: ${timeColumns.slice(0, 10).join(", ")}.`);
1977-
}
1978-
if (statusColumns.length > 0) {
1979-
notes.push(`Likely dimension/status columns: ${statusColumns.slice(0, 10).join(", ")}.`);
1980-
}
1981-
return notes;
1982-
}
1983-
1984-
function renderMapperContextMarkdown(payload: {
1985-
mode: ObConfig["compatibility"];
1986-
source?: ProjectQuerySource;
1987-
sql?: string;
1988-
tableCount: number;
1989-
tables: Array<Awaited<ReturnType<typeof buildMapperContextTable>>>;
1990-
warnings: string[];
1991-
}): string {
1992-
const lines = ["# OceanBase Mapper Context", "", `Mode: ${payload.mode}`, `Tables: ${payload.tableCount}`, ""];
1993-
if (payload.source?.type === "file") {
1994-
lines.push(`Source: ${payload.source.file}:${payload.source.line}`, "");
1995-
}
1996-
if (payload.sql) {
1997-
lines.push("```sql", payload.sql, "```", "");
1998-
}
1999-
for (const table of payload.tables) {
2000-
const title = `${table.schema ? `${table.schema}.` : ""}${table.table}`;
2001-
lines.push(`## ${title}`, "");
2002-
if (table.primaryKeys.length > 0) {
2003-
lines.push(`Primary key: ${table.primaryKeys.join(", ")}`, "");
2004-
}
2005-
if (table.indexes.length > 0) {
2006-
lines.push(`Indexes: ${table.indexes.map((index) => `${index.name}(${index.columns.join(", ")})`).join("; ")}`, "");
2007-
}
2008-
lines.push("| Column | Type | Nullable | Roles | Masked | Comment |");
2009-
lines.push("| --- | --- | --- | --- | --- | --- |");
2010-
for (const column of table.columns) {
2011-
lines.push([
2012-
markdownCell(column.name),
2013-
markdownCell(column.type ?? ""),
2014-
column.nullable == null ? "" : column.nullable ? "YES" : "NO",
2015-
markdownCell(column.roles.join(", ")),
2016-
column.masked ? "YES" : "NO",
2017-
markdownCell(column.comment ?? "")
2018-
].join(" | ").replace(/^/, "| ").replace(/$/, " |"));
2019-
}
2020-
if (table.columnsTruncated) {
2021-
lines.push("", `More columns exist for ${title}; output was truncated by maxColumns.`);
2022-
}
2023-
for (const note of table.notes) {
2024-
lines.push("", `Note: ${note}`);
2025-
}
2026-
lines.push("");
2027-
}
2028-
if (payload.warnings.length > 0) {
2029-
lines.push("## Warnings", "");
2030-
lines.push(...payload.warnings.map((warning) => `- ${warning}`));
2031-
lines.push("");
2032-
}
2033-
return lines.join("\n");
2034-
}
2035-
20361829
async function findUnknownTableCandidates(
20371830
selected: ReturnType<typeof selectRuntimeProfile>,
20381831
schema: string | undefined,

0 commit comments

Comments
 (0)