npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

a2a-plugin

v0.2.0

Published

CAP plugin that automatically exposes annotated CDS services as A2A (Agent-to-Agent) agents.

Readme

a2a-plugin

A CAP plugin that automatically exposes annotated CDS services as A2A (Agent-to-Agent protocol) agents.

Installation

npm add a2a-plugin

CAP activates the plugin automatically via the cds.plugin flag.

Usage

1. Annotate your CDS service

Both @a2a and @a2a.Skill are required. @a2a opts the service into the A2A protocol; without it the plugin ignores the service entirely.

@a2a.Skill: {
    id:          'my-skill',
    name:        'My Skill',
    description: 'Does something useful for an LLM caller.'
}
@a2a
service MyService {}

2. Implement the agent handler

import cds from '@sap/cds';
import { AgentRequest, getEventBus } from 'a2a-plugin/lib/types';

export default class MyService extends cds.ApplicationService {
    async init() {
        this.on('agent', async (req: AgentRequest) => {
            // Optional: publish an intermediate status update
            getEventBus()?.publish({
                kind: 'status-update',
                contextId: req.data.contextId,
                taskId:    req.data.taskId,
                final:     false,
                status:    { state: 'working' },
            });

            return 'Hello from my CAP agent!';
        });
        return super.init();
    }
}

Endpoints

The plugin mounts the following routes at /a2a/<service-path>:

| Route | Description | |---|---| | GET .well-known/agent.json | Agent card (A2A discovery) | | POST /jsonrpc | JSON-RPC 2.0 transport | | * /rest/* | HTTP+JSON REST transport |

Customising execution lifecycle

By default the plugin creates a CAPAgentExecutor internally and dispatches to your agent handler. If you need control over the full execution lifecycle — custom error handling, pre/post logic, different transport behaviour — extend CAPAgentService instead of cds.ApplicationService:

import { ExecutionEventBus, RequestContext } from '@a2a-js/sdk/server';
import { AgentRequest, CAPAgentService } from 'a2a-plugin/lib/types';

export default class MyService extends CAPAgentService {

    async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void> {
        // pre-execution logic
        await super.execute(requestContext, eventBus);
        // post-execution logic
    }

    async init() {
        this.on('agent', async (req: AgentRequest) => {
            return 'Hello from my CAP agent!';
        });
        return super.init();
    }
}

CAPAgentService extends cds.ApplicationService and implements the A2A AgentExecutor interface. When the plugin detects that the service already satisfies AgentExecutor, it uses it directly as the executor — no separate internal executor is created. Override cancelTask the same way to customise task cancellation.

Services that extend plain cds.ApplicationService continue to work exactly as before.

API

CAPAgentService

import { CAPAgentService } from 'a2a-plugin/lib/types';

Base class combining cds.ApplicationService with the A2A AgentExecutor interface. Override execute and/or cancelTask to customise the execution lifecycle. Both methods are regular prototype methods so super calls work normally.

getEventBus(): ExecutionEventBus | undefined

import { getEventBus } from 'a2a-plugin/lib/types';

Returns the ExecutionEventBus for the currently running A2A task. Only available inside an agent handler. Use it to publish streaming intermediate status updates back to the client.

AgentRequest

import { AgentRequest } from 'a2a-plugin/lib/types';

Typed alias for cds.Request<RequestContext>. Gives typed access to req.data.taskId, req.data.contextId, and req.data.userMessage.

Skill annotation reference

| Annotation | Type | Required | Description | |---|---|---|---| | @a2a.Skill.id | String | ✓ | Unique skill identifier | | @a2a.Skill.name | String | ✓ | Human-readable name | | @a2a.Skill.description | String | ✓ | Skill description for LLM prompting | | @a2a.Skill.tags | [String] | | Categorisation tags | | @a2a.Skill.examples | [String] | | Example input queries | | @a2a.Skill.inputModes | [String] | | Accepted MIME types (default: ["text/plain"]) | | @a2a.Skill.outputModes | [String] | | Produced MIME types (default: ["text/plain"]) |

Input mapping

The req.data in the agent handler is the full A2A RequestContext. The plugin also attempts to extract a plain text value from the incoming message:

  • If the request has a DataPart whose body can be parsed as JSON, it is spread into req.data.
  • Otherwise the first text content of the message is mapped to the first String-typed action parameter.

Security

The adapter uses the authentication method configured for the CAP service.

License

Apache-2.0