Is Agentic report for monogram.io showing a score of 99 out of 100

From 72 to 99 on Is Agentic: what we changed on monogram.io

  • Isaac
    Isaac
    Systems Engineer

On Aug 27, Vercel's Is Agentic scanner scored monogram.io 72 out of 100. 11 days and one pull request later, the same report reads 99. This post covers what the scanner measures, what we changed to move the number, and the checks that still fail.

What Is Agentic measures

Is Agentic is a free scoring tool Vercel launched in August 2026. Ora, an agent-experience research company, runs the technical audit against the public surface of a site and scores each check; Vercel operates the interface and groups the results into the score on the report page. The question it asks is narrower than the Cloudflare scanner we covered in How to get your website agent-ready and What isitagentready.com Actually Measures. Cloudflare asks whether a site speaks the emerging protocols. Is Agentic asks whether an ordinary agent can enter, read, and operate the site today.

The scoring model has three groups. Essential checks share an 80-point pool: server-rendered content, correct HTTP behavior, recoverable errors, usable form controls. Recommended checks share a 20-point pool and only switch on when the scan finds evidence they apply, so a site with no API is not penalized for lacking an OpenAPI spec. Emerging signals add a bonus capped at 5 points. Partial results earn proportional credit. Alongside the score, the report runs an observed agent journey: an agent is handed a task ("What does monogram.io do and who is it for?") and an evaluator notes where it got stuck. The journey does not change the number, but it is the part of the report worth reading first.

Where we started

