Skip to main content

Build-Time Compilation

A bundler plugin reads annotations while your app builds, so the manifest and the agent-facing tools are produced from the same sources, at the same moment, as the code that ships.

npm install -D @axag/compiler
vite.config.ts
import { defineConfig } from 'vite';
import axag from '@axag/compiler/vite';

export default defineConfig({ plugins: [axag()] });

The plugin is built on unplugin, so the same implementation ships for Vite, Rollup, webpack, Rspack and esbuild — import from @axag/compiler/webpack, /rollup, /rspack or /esbuild.

What a build produces

OutputPurpose
.well-known/axag-manifest.jsonThe Semantic Manifest, at the discovery location
axag-tools.webmcp.jsonThe same actions as WebMCP tool definitions
virtual:axag/toolsA module your app imports to register the tools
src/agent.ts
import { tools, manifest, dynamicActions } from 'virtual:axag/tools';

Nothing from the compiler ends up in your bundle: the parsers run at build time, and what reaches the browser is the JSON it produced.

In dev, Vite serves both files from memory and recompiles when an annotated file changes.

Sources it reads

SourceNotes
.html, .htmPlain HTML
.jsx, .tsxAttributes, and axag={spec} objects
.vueThe <template> block
*.component.htmlAngular templates

Action specs

Components can carry the annotation as an object instead of a string. defineAction is an identity function — it exists so the value has a name the compiler recognises:

src/admin/actions.ts
import { defineAction } from '@axag/core';

export const deactivateUser = defineAction({
intent: 'user.deactivate',
actionType: 'write',
riskLevel: 'critical',
confirmationRequired: true,
approvalRoles: ['security_admin'],
requiredParameters: [{ name: 'user_id', type: 'string', format: 'uuid' }],
handler: async ({ user_id }) => api.deactivate(user_id),
});
<button axag={deactivateUser}>Deactivate</button>

The compiler reads a spec when it is a module-level const or a defineAction({...}) call, in that file or one it imports with a relative path. The handler is ignored: it belongs to the runtime, not the manifest.

Dynamic actions

A value the compiler can't read — a prop, something built at runtime — can't appear in the manifest, because its intent isn't known until the page runs. Those elements are listed instead:

axag-manifest.json
{
"actions": [],
"dynamic_actions": [
{ "source_file": "src/Admin.tsx", "source_line": 16, "reason": "<button> has a dynamic axag value that could not be read at build time" }
]
}

A runtime reading this manifest knows the list of actions is incomplete for that page. axag-lint reports the same elements as AXAG-LINT-033 (info), so you can find them without reading the manifest.

In CI

Without a bundler, the CLI runs the same compiler:

npx axag generate src --manifest public/.well-known/axag-manifest.json --validate

Next Steps