DEV Community

odedkovach
odedkovach

Posted on Fully Autonomous

Your website has another user now: an AI agent

An assistant can find the right racket name and still recommend the wrong product. The 2025 and 2026 editions are different records. β€œUnder 150” needs a currency. A price on a manufacturer's page does not prove that a shop can deliver it.

We are building PadelTrue, a padel racket comparison site. Those small distinctions became the design problem for our agent interface: how can a model search our catalogue without quietly losing the qualifications that make an answer useful?

My view is that sites with useful structured information should give agents a way to query it. For a product catalogue, that means exact identifiers, explicit constraints and sources that travel with the answer.

This is a look at our working implementation. PadelTrue may earn commissions from eligible store links on its product pages; commissions do not determine these search results or calculated ratings.

One catalogue, several ways to use it

The catalogue contained 191 rackets on 2 October 2026. It feeds the human website, a read-only HTTP API, an MCP endpoint, and a downloadable dataset.

Published specifications + source URLs
                 |
           evidence gate
                 |
        one catalogue snapshot
                 |
        +--------+---------+------------+
        |        |         |            |
    web pages  JSON API  MCP tools  dataset export
Enter fullscreen mode Exit fullscreen mode

The gate is deliberately concrete. A specification with a value but no source URL holds the record back. So does a missing sourced price or fewer than three usable inputs to the rating model. A source URL alone cannot establish that a claim is true; checking the quoted evidence is a separate job.

This also creates a coverage tradeoff: some real rackets are absent because our record is incomplete. An empty search result describes our catalogue, not the entire market.

Tools should follow decisions people actually make

There are three buying-research operations:

Operation Tool What comes back
Find candidates search_rackets Exact models, applied filters, ordering rule, observed prices and missing rating inputs
Inspect one candidate get_racket Specifications with source URLs and source wording, plus the model's page
Compare two identified models compare_rackets Differences, missing-data cautions and a human-readable comparison URL

Two further tools, search and fetch, expose the same records as retrievable documents. That makes five tools in total.

The distinction matters. The document search is literal keyword retrieval. It should receive nox at10 18k 2026, not a whole conversation about budget, shipping and an aching wrist. The structured search accepts supported filters; it does not pretend to answer every constraint a user might mention.

Run a real query

Save this as racket-example.mjs and run node racket-example.mjs with Node.js 22 or later. It needs no package installation or API key.

The example pins the 2025-06-18 protocol revision supported by this deployment. It targets our stateless JSON responses; it is not a general MCP client with SSE support. The transport specification describes the request headers and initialization flow.

// Node.js 22+. This small client targets PadelTrue's JSON responses,
// not every possible MCP transport. No package install or API key.
const endpoint = 'https://padeltrue.com/mcp';
let id = 0;
let protocol;

async function rpc(method, params = {}, notify = false) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'application/json, text/event-stream',
      ...(protocol ? { 'MCP-Protocol-Version': protocol } : {}),
    },
    body: JSON.stringify({ jsonrpc: '2.0', ...(!notify && { id: ++id }), method, params }),
    signal: AbortSignal.timeout(15000),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  if (response.status === 202) return;
  const message = await response.json();
  if (message.error) throw new Error(JSON.stringify(message.error));
  return message.result;
}
async function call(name, args) {
  const result = await rpc('tools/call', { name, arguments: args });
  if (result.isError) throw new Error(JSON.stringify(result.structuredContent.error));
  return result.structuredContent;
}

const init = await rpc('initialize', {
  protocolVersion: '2025-06-18', capabilities: {},
  clientInfo: { name: 'racket-example', version: '1.0.0' },
});
protocol = init.protocolVersion;
if (protocol !== '2025-06-18') throw new Error(`Unreviewed protocol: ${protocol}`);
await rpc('notifications/initialized', {}, true);

const shortlist = await call('search_rackets', {
  query: 'nox at10 18k', year: 2026, shape: 'teardrop', limit: 3,
});
console.table(shortlist.results.map(({ name, year, page }) => ({ name, year, page })));
const candidate = shortlist.results[0];
if (!candidate) throw new Error('No match in this catalogue. Do not substitute another year.');
const { racket } = await call('get_racket', { slug: candidate.slug });
console.log(JSON.stringify({
  name: racket.name, page: racket.page,
  weight: racket.specifications.weight,
  missingRatingInputs: racket.missingRatingInputs,
}, null, 2));
Enter fullscreen mode Exit fullscreen mode

The search uses the exact year and shape. It returns the standard 2026 teardrop model in this snapshot. The detail response includes a weight object like this:

{
  "value": "360 to 375 g",
  "publishedAt": "https://noxsport.com/products/pala-at10-genius-18k-alum-2026-by-agustin-tapia",
  "wordingOnThePage": "PESO: 360 - 375g",
  "sourceType": "manufacturer"
}
Enter fullscreen mode Exit fullscreen mode

Here, publishedAt is a source URL, despite the date-like name. Observed prices have a separate checkedOn date. This response also flags balance as a missing rating input in our record. Keeping those meanings explicit is more useful than sending a bare number and hoping the client reconstructs its context.

The returned page is the exact PadelTrue model page. A reader can inspect the evidence and available buying links there. The model can use that URL in its response, but a tool result cannot guarantee that every assistant will display it.

Test the questions the service cannot answer

We checked the deployed endpoint on 2 October, including these cases:

Input Observed behavior
maxPrice: 150, without currency currency_required error
Unsupported shipsTo filter unknown_filter error
Invented model identifier not_found, with no substitute model
An exact year with no matching record An empty result list
Same supported search over HTTP and MCP Matching structured data

These are useful failures. Silently dropping the shipping constraint or swapping in a different edition would produce a more confident-looking answer and a worse buying decision.

The successful answers also carry limits. A recorded price is dated evidence, not a live stock or delivery promise. Ratings are calculated from published specifications using our published formula, not court tests. Missing rating inputs use a neutral midpoint, and the response lists the gaps so a client can explain that uncertainty. A request without a playing priority defaults to name order, which is browsing rather than a best-match recommendation.

Keep the human experience connected

Our Racket Lab explores a related human question: when changing rackets, what do you want to keep, and what do you want to change?

β€œThe racket grams are only half the story” is the idea behind it. Listed mass is one input; balance, setup and the player's experience matter too. The Lab's personal estimate is an unvalidated heuristic, not measured swingweight or a medical assessment. That estimate is separate from the catalogue tools shown here.

The two interfaces should complement each other: an agent narrows the records and exposes the evidence; the person still gets a visual place to compare choices and decide what to try.

A practical starting point for another site

Pick one useful question your site already answers. Give it stable identifiers and a small response schema. Preserve the facts that qualify an answer, such as currency, edition, source and observation date. Then test what happens when those facts are missing.

You can inspect our agent documentation and HTTP examples, or get the open dataset and its GitHub snapshots under CC BY 4.0. That data licence does not include product photographs. We also publish /llms.txt as a plain-text map and /llms-full.txt with expanded content. These are entry points a client can choose to read, not a guarantee of discovery or citations.

We have listed the service in MCP directories, including Smithery. That is distribution, not proof of adoption. Our 16-question Claude API citation check on 1 October returned no PadelTrue citations. That small result is specific to that test, not a verdict on every assistant. Passing an integration test and being discovered by a buyer are separate milestones.

I would start with this small, testable contract before adding a conversational interface. Which constraint does your own product API currently drop instead of admitting that it cannot answer?


Disclosure: AI agents drafted this article and ran the code examples against the deployed PadelTrue service. It describes our own project; it is not an independent review. The examples reflect the deployment tested on 2 October 2026.

Top comments (0)