SkillAgentSearch skills...

hcifootprint

Turn a web app's interaction surface into a typed, traversable skill graph an LLM can plan over — the frontend sibling of footprintjs.

Install / Use

claude mcp add footprintjs -- npx -y github:footprintjs/hcifootprint

If the server publishes to npm under a different name, use that package instead — check the repo README.

About this skill
🔌

MCP Server

Model Context Protocol server

Quality Score

84/100

Supported Platforms

Claude Code
Claude Desktop

Our assessment of hcifootprint

hcifootprint scores 84/100 on our quality scale, 266th of 538 AI & Machine Learning skills we index (top 50%).

Its MCP Server is 21 KB long, well organised into 18 sections with 17 code examples: a thorough specification that gives an agent plenty to work with.

It has 10 GitHub stars, so there is little community track record yet; judge it on its content.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
4/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 28 days ago, so hcifootprint is actively maintained.
  • Our last check on 2026-09-24 found the source still online.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 97/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands.

Automated pattern scan on 2026-09-24. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

hcifootprint compared with similar skills

All 4 of these similar skills score higher than hcifootprint; compare them before choosing.

SkillScoreStarsUpdatedFormat
hcifootprint (this skill)by footprintjs841028d agoMCP Server
cavemanby JuliusBrussee100107.6k1d agoCLAUDE.md
claude-memby thedotmack10094.6ktodayCLAUDE.md
Agent-Reachby Panniantong10085.2k8d agoCLAUDE.md
Understand-Anythingby Egonex-AI10084.0k12d agoCLAUDE.md

Frequently asked questions

How do I install hcifootprint?
Run claude mcp add footprintjs -- npx -y github:footprintjs/hcifootprint. The install tabs above show the steps for each supported agent.
Which AI agents does hcifootprint work with?
It is written for Claude Code and Claude Desktop, as a MCP Server file. Other agents that read the same format can often use it too.
Is hcifootprint safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. It is MIT-licensed and scores 97/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is hcifootprint still maintained?
The repository was last updated 28 days ago, so hcifootprint is actively maintained.
<h1 align="center">HACI&nbsp;Footprint</h1> <p align="center"><b>Human &amp; Agent · Computer Interaction</b></p> <p align="center"> <strong>Your agent can reach your app — but it's flying blind.<br/> Your app already knows what can be done here, and by whom. Hand the agent that map.</strong> </p> <p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/assets/haci-hero-dark.svg"> <source media="(prefers-color-scheme: light)" srcset="docs/assets/haci-hero-light.svg"> <img alt="HACI Footprint — the classic term HCI becomes HACI as a yellow A (Agent) joins the Human's side; a person and an AI act on the app as a team." src="docs/assets/haci-hero-light.svg" width="100%"> </picture> </p> <p align="center"> <img src="https://img.shields.io/npm/v/hcifootprint?style=flat&color=e0a400" alt="npm version"> <img src="https://img.shields.io/badge/tests-3047%20passing-f5b301?style=flat" alt="3047 tests passing"> <img src="https://img.shields.io/badge/core-zero--dependency-f5b301?style=flat" alt="zero-dependency core"> <img src="https://img.shields.io/badge/serves-a%20real%20MCP%20server-f5b301?style=flat" alt="serves a real MCP server"> <a href="https://github.com/footprintjs/hcifootprint/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT"></a> </p> <p align="center"> <strong>🌐 <a href="https://footprintjs.github.io/hcifootprint/">footprintjs.github.io/hcifootprint</a></strong> · <a href="https://footprintjs.github.io/hcifootprint/docs/">Docs</a> · <a href="https://footprintjs.github.io/hcifootprint/docs/get-started/quick-start">Quick start</a> </p>
npm install hcifootprint

1.0 — the names are frozen. You author actions, you name journeys, and a tool is what is served to a model. The pre-1.0 spellings (tools:, skills:) are gone, not deprecated — see the migration. Agents: llms.txt is the whole API surface on one page.