The Aug 27 scan gave us 57 of 80 on Essential and 9.6 of 20 on Recommended, plus the 5 bonus points. The failures clustered in a few places:

  • Unknown /api/* paths returned the Next.js HTML shell instead of a 404 an agent could parse. To a crawler, every path on the site appeared to exist.
  • Markdown content negotiation had shipped in April, but the Vary: Accept header never reached the client. A CDN could serve cached HTML to an agent asking for markdown, or the reverse, depending on which variant landed in cache first.
  • The OpenAPI spec had no operationId on any operation, no error schema, and no versioning policy.
  • The JSON-LD identity block was typed ProfessionalService alone, which the scanner does not resolve as an Organization.
  • /mcp returned 501. /auth.md told agents to register for credentials while /oauth/authorize returned 501 as well.
  • There was no developer portal, so nothing on the site pointed a human or an agent at any of the machine-readable surfaces.

Some of these were capability gaps. Several were labeling: the scanner literal-matches the JSON-LD @type and the OpenAPI operationId, and it does not walk the schema.org subclass tree. Both kinds cost the same points, and both are worth fixing, because the scanner behaves the way a real agent behaves. An agent that cannot find the label does not infer it.

Errors that give an agent a way back

Unknown API paths now return an RFC 9457 application/problem+json body with a 404 status, an instance field, and a hint pointing at the OpenAPI spec. The markdown routes return a markdown 404 with recovery links to llms.txt, the sitemap, the API catalog, and the home page; the HTML 404 page carries the same links. The scanner rates this "the strongest 404 contract" it recognizes. The implementation is a single catch-all route at app/api/[...unmatched]/route.ts and a shared not-found body for the markdown generators.

Vary: Accept and the Next.js bug behind it

This was the one check we could not pass without an upgrade. Next.js 15.1 and earlier set the Vary header in base-server.setVaryHeader() by overwriting whatever was already there with its internal RSC list. Middleware, route handlers, and next.config headers all lost. Next 15.2 changed the call to append, so we moved from 15.1.11 to 15.5.24.

That exposed a second problem. Vercel's prerender cache strips custom response headers from ISR-served responses; a fresh render carried Vary: Accept and a cache HIT dropped it. The markdown mirrors now render per request with force-dynamic and an s-maxage Cache-Control, which keeps the header on every response at the cost of a function invocation per markdown request instead of a static hit. We took the trade; the header matters more than the milliseconds.

An OpenAPI spec an agent can call

Every operation now has a unique operationId and a description. The 404, 429, and 500 responses reference a shared Problem schema. The spec declares security: [] at the root, which is the machine-readable way of saying the surface needs no authentication. /api/v1/health is the canonical health endpoint and /api/health stays as an alias. The versioning policy is written into the spec: stable v1 URLs, and deprecation signaled with an RFC 8594 Sunset header.

The public API and discovery endpoints also emit RateLimit-* headers. These are fixed advisory values, not a live counter, and the spec and the developer portal both say so. An agent reading the headers should treat them as a budget hint, not a promise.

A developer portal with nothing to sign up for

/developers is a server-rendered page with no client JavaScript: a 3-step quickstart, an index of every machine-readable surface on the site, the API conventions, access terms, and a contact address. Its markdown mirror at /developers.md carries identical copy. The access section is short because there is nothing to configure: no API key, no registration, no OAuth flow, no sandbox. Every endpoint is public and read-only.

The agent-auth status is single-sourced. One constant, AGENT_AUTH_ENABLED, drives /auth.md, the developers page, and its mirror. The auth walkthrough still exists and still passes its 8 structural checks, but it now carries a status block saying that credentialed access is not enabled, and the portal no longer advertises registration. Flipping the constant updates every surface at once, which is the only way we found to keep the docs from promising a flow that returns 501.

A real MCP server

/mcp serves a Streamable HTTP MCP server built on mcp-handler. It is stateless: a fresh server and transport per POST, no session store, and SSE disabled so nothing initializes Redis. It exposes 5 read-only tools: get_site_overview, read_page, search_blog, list_case_studies, and get_company_info. The server card is one shared constant served from both /.well-known/mcp/server-card.json and the /.well-known/mcp.json alias, so the two cannot drift, and it no longer claims resources or prompts the server does not register. list_case_studies excludes anything marked noIndex in Sanity, so a hidden case study never reaches an agent through the back door.

Labels the scanner reads literally

The organization node in the homepage JSON-LD is now typed ["Organization", "ProfessionalService"]. The homepage metadata override had been dropping the layout's og:image and og:type, because Next metadata does not deep-merge; both are restored through a shared default. The visible hero headline is the page's single h1 instead of a separate screen-reader-only element. None of these changed what the site does. All of them changed what an agent can read off it.

What still fails

The report lists 9 findings. The ones we are not going to argue with:

  • Content without JavaScript: partial. The homepage HTML weighs about 600 KB, and the scanner counts 4,375 characters of readable text in it: a 2.6% content ratio by its measure, against a 5% target. The observed agent journey said the same thing in plainer words: the pages were fetched fine but came back as heavily client-rendered markup the agent could not turn into structure, and it fell back to LinkedIn and web search to finish the task. It also could not find pricing, because there is no pricing page. That is a finding about the site, and the fix is a lighter homepage.
  • Brand name discoverability: failed. "Monogram" is a common word. A clean search returns 8 results and none of them is our domain. No code change fixes this.
  • Function calling compatibility: partial. 5 of 5 operations have an operationId; 0 of 5 have a typed response schema. This one is on the list.
  • `llms.txt` length: partial. At 40,733 characters it exceeds the 30,000-character recommendation for a navigation index. Every case study and technology page is listed, which is why.
  • CLI tool: partial. We have not published one, and will not until there is something to script beyond reads.
  • Rate limiting: the headers are advisory and /mcp is not throttled. Neither are the markdown mirrors, which offer the same underlying access. The right fix is a rule at the edge, not a header on an endpoint nothing enforces.

Is the number the point?

No. The score is a proxy, and a proxy built by people who are open about its limits: their methodology page says a score is not a security, accessibility, quality, or compliance certification, and that automated checks produce partial and incorrect results. What the score is good for is turning "make the site agent-ready" into a list of specific, checkable behaviors with evidence attached to each one. Most of ours were small changes once the evidence was in front of us. The Next.js upgrade was the exception, and it was the one that mattered most.

At Monogram, we run Is Agentic and the Cloudflare scan on the sites we ship, and we read the agent journey before we read the score. If you want to see where yours stands, the scan takes about a minute:

npx is-agentic yourdomain.com

Then read the journey.