From 2982983a83ae9fa96d90e5c7548856b16bc9d277 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Mon, 11 May 2026 12:40:31 +0200 Subject: [PATCH] feat(cli): pass managed daemon options to serve ingest --- packages/cli/src/serve.test.ts | 58 ++++++++++++++++++++++++++++++++++ packages/cli/src/serve.ts | 27 +++++++++++++--- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/serve.test.ts b/packages/cli/src/serve.test.ts index 42c25c84..9e931ce1 100644 --- a/packages/cli/src/serve.test.ts +++ b/packages/cli/src/serve.test.ts @@ -162,6 +162,9 @@ describe('runKtxServeStdio', () => { expect.objectContaining({ localIngest: expect.objectContaining({ adapters: expect.any(Array), + pullConfigOptions: { + databaseIntrospectionUrl: 'http://127.0.0.1:8765', + }, }), localScan: expect.objectContaining({ adapters: createdAdapters, @@ -174,6 +177,61 @@ describe('runKtxServeStdio', () => { }); }); + it('passes managed daemon options to MCP local ingest adapters and pull-config options', async () => { + const project = { projectDir: '/tmp/ktx-project', config: { connections: {} } } as never; + const adapters = [{ source: 'looker', skillNames: [] }]; + const createIngestAdapters = vi.fn(() => adapters); + const createContextTools = vi.fn(() => ({ connections: { list: async () => [] } })); + const managedRuntimeIo = makeManagedRuntimeIo(); + + await expect( + runKtxServeStdio( + { + mcp: 'stdio', + projectDir: '/tmp/ktx-project', + userId: 'agent', + semanticCompute: false, + semanticComputeUrl: undefined, + databaseIntrospectionUrl: undefined, + executeQueries: false, + memoryCapture: false, + memoryModel: undefined, + cliVersion: '0.2.0', + runtimeInstallPolicy: 'auto', + }, + { + loadProject: async () => project, + createContextTools, + createIngestAdapters, + managedRuntimeIo: managedRuntimeIo.io, + createServer: vi.fn(() => ({ connect: vi.fn(async () => undefined) }) as never), + createTransport: vi.fn(() => ({}) as never), + stderr: { write: vi.fn() }, + }, + ), + ).resolves.toBe(0); + + const expectedManagedDaemon = { + cliVersion: '0.2.0', + installPolicy: 'auto', + io: managedRuntimeIo.io, + }; + expect(createIngestAdapters).toHaveBeenCalledWith(project, { + managedDaemon: expectedManagedDaemon, + }); + expect(createContextTools).toHaveBeenCalledWith( + project, + expect.objectContaining({ + localIngest: expect.objectContaining({ + adapters, + pullConfigOptions: { + managedDaemon: expectedManagedDaemon, + }, + }), + }), + ); + }); + it('uses CLI-native local ingest adapters for standalone scan tools', async () => { const project = { projectDir: '/tmp/ktx-project', config: { connections: {} } } as never; const createContextTools = vi.fn(() => ({}) as never); diff --git a/packages/cli/src/serve.ts b/packages/cli/src/serve.ts index 6e26ecf0..0121834e 100644 --- a/packages/cli/src/serve.ts +++ b/packages/cli/src/serve.ts @@ -21,6 +21,7 @@ import { createManagedPythonSemanticLayerComputePort, type KtxManagedPythonInstallPolicy, } from './managed-python-command.js'; +import type { ManagedPythonCoreDaemonOptions } from './managed-python-http.js'; import { profileMark } from './startup-profile.js'; profileMark('module:serve'); @@ -57,7 +58,7 @@ interface KtxServeDeps { createManagedSemanticLayerCompute?: typeof createManagedPythonSemanticLayerComputePort; managedRuntimeIo?: KtxCliIo; createHttpSemanticLayerCompute?: (baseUrl: string) => KtxSemanticLayerComputePort; - createIngestAdapters?: typeof createDefaultLocalIngestAdapters; + createIngestAdapters?: typeof createKtxCliLocalIngestAdapters; createQueryExecutor?: () => KtxSqlQueryExecutorPort; createMemoryCapture?: typeof createLocalProjectMemoryCapture; createServer?: typeof createDefaultKtxMcpServer; @@ -72,6 +73,20 @@ function requiredManagedRuntimeCliVersion(args: KtxServeArgs): string { return args.cliVersion; } +function managedDaemonOptionsForServe( + args: KtxServeArgs, + deps: KtxServeDeps, +): ManagedPythonCoreDaemonOptions | undefined { + if (args.databaseIntrospectionUrl || !args.cliVersion) { + return undefined; + } + return { + cliVersion: args.cliVersion, + installPolicy: args.runtimeInstallPolicy ?? 'prompt', + io: deps.managedRuntimeIo ?? process, + }; +} + async function createServeSemanticLayerCompute( args: KtxServeArgs, deps: KtxServeDeps, @@ -109,9 +124,12 @@ export async function runKtxServeStdio(args: KtxServeArgs, deps: KtxServeDeps = ? (deps.createQueryExecutor ?? createDefaultLocalQueryExecutor)() : undefined; const createIngestAdapters = deps.createIngestAdapters ?? createKtxCliLocalIngestAdapters; - const localAdapters = createIngestAdapters(project, { - databaseIntrospectionUrl: args.databaseIntrospectionUrl, - }); + const managedDaemon = managedDaemonOptionsForServe(args, deps); + const localAdapterOptions = { + ...(args.databaseIntrospectionUrl ? { databaseIntrospectionUrl: args.databaseIntrospectionUrl } : {}), + ...(managedDaemon ? { managedDaemon } : {}), + }; + const localAdapters = createIngestAdapters(project, localAdapterOptions); const llmProvider = args.memoryCapture ? (createLocalKtxLlmProviderFromConfig(project.config.llm) ?? undefined) : undefined; @@ -123,6 +141,7 @@ export async function runKtxServeStdio(args: KtxServeArgs, deps: KtxServeDeps = : undefined; const localIngest: LocalIngestMcpOptions = { adapters: localAdapters, + pullConfigOptions: localAdapterOptions, ...(semanticLayerCompute ? { semanticLayerCompute } : {}), ...(queryExecutor ? { queryExecutor } : {}), };