Quick start
Author the graph, connect it to your running app, serve it to any LLM host — the first two steps run offline with no API key.
npm install hcifootprintThree steps. The first two run offline with no API key.
1. Describe the app
Describe the app as the tree you already picture — pages, the containers inside them, and the actions inside those. Each action needs one sentence; that sentence is both your label and the tool description the LLM reads.
import { } from 'hcifootprint';
const = ('shop', {
: {
: {
: {
'search': { : 'Search dresses by name or color' },
'add-to-cart': {
: 'Add the open dress to the cart',
: { : { : true } },
},
},
},
: {
: {
'confirm-order': {
: { 'place-order': { : 'Place the order', : true } },
},
},
},
},
: {
: { : 'Buy a dress end to end', : ['add-to-cart', 'place-order'] },
},
});buildNavigationGraph validates and freezes the whole definition in one call — unknown
goTo targets, guard-operator typos, ambiguous journey steps and contradictory guards all
throw at build time, not at runtime. See The navigation graph.
If your app already owns a route table or a journey list, you don't have to re-type them: the graph grows from sources.
2. Connect it
Connect the graph to your running app through three ordinary wires. Components register what they have when they render; your existing functions bind by reference; the router owns the page.
const = .();
// when the component that renders the catalog mounts:
const = .('catalog', {
: { 'search': () => .() }, // your own function, by reference
});
.('search', false); // grey a button out; group.unregister() on unmount
// your existing wires report reality:
.({ : true }); // store tap → guards re-evaluate
.('checkout'); // router change → the cursor movesNode paths are typed: registerActions('catalog.filtr-rail') is a compile error, not a
silent no-op. Pages contributed by fromRoutes are part of that same
typed union.
3. Serve it to the LLM
Serve the session as a fixed set of MCP-shaped tools. The tool list never changes; what's doable right now arrives inside each tool result.
const = ();
const = .(); // static tool array — one per journey + the four generics
const = .('shop.journey.purchase', {}); // → { readySteps, judgment, youAreOn, ... }The agent plans over journeys, sees only what's available at the current position, and acts
through your own handlers — with the human able to approve high-effect steps. That's the whole
integration: tools() + call() for any framework, or a real MCP server as a
one-liner.
Where next
- The three contexts — read this next. Map, traversal and actions: the whole library in three questions, each with the same three parts.
- The adoption ladder — start in read-only guide mode; nothing can touch your app.
- Guards — read this before writing a state projector.
- Demos — two runnable apps in this repo plus the dress-shop, none needing a key.
What is hcifootprint?
Turn a web app's interaction surface into a typed, traversable journey graph an LLM agent can plan over and act on — as the signed-in user, through the app's own buttons and handlers.
The three contexts
Map, traversal, actions — the three questions an agent asks about your app, each answered in the same three parts: what you declare, what you wire, and what the agent gets.