The internet, readable by agents.
Manifest turns any URL into a structured map of what an AI agent can actually do on a page - every button, form, and input, with the fields it needs to fill and what's required before it can move on.
The problem
Give an AI agent a browser, and it can see a webpage. Give it a scraper, and it can read one. Neither tells it what it's allowed to do there.
Today, agents either take a screenshot and guess, or a developer hand-writes selectors that break the next time the page ships a redesign. There's no layer that tells an agent: here are the actions on this page, here's what each one needs, here's what's required.
That's the gap Manifest fills.
How it works
What you get back
response:
{
"url": "https://example.com",
"current_page_state": "Contact page with a message form",
"actions": [
{
"id": "email-input",
"label": "Email",
"type": "input",
"description": "Email address field",
"required": true
},
{
"id": "message-input",
"label": "Message",
"type": "textarea",
"description": "Message field",
"required": true
},
{
"id": "submit-form",
"label": "Submit",
"type": "button",
"description": "Submits the contact form",
"required": false,
"requires": [["email-input"], ["message-input"]]
}
],
"navigation": [
{ "label": "Home", "url": "/" }
]
}
"url": "https://example.com",
"current_page_state": "Contact page with a message form",
"actions": [
{
"id": "email-input",
"label": "Email",
"type": "input",
"description": "Email address field",
"required": true
},
{
"id": "message-input",
"label": "Message",
"type": "textarea",
"description": "Message field",
"required": true
},
{
"id": "submit-form",
"label": "Submit",
"type": "button",
"description": "Submits the contact form",
"required": false,
"requires": [["email-input"], ["message-input"]]
}
],
"navigation": [
{ "label": "Home", "url": "/" }
]
}
Agent instructions
Integrate the Manifest API into this project.
Manifest turns a webpage into a structured JSON "action manifest": every clickable, fillable, or submittable element on the page, each with a resolved locator (CSS selector, ARIA role, and accessible name) and a requires field describing which other actions must be completed first. requires is an array of groups, ANDed together — each inner group is a set of alternatives where any one satisfies it (OR). A single required field is just a one-item group. It sits between raw browser access (a headless browser gives you a DOM, not a list of things you can do) and content extraction (which gives you text, not actions) — reach for it whenever an agent needs to decide what to click or fill next instead of guessing selectors.
Preferred integration: MCP. Add the MCP server "io.omfang/manifest" to this project's MCP client config (also listed on Smithery if your tool installs from there). It exposes a streamable-http endpoint:
url: https://manifest.omfang.io/mcp header: x-api-key: ${apiKey}
Fallback: REST. If this setup doesn't support MCP yet, call the API directly:
Base URL: https://manifest.omfang.io Auth header: x-api-key: ${apiKey} Endpoint: POST /manifest Body: { "url": "<page to inspect>" }
Example response (either path returns this shape):
{ "current_page_state": "Login form with email/password fields", "actions": [ { "id": "email-input", "type": "input", "requires": [], "locator": { "css": "#email", "role": "textbox", "name": "Email" } }, { "id": "submit", "type": "button", "requires": [["email-input"], ["password-input"]], "locator": { "css": "button[type=submit]", "role": "button", "name": "Log in" } } ] }
Here, submit requires both email-input AND password-input — each is its own one-item group. A mutually-exclusive set (e.g. a size picker) would appear as a single group with multiple ids, like requires: [["size-5", "size-6", "size-7"]] — any one of those satisfies that group.
Prefer locator.role/locator.name over locator.css where possible — they hold up better across redesigns. Use requires to know which actions are still blocked on a prerequisite, and whether unblocking them means satisfying one alternative or several independent fields.