# IP.LS — IP Lookup Service

> Keyless IP geolocation, ASN/ISP, timezone and DNS records for any IP or domain, plus your own IP.

- name: ls.ip/ipls-ip-lookup-service
- version: 1.4.0
- connection class: R0 (autonomous)
- trust: 80/100 flags: no-repository
- quality: 100/100 (strong)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/ls.ip%2Fipls-ip-lookup-service
- tools: https://api.protogrid.dev/v1/servers/ls.ip%2Fipls-ip-lookup-service/tools

**An agent can connect right now, no human step.** Remote endpoint, no authentication, reachable on the last probe.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "ipls-ip-lookup-service": {
      "type": "http",
      "url": "https://ip.ls/mcp"
    }
  }
}
```


## Trust

- hygiene: 80
- liveness: 100
- freshness: 100
- provenance: 45
- drivers: +dns-namespace +reachable +uptime-100% +updated-25d-ago -no-repository
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 100/100 (strong)

### protocol: 100 (weight 25)

- pass `protocol.modern`: supports 2026-07-28 (server/discover)
- pass `protocol.stateless`: answers without a session
- pass `protocol.transport`: streamable HTTP
- pass `protocol.list_ttl`: tool list is cacheable (ttlMs 3600000)

### authorization: n/a (weight 15)

- n/a `auth.prm`, `auth.as_metadata`, `auth.cimd`: no remote uses OAuth
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 100 (weight 30)

- pass `tools.descriptions`: every tool has a description
- pass `tools.description_length`: descriptions are concise
- pass `tools.schemas`: every tool has an object input schema
- pass `tools.annotations`: 6 of 6 tools declare readOnlyHint or destructiveHint
- pass `tools.directory_hints`: every tool declares readOnlyHint, destructiveHint, idempotentHint and openWorldHint
- pass `tools.token_cost`: about 966 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 100 (weight 15)

- pass `stability.changes`: 2 tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: n/a (weight 15)

- n/a `deps.known_vulns`, `deps.mcp_sdk_version`, `deps.resolvable`: no npm or PyPI package

- tools: 6, about 966 tokens to load them all
- tool set hash: 498e01299bc7c960fae1a5fad1d8dc07
- tools last changed: 2026-09-28T15:58:08.794Z
- badge: [![protogrid quality](https://protogrid.dev/badge/ls.ip/ipls-ip-lookup-service.svg)](https://protogrid.dev/servers/ls.ip/ipls-ip-lookup-service)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

- 2026-09-28 `ip_summary` changed (description, inputSchema)
  - before: Aggregate a batch of up to 5000 IPv4/IPv6 addresses (or up to 100 hostnames, each resolved server-side) into deterministic distributions by country, continent, ASN/operator and network prefix, each bucket carrying a bounded sample so the result can be spot-checked without re-running the batch. Counting, grouping and sampling happen server-side and are exactly reproducible: buckets sort by count desc then key asc, samples are the lowest addresses in the bucket, and the same input set always yields the same output regardless of order. Nothing is dropped silently — inputs that fail are counted in `errors`, addresses with no database record in `no_data`, and buckets beyond the listing in that dimension's `tail`. Invariants: counted + no_data.n = addresses, and each by_* dimension sums to counted with its remainder as `tail`. Counts are per occurrence, so a repeated address counts each time. Use this instead of ip_lookup once the input is in the hundreds or thousands — ip_lookup returns every row, which no context window can hold. Results are facts only: no datacenter/residential classification, no score, no verdict. Data source: DB-IP Lite (CC BY 4.0).
  - after: Summarize a large batch of IPs into distributions by country, continent, ASN/operator and network prefix, each bucket with a sample of addresses. Use instead of ip_lookup once the input is in the hundreds or thousands — ip_lookup returns every row, which no context window holds. Up to 5000 addresses or 100 hostnames. Counts are per occurrence; nothing is dropped silently (failed → `errors`, no database record → `no_data`, unlisted buckets → `tail`), the same input set always returns the same numbers, and the result is facts only — no datacenter/residential classification, no verdict. DB-IP Lite (CC BY 4.0).
- 2026-09-25 `ip_summary` added
  - Aggregate a batch of up to 5000 IPv4/IPv6 addresses (or up to 100 hostnames, each resolved server-side) into deterministic distributions by country, continent, ASN/operator and network prefix, each bucket carrying a bounded sample so the result can be spot-checked without re-running the batch. Counting, grouping and sampling happen server-side and are exactly reproducible: buckets sort by count desc then key asc, samples are the lowest addresses in the bucket, and the same input set always yields the same output regardless of order. Nothing is dropped silently — inputs that fail are counted in `errors`, addresses with no database record in `no_data`, and buckets beyond the listing in that dimension's `tail`. Invariants: counted + no_data.n = addresses, and each by_* dimension sums to counted with its remainder as `tail`. Counts are per occurrence, so a repeated address counts each time. Use this instead of ip_lookup once the input is in the hundreds or thousands — ip_lookup returns every row, which no context window can hold. Results are facts only: no datacenter/residential classification, no score, no verdict. Data source: DB-IP Lite (CC BY 4.0).

Full history: https://api.protogrid.dev/v1/servers/ls.ip%2Fipls-ip-lookup-service/changes

## Remotes

- https://ip.ls/mcp (streamable-http, auth none, reachable true, uptime30d 1)

## Packages

_none_

## Tools (6)

- `headers`: Return all HTTP request headers as seen by the IP.LS server (including X-Real-IP / X-Forwarded-For injected by the frontend proxy). Useful for debugging proxies and client identity.
- `ip_lookup`: Look up geolocation, ISP and network info for any IPv4/IPv6 address or hostname, or an array of them for batch lookup. Returns ip, city, region, country, coordinates, ASN/ISP, timezone and continent (full name, e.g. North America); when a domain is queried, an extra `domain` field echoes it. Data source: DB-IP Lite (CC BY 4.0). Anycast IPs are approximate.
- `ip_resolved`: Resolve a domain to all its A and AAAA records (deduplicated, up to 6) and return geolocation for each address. Returns an array of { ip, protocol, domain, city, region, country, coordinates, ASN/ISP, ... }. For a bare IP, returns a single-element array.
- `ip_self`: Get geolocation and network info about the caller's own public IP, plus the protocol stack (IPv4/IPv6) of the current connection. On dual-stack machines the stack is chosen by the client and may differ from `curl ip.ls`, which picks a random stack; use curl -4/-6 locally to get the other stack, or — for clients that cannot pick a stack (browser, PowerShell, most HTTP libraries) — the single-stack hostnames https://4.ip.ls / https://6.ip.ls (DNS selects the stack; an unreachable 6.ip.ls means no usable IPv6). Data comes from a server-side GeoIP lookup on the caller's real IP.
- `ip_summary`: Summarize a large batch of IPs into distributions by country, continent, ASN/operator and network prefix, each bucket with a sample of addresses. Use instead of ip_lookup once the input is in the hundreds or thousands — ip_lookup returns every row, which no context window holds. Up to 5000 addresses or 100 hostnames. Counts are per occurrence; nothing is dropped silently (failed → `errors`, no database record → `no_data`, unlisted buckets → `tail`), the same input set always returns the same numbers, and the result is facts only — no datacenter/residential classification, no verdict. DB-IP Lite (CC BY 4.0).
- `ua`: Return the User-Agent string of the current HTTP client.
