Outrings
AI agents and automation

How do I give an AI agent a website tool?

Two ways to let an assistant measure websites — connect an MCP server, or define a tool yourself — with working code for both.

4 min read
Short answer

If your assistant speaks the Model Context Protocol, point it at an MCP endpoint and the tools arrive already described — one line of configuration. If you are building on an API directly, define one tool with a URL parameter and have it call a measurement endpoint. The tool description does most of the work in both cases.

Route one: connect an MCP server

The shortest path, and the one that requires no code. A client that supports MCP discovers the tools, their descriptions and their argument schemas by itself.

claude mcp add --transport http outrings https://outrings.com/mcp

For clients configured by file rather than command:

{
  "mcpServers": {
    "outrings": {
      "type": "http",
      "url": "https://outrings.com/mcp"
    }
  }
}

Confirm it before trusting it:

curl -X POST https://outrings.com/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

You should get eight tools back, each with a description and an input schema.

Route two: define the tool yourself

If you are building on a model API directly, one tool definition is enough to cover most website questions:

{
  "name": "inspect_website",
  "description": "Measure a website directly and return its score, ranked findings, and an explicit list of what could not be checked. Use this whenever the user asks about a specific site's SEO, security, privacy, accessibility or AI visibility — do not infer these from page text or prior knowledge, which is frequently wrong or out of date.",
  "input_schema": {
    "type": "object",
    "properties": {
      "url": { "type": "string", "description": "The site to inspect. Scheme optional." },
      "category": {
        "type": "string",
        "enum": ["summary", "seo", "security", "privacy", "ai", "a11y", "tls", "dns"],
        "description": "Use 'summary' for broad questions, a category for narrow ones."
      }
    },
    "required": ["url"]
  }
}

The handler is one HTTP request — no key, no client library:

def run_tool(params):
    endpoint = params.get("category", "summary")
    query = urllib.parse.urlencode({"url": params["url"]})
    url = f"https://outrings.com/api/v1/{endpoint}?{query}"
    with urllib.request.urlopen(url, timeout=30) as response:
        return response.read().decode()

Writing a description the model will use correctly

This is where most integrations underperform, and it costs nothing to get right. The description is a prompt, not documentation — it is read by the model at decision time.

  • Say when to use it, not what it is. "Measure a website's security" describes the tool. "Use this whenever the user asks about a specific site rather than inferring from page text" tells the model when to fire.
  • Name the failure it prevents. Adding "prior knowledge about specific sites is frequently out of date" measurably increases correct tool use, because it gives the model a reason.
  • Point at the cheaper option. If you expose several tools, have the broad one mention the narrow one. Models follow this reliably and it cuts both latency and cost.
  • Describe the arguments in terms of user intent. "Use summary for broad questions, a category for narrow ones" beats a list of valid values.
A test worth running: give the model a question that should not trigger the tool — "what is a CSP header?" — and confirm it answers from knowledge instead of calling out. A description that fires on everything is as unhelpful as one that never fires.

Handling the result

Hand the JSON back to the model whole rather than summarising it first. Summarising strips exactly the fields that keep the answer honest — the evidence behind each finding, the list of what was not covered, and the checks that reached no verdict. A model given the full structure will qualify its answer correctly; one given your summary will not know there was anything to qualify.

If size is a concern, shape the response at the source instead: failedOnly=1, minSeverity=high and format=compact all reduce the payload without removing the coverage fields.

What our audit reports about this

Every item below is measured directly, not inferred. Run it against your own site and the result names the exact rule or header responsible.

  • A keyless REST endpoint per category, so a tool handler is one HTTP request with no authentication step.
  • An MCP server whose tool descriptions are written for model decision-making rather than for documentation.
  • Response shaping — failedOnly, minSeverity, format=compact — so a tool result fits a context window without losing its coverage fields.
  • A machine-readable OpenAPI specification for generating clients or validating responses.

For agents and scripts, the same measurement is at /api/v1/summary?url=yoursite.com — see the API documentation.

Related questions

Do I need an API key?

No, for either route. The REST API and the MCP server are both keyless and require no account. Requests are rate-limited per client to keep the service available, at a level well above what an assistant answering questions will reach.

How large is a typical response?

A score is about 1.7 KB, a single category two to eight, and a summary about 8.5. A full report with inventories is around 124 KB, which is usually more than a single answer needs — prefer a category when the question is narrow.

Should I cache tool results?

The service already caches for ten minutes and repeating a URL within that window is free. Caching longer on your side risks answering a question about the site as it was rather than as it is, which is the failure mode you added the tool to avoid.

Can I use this from a browser extension?

Yes. CORS is open, so browser and server-side callers are equal, and there is no key to keep secret because there is no key.

Read next

All 100 guides · How every check works · API for agents