mirror of
https://github.com/Kaelio/ktx.git
synced 2026-07-13 11:22:11 +02:00
feat(setup): add Claude Desktop target and MCP-first agent setup (#114)
* feat(setup): add Claude Desktop target and MCP-first agent setup Adds `ktx mcp stdio` and a `claude-desktop` setup target that generates a local plugin ZIP wiring the analytics skill and a stdio MCP config. Replaces the CLI-only agent install mode with MCP+analytics (default) and an optional admin CLI skill, renames the research skill to analytics, and lets interactive setup pick project vs global scope when every target supports it. Extracts a shared MCP server factory used by both HTTP and stdio entrypoints. * Add MCP agent client setup support * Polish setup output formatting * Add MCP tool polish design spec Design for slimming the MCP-registered surface from 25 to 11 tools, introducing memory_ingest, applying the per-tool polish kit (annotations, outputSchema, .describe(), in-band error wrapping, union-drift fixes, type-narrowed jsonToolResult), emitting progress notifications on sql_execution + sl_query, and refining the ktx-analytics SKILL.md to match. * Refine MCP tool polish design spec after adversarial review iteration 1 * Refine MCP tool polish design spec after adversarial review iteration 2 * Refine MCP tool polish design spec after adversarial review iteration 3 * refactor(context): rename memory capture service to ingest * feat(mcp): slim research tool surface * refactor(mcp): remove admin ports from server factory * refactor(cli): rename text ingest memory port * docs: update analytics skill for memory ingest * chore: verify mcp surface rename * Add MCP tool polish v1 surface change plan * feat(context): polish mcp tool metadata * fix(context): enforce resolved semantic layer compute sources * feat(context): emit mcp query progress stages * fix(context): keep mcp progress event internal * Add MCP tool polish v1 metadata & progress plan * Fix CI snapshot and docs checks
This commit is contained in:
parent
a72fca2b32
commit
e6d578c03f
50 changed files with 8092 additions and 3143 deletions
|
|
@ -182,6 +182,46 @@ grain: []
|
|||
});
|
||||
});
|
||||
|
||||
it('strips authoring-only fields (usage, inherits_columns_from) before sending sources to the daemon', async () => {
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/_schema/public.yaml',
|
||||
`tables:
|
||||
invoices:
|
||||
table: public.invoices
|
||||
columns:
|
||||
- name: invoice_id
|
||||
type: number
|
||||
pk: true
|
||||
- name: amount
|
||||
type: number
|
||||
usage:
|
||||
narrative: Activation policy windows table for invoice analytics.
|
||||
frequencyTier: mid
|
||||
commonFilters:
|
||||
- amount
|
||||
commonGroupBys: []
|
||||
commonJoins: []
|
||||
staleSince: null
|
||||
`,
|
||||
'ktx',
|
||||
'ktx@example.com',
|
||||
'Add manifest shard with usage',
|
||||
);
|
||||
|
||||
await compileLocalSlQuery(project, {
|
||||
connectionId: 'warehouse',
|
||||
query: { measures: ['sum(invoices.amount)'], dimensions: [] },
|
||||
compute,
|
||||
});
|
||||
|
||||
const lastCall = (compute.query as ReturnType<typeof vi.fn>).mock.calls.at(-1)?.[0];
|
||||
const invoices = lastCall?.sources.find((s: Record<string, unknown>) => s.name === 'invoices');
|
||||
expect(invoices).toBeDefined();
|
||||
expect(invoices).not.toHaveProperty('usage');
|
||||
expect(invoices).not.toHaveProperty('inherits_columns_from');
|
||||
expect(invoices).not.toHaveProperty('source_type');
|
||||
});
|
||||
|
||||
it('resolves the only configured connection when connectionId is omitted', async () => {
|
||||
await compileLocalSlQuery(project, {
|
||||
query: { measures: ['orders.order_count'], dimensions: [] },
|
||||
|
|
@ -236,6 +276,43 @@ grain: []
|
|||
});
|
||||
});
|
||||
|
||||
it('emits progress while compiling and executing a local semantic-layer query', async () => {
|
||||
const progress: Array<{ progress: number; message: string }> = [];
|
||||
const queryExecutor = {
|
||||
execute: vi.fn(async () => ({
|
||||
headers: ['status', 'order_count'],
|
||||
rows: [['paid', 2]],
|
||||
totalRows: 1,
|
||||
command: 'SELECT',
|
||||
rowCount: 1,
|
||||
})),
|
||||
};
|
||||
|
||||
const result = await compileLocalSlQuery(project, {
|
||||
connectionId: 'warehouse',
|
||||
query: {
|
||||
measures: ['orders.order_count'],
|
||||
dimensions: ['orders.status'],
|
||||
limit: 25,
|
||||
},
|
||||
compute,
|
||||
execute: true,
|
||||
maxRows: 10,
|
||||
queryExecutor,
|
||||
onProgress: (event) => {
|
||||
progress.push({ progress: event.progress, message: event.message });
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.totalRows).toBe(1);
|
||||
expect(progress).toEqual([
|
||||
{ progress: 0, message: 'Compiling query' },
|
||||
{ progress: 0.3, message: 'Generating SQL' },
|
||||
{ progress: 0.6, message: 'Executing' },
|
||||
{ progress: 1, message: 'Fetched 1 rows' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('requires a query executor for executed mode', async () => {
|
||||
await expect(
|
||||
compileLocalSlQuery(project, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue