AbsoluteJS

Manifest

@absolutejs/manifestv0.8.0betaAI

Typed manifest contract for @absolutejs/* packages, with bridges that turn any manifest into an AI tool map or remote MCP tool registry.

The AbsoluteJS package manifest contract: every @absolutejs/* package exports a typed manifest from its ./manifest subpath describing its settings schema, env requirements, adapter slots, wiring recipes, and guarded AI tools. This package is the contract those manifests are written against, plus fail-closed bridges that turn a contract-v2 manifest into an AI tool map for @absolutejs/ai or a remote MCP tool registry for @absolutejs/mcp. TypeBox schemas are the single source of truth, so the same schema types the handler at compile time and is handed verbatim to AI providers and MCP at runtime.

#Installation

BASH
bun add @absolutejs/manifest

#Capabilities

Overview

Version 0.4 makes remote tools secure by construction. Contract-2 manifests declare enough policy metadata for a host or no-code control plane to explain, approve, lease, execute, audit, and where possible compensate every action. Contract-1 manifests still load for catalog and upgrade tooling, but their tools are deliberately omitted from AI and MCP bridges.

Agent action authorization (contract 2)

Contract 2 adds semantic tool effects and enforcement requirements. These are policy inputs, not model hints:

Bridges fail closed: tools are omitted unless the manifest is contract 2, the tool has valid authorization metadata, its runtime or workspace capabilities are available, and the host supplies ToolBindings.enforce. The enforcer gets deep-frozen, validated, defaulted arguments, their canonical SHA-256 digest, the package/tool identity, and a single-use execution closure. The closure expires when enforcement returns, closing the approve-now/execute-different- input gap. A host can wrap approval, authorization, idempotency leasing, execution, and receipt recording around that exact closure.

inspectManifestSecurity returns per-tool posture and actionable issue codes for catalogs and no-code UIs. It checks scopes, public exposure, destinations, resource and spend bindings, idempotency, reversibility/compensation, destructive hints, read-only hints, and every referenced input field.

Show 2 more

The AbsoluteJS package manifest contract. Every @absolutejs/ package exports a typed manifest from its ./manifest subpath describing what the package is, what it needs, how it wires into an app, and what AI tools it offers. This package is the contract those manifests are written against — plus the bridges that turn any manifest into an AI tool map (@absolutejs/ai) or a remote MCP tool registry (@absolutejs/mcp), so anyone can build AI tooling or MCP servers on top of the ecosystem's manifests, not just AbsoluteJS's own products.

MIT licensed. Sole peer dependency: @sinclair/typebox.

No-code integration role

Packages explicitly describe how a no-code host may integrate them:

integration: { mode: "recipe" } owns executable wiring and must publish at

least one wiring recipe.

Show 5 more

integration: { mode: "adapter" } fills a named ecosystem slot and must

publish at least one implements entry.

integration: { mode: "code-first", description: "…" } remains searchable

and explainable, but the host must not pretend it can automatically assemble production-specific stores, policies, signers, callbacks, or identities.

Older manifests without this field remain valid for compatibility. Consumers may retain their historical inference for those packages, but current manifests should declare the role explicitly.

Why TypeBox

A TypeBox schema is simultaneously a TypeScript type (via Static<>) and a plain JSON Schema object at runtime. That makes it the single source of truth for a tool's input: the same schema types the handler at compile time and is handed verbatim to AI providers (input_schema) and MCP (inputSchema). No hand-written JSON Schema, no drift.

Authoring a manifest

defineManifest<TConfig, TRuntime>() is the drift-breaker: the settings schema is checked against the package's real exported config type. Rename a config key without updating the manifest and the package's own tsc fails at this module. Type safety survives upgrades because it is enforced where the types live.

Server wiring uses placement to preserve lifecycle semantics: server-boundary is mounted before the first route (error capture, request context, and other hooks that must observe every handler); server-plugin joins the normal plugin chain; module-scope creates top-level resources; and server-factory is reserved for host factory composition. Client recipes use client-entry.

Package plumbing

Show 3 more

absolute-manifest emit validates the manifest (schema, tool-key naming, preset values, package.json agreement, and shared-runtime ownership) and writes dist/manifest.json — the serializable projection (handlers stripped) for consumers that can't execute package code. It is derived, never hand-authored, so the two forms cannot diverge. absolute-manifest scaffold generates a starter src/manifest.ts.

absolutejs.runtimePeers classifies every peerDependencies entry. Coverage is mandatory: the validator rejects any peer omitted from this map, a runtime duplicated in dependencies, a peer range or optionality mismatch, a dev dependency that differs from the exact tested version, any missing build external, and any artifactImports entry that disappeared from the emitted JavaScript because it was bundled. An empty artifactImports array is the explicit declaration that no static import survives. A dynamically resolved peer must instead declare stable artifactReferences strings that survive in the built JavaScript (for example, a host node_modules/sharp lookup); only a peer with both evidence arrays empty is type-only. Omission is never a declaration. Keep compatibility windows conservative; supporting a new pre-1.0 minor requires a deliberate package release.

Run absolute-manifest verify-package --artifacts directly for packages that want both package and emitted-artifact gates without emitting a manifest. absolute-manifest verify-tree [directory] [--artifacts] recursively checks a workspace while excluding generated and dependency directories.

Consuming manifests

Bridges validate and default every call before enforcement and fail closed. Contract-1 tools, unguarded tools, missing runtimes, unavailable workspace capabilities, and hosts without an enforcer never appear in the registry.

Handlers receive nothing ambient — no process.env, no host secrets. A runtime tool gets the package instance the host constructed; a workspace tool gets the host's jailed Workspace (read/glob/write/exec, each granted explicitly). Hosts running untrusted manifests should additionally compose @absolutejs/ai's hardenUntrustedTool over the bridged map.

Adapter slots

Core packages declare slots (contract: 'dispatch/email-adapter'); adapter packages declare implements entries with the same contract id. Consumers resolve them by scanning installed manifests, so publishing a new vendor adapter lights up every consumer without a core release. Reserved ids and the frozen wiring placeholder grammar live in CONTRACTS.md.

TypeBox single source

A TypeBox schema is simultaneously a TypeScript type and a plain JSON Schema object, so tool inputs are typed at compile time and validated at runtime with no hand-written JSON Schema and no drift.

Drift-checked authoring

defineManifest checks the settings schema against the package’s real exported config type, so renaming a config key without updating the manifest fails the package’s own tsc.

AI and MCP bridges

toAIToolMap and toMcpToolRegistry validate every call’s input and require a host authorization enforcer before the handler runs. Contract-v1 tools, unguarded tools, missing runtimes, and ungranted capabilities are omitted entirely.

Adapter slot resolution

Core packages declare slots by contract id and adapter packages declare matching implements entries, so publishing a new vendor adapter lights up every consumer without a core release.

CLI emit and scaffold

absolute-manifest emit validates the manifest and writes the derived, handler-stripped dist/manifest.json for consumers that cannot execute package code; scaffold generates a starter manifest.

Capability-scoped handlers

Handlers receive nothing ambient: no process.env and no host secrets. Runtime tools get the instance the host constructed; workspace tools get an explicitly granted, jailed Workspace.

Outcomes

What you can build

Overview

Version 0.4 makes remote tools secure by construction. Contract-2 manifests declare enough policy metadata for a host or no-code control plane to explain, approve, lease, execute, audit, and where possible compensate every action. Contract-1 manifests still load for catalog and upgrade tooling, but their tools are deliberately omitted from AI and MCP bridges.

Agent action authorization (contract 2)

Contract 2 adds semantic tool effects and enforcement requirements. These are policy inputs, not model hints:

No-code integration role

Packages explicitly describe how a no-code host may integrate them:

Hardening checklist

Production guidance

Make every external boundary explicitPin the deployed @absolutejs/manifest version, replace example or memory-backed dependencies with durable implementations, bound external calls, protect credentials, and emit enough evidence to retry or recover safely.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/manifest example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#Agent action authorization (contract 2)

Partial snippet

Contract 2 adds semantic tool effects and enforcement requirements. These are policy inputs, not model hints:

TS
send_email: tool.runtime({
  authorization: {
    approval: "policy",
    audience: "owner",
    destinationFields: ["to"],
    effects: ["send", "external-network"],
    idempotency: { mode: "host" },
    requiredScopes: ["email:send"],
    reversible: false,
  },
  // input, handler, description…
});

#Authoring a manifest

Partial snippet

Working example for Authoring a manifest.

TS
// src/manifest.ts of @absolutejs/dispatch
import { Type } from "@sinclair/typebox";
import { defineManifest, toolFactory } from "@absolutejs/manifest";
import type { Dispatcher, DispatcherOptions } from "./types";

const tool = toolFactory<Dispatcher>();

export const manifest = defineManifest<DispatcherOptions, Dispatcher>()({
  contract: 2,
  identity: {
    category: "messaging",
    name: "@absolutejs/dispatch",
    tagline: "Send email, texts, and push notifications from your site.",
  },
  settings: Type.Object({
    defaultFrom: Type.Optional(
      Type.Object(
        { email: Type.Optional(Type.String({ format: "email" })) },
        { title: "Default sender" },
      ),
    ),
  }),
  slots: {
    email: {
      configPath: "email",
      contract: "dispatch/email-adapter",
      description: "Email transport",
      known: ["@absolutejs/dispatch-resend", "@absolutejs/dispatch-postmark"],
    },
  },
  tools: {
    send_email: tool.runtime({
      annotations: { openWorldHint: true },
      authorization: {
        approval: "policy",
        audience: "owner",
        destinationFields: ["to"],
        effects: ["send", "external-network"],
        idempotency: { mode: "host" },
        requiredScopes: ["email:send"],
        reversible: false,
      },
      description: "Send a transactional email through the configured adapter.",
      handler: async (input, dispatcher) => {
        const result = await dispatcher.email(input);

        return `sent via ${result.provider}`;
      },
      input: Type.Object({
        subject: Type.String(),
        text: Type.String(),
        to: Type.String({ format: "email" }),
      }),
    }),
  },
  wiring: [
    {
      id: "default",
      server: {
        code: "const dispatcher = createDispatcher({ email: ${slot.email}, ...${settings} });",
        imports: [
          { from: "@absolutejs/dispatch", names: ["createDispatcher"] },
        ],
        placement: "module-scope",
      },
      title: "Create the dispatcher",
    },
  ],
});

#Authoring a Manifest

Partial snippet

A package describes itself once; the settings schema is type-checked against the real exported config type.

TS
// src/manifest.ts of @absolutejs/dispatch
import { Type } from '@sinclair/typebox';
import { defineManifest, toolFactory } from '@absolutejs/manifest';
import type { Dispatcher, DispatcherOptions } from './types';

const tool = toolFactory<Dispatcher>();

export const manifest = defineManifest<DispatcherOptions, Dispatcher>()({
	contract: 2,
	identity: {
		category: 'messaging',
		name: '@absolutejs/dispatch',
		tagline: 'Send email, texts, and push notifications from your site.'
	},
	settings: Type.Object({
		defaultFrom: Type.Optional(
			Type.Object(
				{ email: Type.Optional(Type.String({ format: 'email' })) },
				{ title: 'Default sender' }
			)
		)
	}),
	slots: {
		email: {
			configPath: 'email',
			contract: 'dispatch/email-adapter',
			description: 'Email transport',
			known: ['@absolutejs/dispatch-resend']
		}
	},
	tools: {
		send_email: tool.runtime({
			annotations: { idempotentHint: true, openWorldHint: true },
			authorization: {
				approval: 'policy',
				audience: 'authenticated',
				destinationFields: ['to'],
				effects: ['send', 'external-network'],
				idempotency: { mode: 'host' },
				requiredScopes: ['messaging:send'],
				reversible: false
			},
			description: 'Send a transactional email.',
			handler: async (input, dispatcher) => {
				const result = await dispatcher.email(input);

				return `sent via ${result.provider}`;
			},
			input: Type.Object({
				subject: Type.String(),
				text: Type.String(),
				to: Type.String({ format: 'email' })
			})
		})
	},
	wiring: [
		{
			id: 'default',
			server: {
				code: 'const dispatcher = createDispatcher({ email: ${slot.email}, ...${settings} });',
				imports: [
					{ from: '@absolutejs/dispatch', names: ['createDispatcher'] }
				],
				placement: 'module-scope'
			},
			title: 'Create the dispatcher'
		}
	]
});

#Consuming Manifests

Partial snippet

Load a guarded manifest and bridge it only through the host authorization, lease, and receipt boundary.

TS
import {
	loadManifest,
	toAIToolMap,
	toMcpToolRegistry
} from '@absolutejs/manifest';

const result = await loadManifest('@absolutejs/dispatch');
if (!result.ok) throw new Error(result.details);

const enforce = async (request, execute) => {
	await agency.authorizeAndLease(request);
	const result = await execute();
	await agency.recordReceipt(request, result);

	return result;
};

// AI tool loop (@absolutejs/ai)
const tools = toAIToolMap(result.manifest, { enforce, runtime: dispatcher });

// Remote MCP server (@absolutejs/mcp)
new Elysia().use(
	mcpServer({
		path: '/mcp',
		tools: () =>
			toMcpToolRegistry(result.manifest, { enforce, runtime: dispatcher })
	})
);
Open contract
MIT licensed with @sinclair/typebox as the sole peer dependency, so anyone can build AI tooling or MCP servers on top of the ecosystem’s manifests, not just AbsoluteJS’s own products.
Untrusted manifests
Hosts running untrusted manifests should additionally compose hardenUntrustedTool from @absolutejs/ai over the bridged tool map.

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

80 symbols
defineImplementationexportPermalink
TS
defineImplementation
Exported from @absolutejs/manifest

Current package surface

What ships today

@absolutejs/manifestv0.8.0 · betaAInpmSource
1entry points80symbols

Import surface · click to copy