Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/setModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ async function compileModel(path) {
Object.assign(def, info)
}

for (const name in compiled.definitions) {
Object.defineProperty(compiled.definitions[name], 'name', { value: name, enumerable: true })
}

const _entities_in = service => {
const exposed = [],
{ entities } = service
Expand Down
42 changes: 13 additions & 29 deletions lib/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { z } from 'zod'
import setModel from './setModel.js'
import fuzzyTopN from './fuzzyTopN.js'

const PROJECT_PATH = {
projectPath: z.string().describe('Root path of the project')
}

const tools = {
search_cds_definitions: {
title: 'Search for CDS definitions',
description:
'Get details of CDS definitions, returns Core Schema Notation (CSN). Use this if you want to see elements, parameters, file locations, URL paths, etc., helpful when constructing queries or OData URLs or when modifying CDS models.',
inputSchema: {
...PROJECT_PATH,
name: z.string().optional().describe('Name of the definition (fuzzy search, no regex or special characters)'),
kind: z.string().optional().describe('Kind of the definition (service, entity, action, ...)'),
topN: z.number().default(1).describe('Number of results')
projectPath: z.string().describe('Root path of the project'),
name: z
.string()
.optional()
.describe('Name of the definition (fuzzy search (Levenshtein distance), no regex or special characters)'),
kind: z.string().optional().describe('Filter for kind of the definition (service, entity, action, ...)'),
topN: z.number().default(1).describe('Number of results'),
namesOnly: z.boolean().optional().describe('If true, only return the names of the definitions')
},
handler: async ({ projectPath, name, kind, topN }) => {
handler: async ({ projectPath, name, kind, topN, namesOnly }) => {
await setModel(projectPath)
const defNames = kind
? Object.entries(cds.model.definitions)
Expand All @@ -29,26 +29,10 @@ const tools = {
.map(([k]) => k)
: Object.keys(cds.model.definitions)
const scores = name ? fuzzyTopN(name, defNames, topN) : fuzzyTopN('', defNames, topN)
return scores.map(s => Object.assign({ name: s.item }, cds.model.definitions[s.item]))
}
},
list_all_cds_definition_names: {
title: 'List all CDS definitions names',
description:
'Get an overview of available CDS definitions, for details use `search_cds_definitions`. Helpful for initial exploration, e.g. to get all service names.',
inputSchema: {
...PROJECT_PATH,
kind: z.string().optional().describe('Kind of the definition (service, entity, action, ...)')
},
handler: async ({ projectPath, kind }) => {
await setModel(projectPath)
const defNames = kind
? Object.entries(cds.model.definitions)
// eslint-disable-next-line no-unused-vars
.filter(([_k, v]) => v.kind === kind)
.map(([k]) => k)
: Object.keys(cds.model.definitions)
return defNames
if (namesOnly) {
return scores.map(s => s.item)
}
return scores.map(s => cds.model.definitions[s.item])
}
}
}
Expand Down
18 changes: 12 additions & 6 deletions tests/tools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,27 @@ test.describe('tools', () => {
assert.equal(books[0].endpoints[0].path, 'odata/v4/admin/Books', 'Should contain endpoint path')
})

test('list_all_cds_definition_names: should list all entities', async () => {
const entities = await tools.list_all_cds_definition_names.handler({
test('search_cds_definitions: should list all entities (namesOnly)', async () => {
const entities = await tools.search_cds_definitions.handler({
projectPath: sampleProjectPath,
kind: 'entity'
kind: 'entity',
topN: 100,
namesOnly: true
})
assert(Array.isArray(entities), 'Entities should be an array')
assert(entities.length > 0, 'Should find at least one entity')
assert(typeof entities[0] === 'string', 'Should return only names')
})

test('list_all_cds_definition_names: should list all services', async () => {
const services = await tools.list_all_cds_definition_names.handler({
test('search_cds_definitions: should list all services (namesOnly)', async () => {
const services = await tools.search_cds_definitions.handler({
projectPath: sampleProjectPath,
kind: 'service'
kind: 'service',
topN: 100,
namesOnly: true
})
assert(Array.isArray(services), 'Services should be an array')
assert(services.length > 0, 'Should find at least one service')
assert(typeof services[0] === 'string', 'Should return only names')
})
})