Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
fc5e80f
feat/gen: expose agent card compiler for ord plugin
PDT42 Oct 22, 2026
4e783b9
Merge branch 'main' into feat/ord-plugin-compat
PDT42 Oct 22, 2026
198cc11
test/gen: extend & clean up tests
PDT42 Oct 22, 2026
82bbec0
test/gen: integrate feedback
PDT42 Oct 22, 2026
89fba05
feat: accecpt host url input param
PDT42 Oct 26, 2026
4594648
feat/gen: cleanup & restructuring
PDT42 Oct 27, 2026
b149c92
test/gen: let claude cook
PDT42 Oct 27, 2026
6c1588b
feat/gen: cleanup & restructuring
PDT42 Oct 27, 2026
bef2bd2
feat/gen: cleanup & restructuring
PDT42 Oct 27, 2026
3c446b8
feat/gen: cleanup & restructuring
PDT42 Oct 28, 2026
c092f22
feat/gen: cleanup & restructuring
PDT42 Oct 28, 2026
9eb7a0a
test/gen: consolidate tests
PDT42 Oct 28, 2026
ec89ddd
fix/gen: adjust api incomptibilities
PDT42 Oct 28, 2026
91b0908
test/gen: consolidate tests
PDT42 Oct 28, 2026
9d6e8dc
chore: lint & format
PDT42 Oct 28, 2026
c9d96cf
chore: review
PDT42 Oct 28, 2026
9527745
test: review & improve
PDT42 Oct 28, 2026
56dd639
chore: remove outdated revisit
PDT42 Oct 28, 2026
190d87a
chore: lint & format
PDT42 Oct 28, 2026
f87aab6
test: review & improve
PDT42 Oct 28, 2026
49fa196
test: review & improve
PDT42 Oct 28, 2026
534196c
test: review & improve
PDT42 Oct 29, 2026
b65b511
feat: cleanup & restructure, drop @agent.card support
PDT42 Oct 29, 2026
7a2a93c
chore: format
PDT42 Oct 29, 2026
1a3fc1b
feat: cleanup & restructure & reduce
PDT42 Oct 29, 2026
42764b5
Merge branch 'main' into feat/ord-plugin-compat
PDT42 Oct 29, 2026
ff56786
feat/gen: separation of concerns
PDT42 Oct 29, 2026
8ed8c6b
chore: remove outdated or resolved todos
PDT42 Oct 29, 2026
98d7a55
feat/gen: add minimal logging
PDT42 Oct 29, 2026
3fbe245
chore: remove redundant config
PDT42 Oct 29, 2026
8fd46a3
chore: lint & format
PDT42 Oct 29, 2026
6cec226
chore: add entry to changelog
PDT42 Oct 29, 2026
89457be
chore: regenerate lock file
PDT42 Oct 29, 2026
31d5ea9
chore: regenerate lock file
PDT42 Oct 29, 2026
dcbe897
chore: re-generate lock file
PDT42 Oct 29, 2026
0d800dc
fix: apply review suggestion
PDT42 Oct 29, 2026
b514b92
fix: apply review suggestion
PDT42 Oct 29, 2026
15b2930
fix: apply review suggestion
PDT42 Oct 29, 2026
2d8eff9
fix: apply review suggestion
PDT42 Oct 29, 2026
0e2adc9
fix: apply review suggestion
PDT42 Oct 29, 2026
0e1d69e
chore: apply suggestion to improve comment
PDT42 Oct 30, 2026
a73e716
fix: apply review suggestion
PDT42 Oct 30, 2026
67eea87
fix: re-enable lazy init from commonjs
PDT42 Oct 31, 2026
f67c766
Merge remote-tracking branch 'refs/remotes/origin/feat/ord-plugin-com…
PDT42 Oct 31, 2026
dae3fd6
feat: prefix action based skills
PDT42 Oct 31, 2026
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
### Changed

- Reverted AI Core content filtering changes since they are not live on all BTP landscapes
- Dropped support for `@agent.card`, as it was never fully integrated; Resolving agent-card info from `{service}.cds` annotations and `AGENTS.md` (relative path or `@agent.directory`) exclusively
Comment thread
PDT42 marked this conversation as resolved.
Resolved
- Dropped support for `@Core.Links`, as incompatible with mt scenarios

### Added

- Register A2A AgentCard compile target via `lib/api.cjs`, as per `@cap-js/ord` plugin convention

## Version 0.9.9 - 2026-10-23

Expand Down
5 changes: 3 additions & 2 deletions cds-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ const LOG = cds.log("agents")
import registerDefaultAgentHandlers from "./srv/handlers/index.js"
import { patchLangChain } from "./lib/telemetry/tracing.js"
import { slugified } from "./lib/utils/utils.js"
import cds_compile_to_a2a from "./lib/compile.js"
cds.compile.to.a2a = cds_compile_to_a2a
import { registerCompileTargets } from "./lib/api.cjs"

registerCompileTargets()

// Enable doc comments in CSN for agent card generation
cds.env.cdsc = { ...cds.env.cdsc, docComment: true }
Expand Down
25 changes: 25 additions & 0 deletions lib/api.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const cds = require("@sap/cds")

const TARGETS = ["a2a"]

function _lazyRegisterCompileTargets() {
const { default: compile } = require("./compile.js")
TARGETS.forEach((target) =>
Object.defineProperty(this, target, {
value: compile,
configurable: true,
}),
)
return compile
}

const registerCompileTargets = () => {
TARGETS.forEach((target) =>
Object.defineProperty(cds.compile.to, target, {
get: _lazyRegisterCompileTargets,
configurable: true,
}),
)
}