The problem

An agent can already reach your app. The question is how it operates one.

| How agents drive a UI today | The cost | |---|---| | Screenshot the page, reason over pixels | slow, fragile, redone every turn | | Dump the DOM into the prompt | every wrapper and class, re-sent each turn — and it still guesses what does what | | Hard-coded selectors, RPA scripts | break on the next redesign |

All three relearn your app from scratch on every visit.

Measured, on a real page: a five-page demo's rendered DOM is 2,027 tokens; what this library sends for the same page is 332 — 6.1× less per turn, at 41.5 tokens per available action. That is one page of one small app whose DOM is dominated by its shell, so treat it as a floor rather than a headline; the script is in the repo (node bench/token-cost/token-cost.mjs) and the honest way to know your own number is to run it against your own app. A returning human doesn't — they carry a mental model of where things are and what they're allowed to do. Your app holds that same map. This hands it to the agent.

And it is safe to adopt for one reason: you are not opening your backend — you are letting an agent drive the frontend a human already can. It acts as the signed-in user, through your own buttons and handlers, inside exactly the permissions they already have. No new endpoints, no new grants.


The model

Three contexts. An agent driving your app asks three questions, and this library answers exactly those — each in the same three parts: what you declare, what you wire, and what the agent gets.

1 · The map — what can this app do?

Declare the app as the tree you already picture: places, the things inside them, the named flows worth finishing. One sentence per action — that sentence is your label and the tool description the model reads.

pages: {
  catalog:  { route: '/catalog',  actions: { 'add-to-cart': { does: 'Add the open dress to the cart', writes: ['cart.items'] } } },
  checkout: { route: '/checkout', actions: { 'place-order': { does: 'Place the order', enabledWhen: { 'cart.items': { gt: 0 } }, confirm: true } } },
},
journeys: { purchase: { does: 'Buy a dress end to end', steps: ['add-to-cart', 'place-order'] } },

Already have a route table, a router's own nested route tree, a journey list, a live action store? fromRoutes, fromReactRouter, fromJourneys and fromLiveStore adopt them under one documented merge order — nobody re-types anything. fromReactRouter transcribes a page name from a fully-static address (/projects/newprojects-new) and refuses, naming both doors, wherever there is nothing to transcribe — a :param, a *, the root. It never guesses a name.

Wire: nothing. A map is static data. It validates and freezes in one call, so it can be linted in CI and argued about in a pull request before your app runs at all.

The agent gets one tool per journey, plus four fixed generics. The tool list is the map, and its bytes never change for the life of a conversation. A whole-page dump is never served — that is the thesis, not an optimisation.

2 · Traversal — where am I, and how do I get there?

Declare two fields, on the map you already wrote: route on a page, goTo on an action. An action's claim is the edge; pages declare no edges to one another.

cart: { route: '/cart', actions: { pay: { does: 'Check out', goTo: 'checkout' } } },

Wire the session, and one line wherever your router already knows the page changed.

const session = graph.createSession({ node: 'catalog' });
session.sync('checkout');            // the router moved → the cursor moves

The agent gets where it is, whether arrival is claimed or observed — never a guessed third value meaning did not arrive — and the declared hops to any destination.

3 · Actions — what is possible here?

Declare what an action is, once, where it lives: does, writes, enabledWhen, goTo, confirm, verify, input, humanDecides.

Wire your own functions, by reference, when the component that renders them mounts.

const group = session.registerActions('checkout', {
  handlers: { 'place-order': (input) => shop.placeOrder(input) },
});
group.setEnabled('place-order', false);                // the greyed button
group.setBusy('place-order', 'Placing your order…');   // your words, never ours
session.updateState({ 'cart.items': 3 });              // your store → conditions re-evaluate

The agent gets one row per action that is offered here, carrying enabled, blockedBecause, busy, holds, goesTo, expects, highEffect, humanDecides and unblockedBy. Every stamp is presence-only: a key means your app said so, and no key means the library does not know.

