Extend

Embed the gateway

Compose the gateway in your own Node process with createGateway, providers and adapters.

createGateway is dependency-injected: you hand it a validated config, a receipt store, payment providers, optional authorization providers and protocol adapters. This is the same composition the gateway's own entry point performs.

Full composition

gateway.tstypescript
import {
  createAcpAdapter,
  createGateway,
  loadConfig,
  receipts,
  type PaymentProvider,
  type ProtocolAdapter,
} from '@devlab.group/agent-commerce';
import { mcp } from '@devlab.group/agent-commerce/mcp';
import { x402 } from '@devlab.group/agent-commerce/x402';
import { ap2 } from '@devlab.group/agent-commerce/ap2';

const config = await loadConfig();            // AGENT_COMMERCE_CONFIG or ./config.yaml

const store = receipts({ path: config.storage.receipts.path });
await store.init();                           // a store that will not open is fatal

const paymentProviders: PaymentProvider[] = [];
const rail = config.payments.x402;
if (rail?.enabled) {
  paymentProviders.push(
    x402({
      network: rail.network,
      rpcUrl: rail.rpcUrl,
      asset: rail.asset as `0x${string}`,
      assetName: rail.assetName,
      assetVersion: rail.assetVersion,
      assetDecimals: rail.assetDecimals,
      payTo: rail.payTo as `0x${string}`,
      maxTimeoutSeconds: rail.maxTimeoutSeconds,
      facilitator: rail.facilitator,
      ...(rail.allowMainnet !== undefined ? { allowMainnet: rail.allowMainnet } : {}),
      ...(rail.allowUnauthenticatedFacilitator !== undefined
        ? { allowUnauthenticatedFacilitator: rail.allowUnauthenticatedFacilitator }
        : {}),
    }),
  );
}

const mandates = config.authorization?.ap2;
const authorizationProviders = mandates?.enabled ? [ap2({ config: mandates })] : [];

const protocolAdapters: ProtocolAdapter[] = [];
if (config.protocols.mcp.enabled) {
  protocolAdapters.push(mcp({ mountPath: config.protocols.mcp.mountPath }));
}
const acp = config.protocols.acp;
if (acp.enabled) {
  protocolAdapters.push(
    createAcpAdapter({
      mountPath: acp.mountPath,
      token: acp.auth.token,
      operations: acp.checkout.operations,
      idempotency: acp.idempotency,
    }),
  );
}

const gateway = await createGateway({
  config,
  store,
  paymentProviders,
  authorizationProviders,
  protocolAdapters,
});

const { url } = await gateway.listen();

process.on('SIGTERM', async () => {
  await gateway.close();
  for (const provider of authorizationProviders) provider.close();
  await store.close();
  process.exit(0);
});
The A2A adapter is not part of the published entry points; the repository's own entry point wires it from src/protocols/a2a.

GatewayOptions

configGatewayConfigrequired
From loadConfig() or parseConfig(raw, env).
storeReceiptStorerequired
receipts({ path }) — SQLite; ':memory:' for tests. Call init() before use.
paymentProvidersPaymentProvider[]required
Empty is valid for a gateway serving only free resources.
authorizationProvidersAuthorizationProvider[]
Absent means no resource requires authorization.
protocolAdaptersProtocolAdapter[]required
HTTP routes are built in; add MCP, ACP or your own adapters.
logger / clock / ids / backend
Injectable for tests; backend overrides the HTTP backend executor.

GatewayInstance

MemberPurpose
listen()binds server.host:server.port, resolves { url }
close()stops the server and adapters
pipelinethe ExecutionPipeline every adapter calls
resourcesthe resource registry
serverthe Fastify instance — use .inject() in tests