module.exports = { registerCompileTargets }
43 changes: 19 additions & 24 deletions lib/compile.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import cds from "@sap/cds"
import { getFilteredEntities, getFilteredActions } from "@cap-js/mcp/lib/utils/tools-shared.js"
import { getDescription, slugified } from "./utils/utils.js"
import { buildAgentCard } from "./protocol/agent-card.js"
import { slugified } from "./utils/utils.js"
import { compileAgentCard } from "./protocol/agent-card.js"
import { resolveAgentDir } from "./utils/markdown.js"

const A2A_BASE_PATH = "/a2a"

function cds_compile_to_a2a(csn, options = {}) {
function cds_compile_to_a2a(csn, options = { service: undefined, hostUrl: undefined }) {
const model = cds.linked(csn)
const services = model.services

Expand All @@ -20,32 +20,27 @@ function cds_compile_to_a2a(csn, options = {}) {
)
}

let def
let srv
if (!options.service) {
def = services[0]
srv = services[0]
} else {
def = services.find((s) => s.name === options.service)
if (!def) {
srv = services.find((s) => s.name === options.service)
if (!srv) {
throw new Error(`No service definition matching ${options.service} found in given model(s).`)
}
}

// Build service path — respect @path annotation, otherwise use slugified name
const customPath = def["@path"]
const servicePath = customPath ? customPath.replace(/^\//, "") : slugified(def.name)

// If service is behind a proxy, @Core.Links with rel='via' provides the proxy URL
const viaLink = def["@Core.Links"]?.find((l) => l.rel === "via")
const url = viaLink?.href ?? `https://HOST${A2A_BASE_PATH}/${servicePath}`

const agentCard = buildAgentCard({
name: def.name,
description: getDescription(def, "en") || `Agent for ${def.name}`,
entities: getFilteredEntities(def),
actions: getFilteredActions(def),
url,
streaming: true,
})
const customPath = srv["@path"]?.replace(/^\//, "")
const servicePath = customPath ? customPath : slugified(srv.name)

let hostUrl = (options.hostUrl ?? "").trim()
if (hostUrl.length > 0 && !/^https?:\/\//i.test(hostUrl)) hostUrl = "https://" + hostUrl
hostUrl = hostUrl.replace(/\/+$/, "")

const agentDir = resolveAgentDir(srv)
Comment thread
PDT42 marked this conversation as resolved.
Resolved
const url = `${hostUrl}${A2A_BASE_PATH}/${servicePath}`

const agentCard = compileAgentCard(srv, agentDir, url)

if (process.stdout.isTTY || options.as === "json") return agentCard
else return JSON.stringify(agentCard, null, 2)
Expand Down
48 changes: 17 additions & 31 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import cds from "@sap/cds"
import express from "express"
import { AsyncLocalStorage } from "node:async_hooks"

import { generateAgentCard } from "./protocol/agent-card.js"
import { resolveAgentDir, resolveAgentCardPath } from "./utils/markdown.js"
import { compileAgentCard } from "./protocol/agent-card.js"
import { resolveAgentDir } from "./utils/markdown.js"
import { CdsTaskStore } from "./protocol/persistence/task-store.js"
import { CdsPushNotificationStore } from "./protocol/persistence/push-notification-store.js"
import { CdsPushNotificationSender } from "./protocol/push-notification-sender.js"
Expand Down Expand Up @@ -103,16 +103,7 @@ export default function A2AProtocolAdapter(srv, options = {}) {
return res.end()
})

const resolved = {
agentDir: resolveAgentDir(srv),
agentCardPath: resolveAgentCardPath(srv),
}

const agentCard = generateAgentCard(srv, options, resolved)

// If service is behind a proxy, @Core.Links with rel='via' provides the proxy URL
const viaLink = srv.definition["@Core.Links"]?.find((l) => l.rel === "via")
const proxyUrl = viaLink?.href
const agentCard = compileAgentCard(srv.definition, resolveAgentDir(srv))
Comment thread
PDT42 marked this conversation as resolved.
Resolved

// Add "Agent Card" and "Preview" links to the CDS index page
const linkProviders = (srv.$linkProviders ??= [])
Expand Down Expand Up @@ -164,26 +155,21 @@ export default function A2AProtocolAdapter(srv, options = {}) {
}

router.get("/.well-known/agent-card.json", (req, res) => {
// Determine tenant specific URL the card should include
const proto = req.headers["x-forwarded-proto"]?.split(",")[0].trim() || req.protocol
const url = proxyUrl || `${proto}://${req.get("host")}${req.baseUrl}`
// Regenerate agent card when feature toggles are active (annotations may differ)
let card
if (cds.context?.features && Object.keys(cds.context.features).length > 0) {
const featureResolved = {
agentDir: resolveAgentDir(srv),
agentCardPath: resolveAgentCardPath(srv),
}
card = { ...generateAgentCard(srv, options, featureResolved), url }
} else {
card = { ...agentCard, url }
}
if (card.supportedInterfaces) {
card.supportedInterfaces = card.supportedInterfaces.map((iface) => ({
...iface,
url,
}))
}
res.json(card)
const url = `${proto}://${req.get("host")}${req.baseUrl}`

const srvModel = cds.context?.model?.definitions?.[srv.name]

// Regenerate the agent card when feature toggles are active
const card =
srvModel && Object.keys(cds.context?.features ?? {}).length
? compileAgentCard(srvModel, resolveAgentDir(srv), url)
: agentCard

const additionalInterfaces = card.additionalInterfaces.map((iface) => ({ ...iface, url }))

res.json({ ...card, url, additionalInterfaces })
})

if (cds.env?.server?.index) {
Expand Down
Loading
Loading