Declared or wired? One question decides

Can this fact change while the page is open? If no, it is a declaration (enabledWhen). If yes, it is a wire (setBusy).

That is why there is no busyWhen: a condition can prove a state, but it cannot author a label, and a library-written label would be a library-written meaning.

And a fourth thing — which you never build

The relations between actions are the part people expect to have to author. You don't.

Is place-order blocked, and by what? add-to-cart writes cart.items; place-order waits on it. Nobody wrote an edge, and the edge is unambiguously there — so it is derived, never authored, and cannot drift from your graph:

session.whatUnblocks('checkout.place-order');
// [{ affordanceId: 'catalog.add-to-cart', viaKeys: ['cart.items'] }]

How do I get to checkout? A route is walked from the goTo claims you already made:

session.howToReach('checkout');   // [{ action: 'catalog.open', to: 'product' }, …]

Everything relational is derived from declarations you make for other reasons. There is no edge API in this library — not between pages, not between actions. The only thing you declare that cannot be derived is intent: "these steps, in this order, toward this goal" — a journey, because a preferred order is meaning, and meaning is yours.

The three contexts · What would free it · How to reach a page · Navigation graph


Map & Walker

The three contexts above have one sentence under them, and it is the same sentence this library's siblings say at their own altitude:

You declare the JourneyMap; the session is the Walker; the recording carries both.

Since 1.10.0 those two nouns have names in the API — defineJourneyMap and JourneyMap, permanent aliases of buildNavigationGraph and NavigationGraph. Same function object, same type, both names forever; neither is a rename, and a codebase may speak either dialect.

import { defineJourneyMap } from 'hcifootprint';

const map = defineJourneyMap('shop', { pages: { /* … */ } });
const walker = map.createSession({ node: 'catalog' });   // the walker IS the session

There is deliberately no Walker to construct. A walker is not a thing you wire up — it is the session you already create, standing on a node, moving. Three movers move it, and each lands on the record as a Cause (kind — was an offered edge fired, or did the world move? — plus principal, whose move it was):

| Mover | What moves the cursor | On the record | |---|---|---| | human | a real click in your own controls — watchPage senses it, contextful catches the app's own call | kind: 'fired', principal: 'user', with Attribution grading what that claim is worth | | agent | the four served verbs through the MCP door — whats_here, why, do_action, did_it_work | kind: 'fired', principal: 'agent', plus the offerId of the row it planned against | | guard | your data: an action's when / enabledWhen judged against the state your store pushed | the guard's own evidence, and blockedBecause / unblockedBy when it says no |

And the world moves on its own — a back button, a server push, an expiry. That is kind: 'stimulus': recorded, never silently absorbed.

Five moves make up walking, and every one of them is answered by something that already ships:

| A walker… | Here it is | |---|---| | looks | whats_here — one row per action offered on this node | | navigates | do_action on an action declaring goTo; the row carries goesTo, and howToReach walks the hops first | | moves inside a screen | a fire landing on an area, a tab or a modal moves the focus, not the page — session.focus, with focusHistory recording who moved it | | keeps a task list | journeyscommitJourney opens a frame, journeyStanding says where the flow stands | | verifies | did_it_work — the settled facts of one fire, never a guess; the drift sensor (checkGraph) is the honesty backstop under it |

The same pattern sits at three altitudes: footprintjs walks stages, agentfootprint (defineSkillMap) walks skills, and this walks screens.

Where the reader is: page, container, state

Sync pages; observe the deeper place. sync() moves the walker and decides what is served; observeFocus() says which tab or area the reader is in. Declare containers, and report the deepest one on screen.

Position has three tiers, and each has one door: the page (sync('run-detail') — the walker moves, and what is served moves with it), the container inside

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars10
CategoryAI
Updated28d ago
Forks1

Languages

TypeScript

Trust signals

97/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 info