feat(sqlite): implement connector scope listing

This commit is contained in:
Andrey Avtomonov 2026-05-25 13:36:06 +02:00
parent 8ec2acba40
commit 54dd9cc518
11 changed files with 82 additions and 1 deletions

View file

@ -56,6 +56,8 @@ describe('createLocalProjectMcpContextPorts', () => {
driver: snapshot.driver,
capabilities: createKtxConnectorCapabilities({ readOnlySql: queryResult !== undefined }),
introspect: vi.fn(async () => snapshot),
listSchemas: vi.fn(async () => []),
listTables: vi.fn(async () => []),
executeReadOnly: queryResult === undefined ? undefined : vi.fn(async () => queryResult),
cleanup: vi.fn(async () => {}),
};

View file

@ -72,6 +72,8 @@ function createConnector(): KtxScanConnector {
introspect: vi.fn(async () => {
throw new Error('introspection is not used by description generation');
}),
listSchemas: vi.fn(async () => []),
listTables: vi.fn(async () => []),
sampleColumn: vi.fn(async () => ({
values: ['paid', 'refunded', null],
nullCount: 1,

View file

@ -104,6 +104,8 @@ function connector(): KtxScanConnector {
columnStats: true,
}),
introspect: vi.fn(async () => snapshot),
listSchemas: vi.fn(async () => []),
listTables: vi.fn(async () => []),
sampleTable: vi.fn(async () => ({
headers: ['id', 'customer_id'],
rows: [[1, 10]],

View file

@ -16,6 +16,11 @@ import type {
KtxSchemaSnapshot,
} from '../../../src/context/scan/types.js';
const connectorScopeListing = {
listSchemas: vi.fn(async () => []),
listTables: vi.fn(async () => []),
};
function relationshipSqlResult(
input: KtxReadOnlyQueryInput,
options: { throwOnCoverage?: boolean } = {},
@ -254,6 +259,7 @@ function nativeScanConnector(options: { cleanup?: () => Promise<void> } = {}): K
formalForeignKeys: false,
estimatedRowCounts: false,
},
...connectorScopeListing,
introspect: vi.fn(async () => nativeScanSnapshot()),
sampleTable: vi.fn(async () => ({ headers: ['id'], rows: [[1]], totalRows: 1 })),
sampleColumn: vi.fn(async () => ({ values: ['1'], nullCount: 0, distinctCount: 1 })),
@ -656,6 +662,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: false,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -741,6 +748,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: true,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -930,6 +938,14 @@ describe('local scan', () => {
};
}
async listSchemas(): Promise<string[]> {
return [];
}
async listTables() {
return [];
}
async executeReadOnly(input: KtxReadOnlyQueryInput): Promise<KtxQueryResult> {
return relationshipSqlResult(input);
}
@ -972,6 +988,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: true,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1073,6 +1090,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: true,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1200,6 +1218,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: true,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1340,6 +1359,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: true,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1455,6 +1475,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: false,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1550,6 +1571,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: false,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',
@ -1666,6 +1688,7 @@ describe('local scan', () => {
formalForeignKeys: false,
estimatedRowCounts: false,
},
...connectorScopeListing,
async introspect() {
return {
connectionId: 'warehouse',

View file

@ -213,6 +213,8 @@ function connector(executor: InMemorySqliteExecutor | null): KtxScanConnector {
columnSampling: false,
}),
introspect: async () => snapshot(),
listSchemas: async () => [],
listTables: async () => [],
executeReadOnly: executor ? executor.executeReadOnly.bind(executor) : undefined,
};
}
@ -645,6 +647,8 @@ describe('production relationship discovery', () => {
columnSampling: false,
}),
introspect: async () => maskedSnapshot,
listSchemas: async () => [],
listTables: async () => [],
executeReadOnly: async (input) => {
const rows = database.prepare(input.sql).all() as Record<string, unknown>[];
const headers = Object.keys(rows[0] ?? {});

View file

@ -93,6 +93,8 @@ describe('KTX scan contract types', () => {
expect(ctx.runId).toBe('scan-run-1');
return snapshot;
},
listSchemas: async () => [],
listTables: async () => [],
};
await expect(
@ -164,6 +166,8 @@ describe('KTX scan contract types', () => {
tables: [],
};
},
listSchemas: async () => [],
listTables: async () => [],
};
await expect(