# lookupd docs

- Servers indexed: 62033 servers
- Answering right now: 24210 servers
- Last crawl: 2026-11-11T15:17:20.593Z
- API base: https://api.lookupd.com
- MCP URL: https://api.lookupd.com/mcp

Every item has the same parts: purpose, inputs, output, one example with its real output, errors. Example outputs were captured from https://api.lookupd.com; outputs longer than 40 lines are cut.

Set up an agent with this line: Set up lookupd for me: read https://lookupd.com/agent.md and follow it.

## CLI

The lookupd command, installed on demand with npx. Every example runs as written.

### lookupd find

Find MCP servers for a task and print ranked candidates with install snippets.

| name | type | required | meaning |
|---|---|---|---|
| task | string (positional, one or more words) | yes | Short capability phrase, for example read Linear issues. |
| --runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Runtime that selects the install snippet. Default: detected from the environment. |
| --limit | integer | no | Number of results to ask for (the API allows 1 to 10, default 5). |
| --json | flag | no | Print the API response as JSON instead of a text list. |

Output: A ranked text list; with --json, the same JSON as GET /v0.1/find. Rankings are provisional.

Example:

```
npx -y lookupd@latest find "read Linear issues" --limit 1 --json
```

Output of the example:

```
{
  "results": [
    {
      "name": "app.linear/linear",
      "title": "Linear",
      "description": "Find and manage issues, projects, and documents in your Linear workspace.",
      "kind": "mcp_server",
      "status": "active",
      "version": "1.0.2",
      "repositoryUrl": null,
      "websiteUrl": "https://linear.app",
      "transports": [
        "streamable-http"
      ],
      "install": [
        {
          "runtime": "claude-code",
          "kind": "command",
          "label": "remote (streamable-http)",
          "value": "claude mcp add --transport http linear https://mcp.linear.app/mcp"
        },
        {
          "runtime": "claude-code",
          "kind": "command",
          "label": "remote (streamable-http)",
          "value": "claude mcp add --transport http linear https://mcp.linear.app/mcp/readonly"
        }
      ],
      "score": 2.8,
      "summary": null,
      "capabilityTags": [],
      "health": {
        "status": "auth_required",
        "lastProbedAt": "2026-11-04T00:37:25.700Z",
        "lastUpAt": "2026-11-04T00:37:25.700Z",
        "uptime7d": 1,
        "p50LatencyMs": null,
        "toolsCount": null,
        "toolsChangedAt": null
      },
... (9 more lines)
```

Errors:

- usage: lookupd find <task...> (exit 1): no task given
- --runtime must be one of claude-code, codex, claude-desktop, cursor, vscode, generic (exit 1)
- lookupd: rate limited; retry in Ns (exit 1)

### lookupd telemetry

Turn anonymous outcome reports on or off, or show the current setting and where it comes from. On by default from 0.2.0; DO_NOT_TRACK=1 or LOOKUPD_TELEMETRY=0 in the environment turns them off.

| name | type | required | meaning |
|---|---|---|---|
| mode | enum: on \| off \| status | yes | on lists report_outcome in the stdio bridge; off hides it; status prints the setting. |

Output: status prints on or off, then the source in parentheses: default, lookupd telemetry on|off, DO_NOT_TRACK or LOOKUPD_TELEMETRY. on and off print a one-line confirmation.

Example:

```
npx -y lookupd@latest telemetry status
```

Output of the example:

```
unset
```

Errors:

- usage: lookupd telemetry on|off|status (exit 1): mode missing or unknown

### lookupd login

Store an optional API key in ~/.config/lookupd/config.json. Lookups work without one.

| name | type | required | meaning |
|---|---|---|---|
| --key | string, lk_ followed by letters and digits | yes | The API key to store. |

Output: API key stored

Example:

```
npx -y lookupd@latest login --key lk_YOUR_KEY
```

Output of the example:

```
API key stored
```

Errors:

- usage: lookupd login --key lk_<key> (exit 1): key missing or malformed

### lookupd hooks

Install, remove or show the opt-in call hook in Claude Code (~/.claude/settings.json) or Codex (~/.codex/hooks.json). The hook runs after each MCP tool call and sends one call event: the server's registry name (resolved from the URL or package in your MCP config), the tool name, whether the call succeeded, your runtime, the client's session id and the lookup id when known. Never arguments, output, prompts or paths. It sends nothing while telemetry is off. Codex asks you to trust a new hook once with /hooks.

| name | type | required | meaning |
|---|---|---|---|
| action | enum: install \| remove \| status | yes | What to do. |
| --client | enum: claude-code \| codex | no | Whose hook file to edit; default claude-code. |

Output: install and remove print the file they changed and what the hook sends; status prints one line per client.

Example:

```
npx -y lookupd@latest hooks status
```

Output of the example:

```
claude-code: not installed (~/.claude/settings.json)
codex: not installed (~/.codex/hooks.json)
```

Errors:

- usage: lookupd hooks install|remove|status [--client claude-code|codex] (exit 1)

### lookupd mcp

Serve lookupd's MCP tools over stdio by bridging to https://api.lookupd.com/mcp. MCP clients launch this command; you do not run it by hand.

| name | type | required | meaning |
|---|---|---|---|
| (none) | | | |

Output: No output of its own. It speaks MCP over stdin and stdout. The first run with the default telemetry setting prints a one-time notice to stderr.

Example:

```
claude mcp add lookupd -- npx -y lookupd@latest mcp
```

Output of the example:

```
(no output: the process serves MCP over stdio until the client closes it)
```

Errors:

- The remote endpoint is unreachable: the bridge lists no remote tools until it connects

## MCP

Four tools on the remote endpoint https://api.lookupd.com/mcp (streamable HTTP, no login). The stdio bridge (npx -y lookupd@latest mcp) serves the same tools.

### find_capability

Find MCP servers that provide a capability for a task. Pass a short capability phrase, not the user's full request. Lookups are free.

| name | type | required | meaning |
|---|---|---|---|
| task | string, 1-200 characters | yes | Short capability phrase. |
| kind | enum: mcp_server | no | Only mcp_server in this release. |
| runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Caller's runtime; selects the install snippet. |
| verified_only | boolean | no | Only verified (registered) publishers. |
| max_results | integer, 1-10 | no | Default 5. |
| capability | enum: tag from lookupd's taxonomy | no | Capability tag to prefer, for example issue-tracking. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |

Output: JSON: results[] (name, install snippets, score, health, lookupd metadata incl. verification and publisher), featured[] (reserved for labeled placement; empty, since placement is not offered), ranking: provisional, and lookup_id (quote it on report_outcome so the outcome ties to this search).

Example:

```
curl -s -X POST https://api.lookupd.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"find_capability","arguments":{"task":"read Linear issues","runtime":"generic","max_results":1}}}'
```

Output of the example:

```
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": "(the structuredContent below, as a JSON string)"
      }
    ],
    "structuredContent": {
      "results": [
        {
          "name": "app.linear/linear",
          "title": "Linear",
          "description": "Find and manage issues, projects, and documents in your Linear workspace.",
          "kind": "mcp_server",
          "status": "active",
          "version": "1.0.2",
          "repositoryUrl": null,
          "websiteUrl": "https://linear.app",
          "transports": [
            "streamable-http"
          ],
          "install": [
            {
              "runtime": "generic",
              "kind": "config",
              "label": "remote (streamable-http) → your MCP client config",
              "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp\"\n    }\n  }\n}"
            },
            {
              "runtime": "generic",
              "kind": "config",
              "label": "remote (streamable-http) → your MCP client config",
              "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp/readonly\"\n    }\n  }\n}"
            }
          ],
          "score": 2.8,
          "summary": null,
          "capabilityTags": [],
          "health": {
... (21 more lines)
```

Errors:

- Invalid arguments: the tool result is an error naming the field
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### get_entry

Full details for one entry. Pass name, or url or package for a server already in your MCP config to find its registry name.

| name | type | required | meaning |
|---|---|---|---|
| name | string, 1-200 characters | no | Registry name, for example app.linear/linear. |
| url | string, 1-500 characters | no | Remote URL from the MCP config. |
| package | string, 1-200 characters | no | npm or PyPI package from the MCP config. |
| runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Runtime for install snippets. Default: generic. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |

Output: JSON: raw server.json, install snippets, lookupd metadata, and setup: what an agent needs to make it work (transports, runtime hints, environment variables and headers with required and secret flags, whether the endpoint needs a login, and the last 30 days of outcome reports: success rate and failure classes). When several entries launch the same url or package, matches lists them, most likely first. Also changes: the newest 10 things lookupd saw change on it (tools added, removed or schema-changed; version and description changes), newest first.

Example:

```
curl -s -X POST https://api.lookupd.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_entry","arguments":{"name":"app.linear/linear"}}}'
```

Output of the example:

```
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": "(the structuredContent below, as a JSON string)"
      }
    ],
    "structuredContent": {
      "entry": {
        "name": "app.linear/linear",
        "title": "Linear",
        "description": "Find and manage issues, projects, and documents in your Linear workspace.",
        "kind": "mcp_server",
        "status": "active",
        "version": "1.0.2",
        "repositoryUrl": null,
        "websiteUrl": "https://linear.app",
        "transports": [
          "streamable-http"
        ],
        "install": [
          {
            "runtime": "generic",
            "kind": "config",
            "label": "remote (streamable-http) → your MCP client config",
            "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp\"\n    }\n  }\n}"
          },
          {
            "runtime": "generic",
            "kind": "config",
            "label": "remote (streamable-http) → your MCP client config",
            "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp/readonly\"\n    }\n  }\n}"
          }
        ],
        "score": 0,
        "summary": null,
        "capabilityTags": [],
        "health": {
          "status": "auth_required",
... (77 more lines)
```

Errors:

- isError: No entry named <name>
- isError: Pass name, url or package
- isError: No entry launches <url or package>
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### health

Probe status of an entry: up, auth_required, down, dead, not_probeable or unprobed, with 7-day uptime, median latency, tool count and the last probe.

| name | type | required | meaning |
|---|---|---|---|
| name | string, 1-200 characters | yes | Registry name. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |

Output: JSON: name, status, lastSeenAt, health, lastProbe.

Example:

```
curl -s -X POST https://api.lookupd.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"health","arguments":{"name":"app.linear/linear"}}}'
```

Output of the example:

```
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": "(the structuredContent below, as a JSON string)"
      }
    ],
    "structuredContent": {
      "name": "app.linear/linear",
      "status": "active",
      "lastSeenAt": "2026-11-01T08:51:30.650Z",
      "health": {
        "status": "auth_required",
        "lastProbedAt": "2026-11-04T00:37:25.700Z",
        "lastUpAt": "2026-11-04T00:37:25.700Z",
        "uptime7d": 1,
        "p50LatencyMs": null,
        "toolsCount": null,
        "toolsChangedAt": null
      },
      "lastProbe": {
        "startedAt": "2026-11-04T00:37:25.510Z",
        "status": "auth_required",
        "errorClass": "auth",
        "httpStatus": 401,
        "latencyMs": null
      }
    }
  },
  "jsonrpc": "2.0",
  "id": 1
}
```

Errors:

- isError: No entry named <name>
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### report_outcome

After a task used an MCP server, report once per server whether it worked. Structured fields only; never task text, prompts or paths. The stdio bridge hides this tool when the user turned telemetry off.

| name | type | required | meaning |
|---|---|---|---|
| entry | string, 1-200 characters | yes | Registry name of the server that was used. |
| success | boolean | yes | Whether the server did the job. |
| capability_tag | enum: tag from lookupd's taxonomy | no | What the server was used for. |
| latency_bucket | enum: lt1s \| lt5s \| lt30s \| gte30s | no | How long the call took. |
| runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Caller's runtime. |
| error_class | enum: auth \| timeout \| schema \| tool_error \| other | no | Why it failed, if it did. |
| session | string, 8-64 of [A-Za-z0-9_-] | no | A random id the client makes up once per conversation and repeats on every report in it; never derived from the user, project or machine. |
| step | integer >= 1 | no | Order in which this server was first used in the conversation. |
| tool | string, 1-120 of [A-Za-z0-9_.-] | no | The tool used most on the server, by name. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |
| found_via | enum: lookupd \| installed | no | Whether lookupd found the server or it was already installed. |
| lookup_id | string, uuid | no | The lookup_id from the find response whose results led to this server, so the report ties to that exact search and the position the server was shown at. |

Output: JSON: accepted, ignored (unknown entry names are ignored), principal.

Example:

```
curl -s -X POST https://api.lookupd.com/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"report_outcome","arguments":{"entry":"app.linear/linear","success":true,"latency_bucket":"lt1s","runtime":"generic"}}}'
```

Output of the example:

```
{
  "result": {
    "content": [
      {
        "type": "text",
        "text": "(the structuredContent below, as a JSON string)"
      }
    ],
    "structuredContent": {
      "accepted": 1,
      "ignored": 0,
      "principal": "anonymous"
    }
  },
  "jsonrpc": "2.0",
  "id": 1
}
```

Errors:

- Unknown fields are rejected, not dropped
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

## REST API

Base URL https://api.lookupd.com. Lookups need no account. Limited endpoints: /v0.1/find, /v0.1/events and the MCP endpoint, 60 requests per minute per IP. Errors are RFC 9457 application/problem+json.

### GET /v0.1/find

Find MCP servers for a task. Same engine as the find_capability tool.

| name | type | required | meaning |
|---|---|---|---|
| task | query string, 1-200 characters | yes | Short capability phrase. |
| kind | enum: mcp_server | no | Only mcp_server in this release. |
| runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Selects the install snippet. |
| verified_only | enum: true \| false \| 1 \| 0 | no | Only verified (registered) publishers. |
| limit | integer | no | Results to return; clamped to 1-10, default 5. |
| capability | enum: tag from lookupd's taxonomy | no | Capability tag to prefer. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |
| X-Lookupd-Install | header, uuid | no | The CLI's random install id, sent while telemetry is on (rotate it with lookupd telemetry reset), so repeat use can be counted. Never required. |

Output: 200 JSON: results[] (organic; lookupd.verification and lookupd.publisher per result), featured[] (reserved for labeled placement; empty, since placement is not offered), ranking: provisional, and lookup_id (quote it in the outcome event so it ties to this search).

Example:

```
curl -s "https://api.lookupd.com/v0.1/find?task=read%20Linear%20issues&limit=1"
```

Output of the example:

```
{
  "results": [
    {
      "name": "app.linear/linear",
      "title": "Linear",
      "description": "Find and manage issues, projects, and documents in your Linear workspace.",
      "kind": "mcp_server",
      "status": "active",
      "version": "1.0.2",
      "repositoryUrl": null,
      "websiteUrl": "https://linear.app",
      "transports": [
        "streamable-http"
      ],
      "install": [
        {
          "runtime": "generic",
          "kind": "config",
          "label": "remote (streamable-http) → your MCP client config",
          "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp\"\n    }\n  }\n}"
        },
        {
          "runtime": "generic",
          "kind": "config",
          "label": "remote (streamable-http) → your MCP client config",
          "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp/readonly\"\n    }\n  }\n}"
        }
      ],
      "score": 2.8,
      "summary": null,
      "capabilityTags": [],
      "health": {
        "status": "auth_required",
        "lastProbedAt": "2026-11-04T00:37:25.700Z",
        "lastUpAt": "2026-11-04T00:37:25.700Z",
        "uptime7d": 1,
        "p50LatencyMs": null,
        "toolsCount": null,
        "toolsChangedAt": null
      },
... (9 more lines)
```

Errors:

- 400 application/problem+json with detail naming the bad field
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### GET /v0.1/entries/{name}

Full details for one entry. The name contains one slash and goes in the path as is.

| name | type | required | meaning |
|---|---|---|---|
| name | path, registry name | yes | For example app.linear/linear. |
| runtime | enum: claude-code \| codex \| claude-desktop \| cursor \| vscode \| generic | no | Query string; selects install snippets. Default: generic. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |

Output: 200 JSON: raw server.json, install snippets, lookupd metadata, setup facts and changes (as get_entry).

Example:

```
curl -s "https://api.lookupd.com/v0.1/entries/app.linear/linear"
```

Output of the example:

```
{
  "entry": {
    "name": "app.linear/linear",
    "title": "Linear",
    "description": "Find and manage issues, projects, and documents in your Linear workspace.",
    "kind": "mcp_server",
    "status": "active",
    "version": "1.0.2",
    "repositoryUrl": null,
    "websiteUrl": "https://linear.app",
    "transports": [
      "streamable-http"
    ],
    "install": [
      {
        "runtime": "generic",
        "kind": "config",
        "label": "remote (streamable-http) → your MCP client config",
        "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp\"\n    }\n  }\n}"
      },
      {
        "runtime": "generic",
        "kind": "config",
        "label": "remote (streamable-http) → your MCP client config",
        "value": "{\n  \"mcpServers\": {\n    \"linear\": {\n      \"url\": \"https://mcp.linear.app/mcp/readonly\"\n    }\n  }\n}"
      }
    ],
    "score": 0,
    "summary": null,
    "capabilityTags": [],
    "health": {
      "status": "auth_required",
      "lastProbedAt": "2026-11-04T00:37:25.700Z",
      "lastUpAt": "2026-11-04T00:37:25.700Z",
      "uptime7d": 1,
      "p50LatencyMs": null,
      "toolsCount": null,
      "toolsChangedAt": null
    },
    "lookupd": {
... (65 more lines)
```

Errors:

- 404 No entry named <name>

### GET /v0.1/entries/{name}/health

Probe status of one entry. Same as the health tool.

| name | type | required | meaning |
|---|---|---|---|
| name | path, registry name | yes | For example app.linear/linear. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |

Output: 200 JSON: name, status, lastSeenAt, health, lastProbe.

Example:

```
curl -s "https://api.lookupd.com/v0.1/entries/app.linear/linear/health"
```

Output of the example:

```
{
  "name": "app.linear/linear",
  "status": "active",
  "lastSeenAt": "2026-11-01T08:51:30.650Z",
  "health": {
    "status": "auth_required",
    "lastProbedAt": "2026-11-04T00:37:25.700Z",
    "lastUpAt": "2026-11-04T00:37:25.700Z",
    "uptime7d": 1,
    "p50LatencyMs": null,
    "toolsCount": null,
    "toolsChangedAt": null
  },
  "lastProbe": {
    "startedAt": "2026-11-04T00:37:25.510Z",
    "status": "auth_required",
    "errorClass": "auth",
    "httpStatus": 401,
    "latencyMs": null
  }
}
```

Errors:

- 404 No entry named <name>

### GET /v0.1/servers

List servers in the Official MCP Registry list format, so tools that read that registry can read this mirror.

| name | type | required | meaning |
|---|---|---|---|
| limit | integer, 1-100 | no | Page size. Default 30. |
| cursor | string | no | metadata.nextCursor from the previous page. |
| search | string | no | Case-insensitive substring of the server name. |
| version | string | no | latest, or an exact version. |
| updated_since | RFC 3339 datetime | no | Only versions updated since then; deleted entries are included. |
| include_deleted | boolean | no | true includes deleted entries. |

Output: 200 JSON: servers[] and metadata (count, nextCursor when more pages exist).

Example:

```
curl -s "https://api.lookupd.com/v0.1/servers?limit=1&search=linear"
```

Output of the example:

```
{
  "servers": [
    {
      "server": {
        "name": "app.linear/linear",
        "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
        "remotes": [
          {
            "url": "https://mcp.linear.app/sse",
            "type": "sse"
          },
          {
            "url": "https://mcp.linear.app/mcp",
            "type": "streamable-http"
          }
        ],
        "version": "1.0.0",
        "repository": {},
        "description": "MCP server for Linear project management and issue tracking"
      },
      "_meta": {
        "com.lookupd/v1": {
          "indexedAt": "2026-11-01T08:51:30.630Z",
          "lastSeenAt": "2026-11-01T08:51:30.650Z",
          "status": "active",
          "verification": "unverified",
          "health": {
            "status": "auth_required",
            "lastProbedAt": "2026-11-04T00:37:25.700Z",
            "lastUpAt": "2026-11-04T00:37:25.700Z",
            "uptime7d": 1,
            "p50LatencyMs": null,
            "toolsCount": null,
            "toolsChangedAt": null
          },
          "enrichment": null
        },
        "io.modelcontextprotocol.registry/official": {
          "status": "active",
          "isLatest": false,
... (12 more lines)
```

Errors:

- 400 Invalid limit, Invalid updated_since or Invalid cursor

### GET /v0.1/servers/{serverName}/versions

Every known version of one server.

| name | type | required | meaning |
|---|---|---|---|
| serverName | path, registry name | yes | For example app.linear/linear. |

Output: 200 JSON: servers[] and metadata.count.

Example:

```
curl -s "https://api.lookupd.com/v0.1/servers/app.linear%2Flinear/versions"
```

Output of the example:

```
{
  "servers": [
    {
      "server": {
        "name": "app.linear/linear",
        "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
        "remotes": [
          {
            "url": "https://mcp.linear.app/sse",
            "type": "sse"
          },
          {
            "url": "https://mcp.linear.app/mcp",
            "type": "streamable-http"
          }
        ],
        "version": "1.0.0",
        "repository": {},
        "description": "MCP server for Linear project management and issue tracking"
      },
      "_meta": {
        "com.lookupd/v1": {
          "indexedAt": "2026-11-01T08:51:30.630Z",
          "lastSeenAt": "2026-11-01T08:51:30.650Z",
          "status": "active",
          "verification": "unverified",
          "health": {
            "status": "auth_required",
            "lastProbedAt": "2026-11-04T00:37:25.700Z",
            "lastUpAt": "2026-11-04T00:37:25.700Z",
            "uptime7d": 1,
            "p50LatencyMs": null,
            "toolsCount": null,
            "toolsChangedAt": null
          },
          "enrichment": null
        },
        "io.modelcontextprotocol.registry/official": {
          "status": "active",
          "isLatest": false,
... (113 more lines)
```

Errors:

- 404 Server not found

### GET /v0.1/servers/{serverName}/versions/{version}

One version of one server. Use latest for the newest.

| name | type | required | meaning |
|---|---|---|---|
| serverName | path, registry name | yes | For example app.linear/linear. |
| version | path | yes | An exact version, or latest. |

Output: 200 JSON: one server entry.

Example:

```
curl -s "https://api.lookupd.com/v0.1/servers/app.linear%2Flinear/versions/latest"
```

Output of the example:

```
{
  "server": {
    "name": "app.linear/linear",
    "icons": [
      {
        "src": "https://static.linear.app/integrations/mcp/icon.svg",
        "sizes": [
          "any"
        ],
        "mimeType": "image/svg+xml"
      },
      {
        "src": "https://static.linear.app/integrations/mcp/icon-256.png",
        "sizes": [
          "256x256"
        ],
        "mimeType": "image/png"
      }
    ],
    "title": "Linear",
    "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
    "remotes": [
      {
        "url": "https://mcp.linear.app/mcp",
        "type": "streamable-http"
      },
      {
        "url": "https://mcp.linear.app/mcp/readonly",
        "type": "streamable-http"
      }
    ],
    "version": "1.0.2",
    "websiteUrl": "https://linear.app",
    "description": "Find and manage issues, projects, and documents in your Linear workspace."
  },
  "_meta": {
    "com.lookupd/v1": {
      "indexedAt": "2026-11-01T08:51:30.630Z",
      "lastSeenAt": "2026-11-01T08:51:30.650Z",
      "status": "active",
... (21 more lines)
```

Errors:

- 404 Server version not found

### POST /v0.1/events

Send 1 to 100 outcome, install or call events in one JSON body. Structured fields only; unknown fields are rejected.

| name | type | required | meaning |
|---|---|---|---|
| events | array of 1-100 objects | yes | Outcome or install: kind, entry, success (required for outcome), and optional capability_tag, latency_bucket, runtime, error_class, session, step, tool, domain, found_via, lookup_id, as in report_outcome. Call (what the opt-in hook sends after a tool call): kind call, entry, tool, ok, and optional latency_ms, runtime, session, lookup_id. |
| X-Lookupd-Install | header, uuid | no | The CLI's random install id, sent while telemetry is on (rotate it with lookupd telemetry reset), so repeat use can be counted. Never required. |

Output: 202 JSON: accepted, ignored (unknown entry names), principal.

Example:

```
curl -s -X POST https://api.lookupd.com/v0.1/events -H 'content-type: application/json' -d '{"events":[{"kind":"outcome","entry":"app.linear/linear","success":true,"latency_bucket":"lt1s","runtime":"generic"}]}'
```

Output of the example:

```
{
  "accepted": 1,
  "ignored": 0,
  "principal": "anonymous"
}
```

Errors:

- 400 Body must be JSON
- 400 with detail naming the bad field
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### GET /v0.1/resolve

The listing behind a server already in an MCP config: pass its remote url, or its npm or PyPI package as typed after npx -y or uvx, and get the registry name to use on events. The call hook asks this once per server and caches the answer.

| name | type | required | meaning |
|---|---|---|---|
| url | query string, 1-500 characters | no | Remote URL from the MCP config. |
| package | query string, 1-200 characters | no | npm or PyPI package from the MCP config, for example @acme/pdf-mcp or pdf-mcp@1.2.0. |
| session | string, 8-64 of A-Z a-z 0-9 _ - | no | The random id you make up once per conversation and repeat on every lookupd call in it, report_outcome included, so lookups and outcomes join up. Never derived from the user, project or machine. |
| domain | enum: software-engineering \| devops-cloud \| data-analytics \| finance \| legal-compliance \| sales-crm \| marketing \| customer-support \| hr-recruiting \| operations \| research \| education \| healthcare \| ecommerce \| design-media \| personal \| other | no | What the conversation is about. |
| X-Lookupd-Install | header, uuid | no | The CLI's random install id, sent while telemetry is on (rotate it with lookupd telemetry reset), so repeat use can be counted. Never required. |

Output: 200 JSON: name (the most likely registry name, or null) and matches (every candidate, most likely first).

Example:

```
curl -s "https://api.lookupd.com/v0.1/resolve?url=https://mcp.linear.app/mcp"
```

Output of the example:

```
{"name":"app.linear/linear","matches":["app.linear/linear"]}
```

Errors:

- 400 neither url nor package given

### GET /v0.1/version

API version, upstream registry and data attribution.

| name | type | required | meaning |
|---|---|---|---|
| (none) | | | |

Output: 200 JSON: version, upstream, attribution.

Example:

```
curl -s https://api.lookupd.com/v0.1/version
```

Output of the example:

```
{
  "version": "0.1.0",
  "upstream": "https://registry.modelcontextprotocol.io",
  "attribution": "Data from the Official MCP Registry (CC0). lookupd is an independent sub-registry and is not affiliated with the MCP project."
}
```

Errors:

- none specific to this item

### GET /v0.1/health

Whether the API and its database are up.

| name | type | required | meaning |
|---|---|---|---|
| (none) | | | |

Output: 200 JSON {status: ok, db: ok}.

Example:

```
curl -s https://api.lookupd.com/v0.1/health
```

Output of the example:

```
{
  "status": "ok",
  "db": "ok"
}
```

Errors:

- 503 JSON {status: error, db: error}: the database is unreachable

### GET /v0.1/ping

Liveness check that does not touch the database.

| name | type | required | meaning |
|---|---|---|---|
| (none) | | | |

Output: 200 JSON {status: ok}.

Example:

```
curl -s https://api.lookupd.com/v0.1/ping
```

Output of the example:

```
{
  "status": "ok"
}
```

Errors:

- none specific to this item

### POST /v1/registrations

Claim one listing. Claiming a listing is free: prove you control it and it is yours, with no expiry and nothing to pay. Answers 201 with the claim (an order of offer claim and amount 0, status pending_verification), its order_token (shown once; it is the only way back to the claim) and verify (challenge, expires_at, instructions). Prove control within 30 days or the claim closes. The first claim to prove control holds the listing.

| name | type | required | meaning |
|---|---|---|---|
| listing | JSON string | yes | Registry name, for example com.acme/server. |
| email | JSON string, email | yes | Contact for the claim: alerts and the weekly digest go there. |

Output: 201 JSON: order_token, order (offer claim, amount 0, status pending_verification), verify (challenge, expires_at, instructions per proof method).

Example:

```
curl -si -X POST https://api.lookupd.com/v1/registrations -H 'content-type: application/json' -d '{"listing":"com.acme/server","email":"dev@acme.com"}'
```

Output of the example:

```
HTTP/2 201
{"order_token":"q9Z…","order":{"id":"0b6f…","listing":"com.acme/server","offer":"claim","amount":0,"status":"pending_verification"},"receipt":null,"verify":{"challenge":"Jd2…","expires_at":"2026-12-11T00:00:00.000Z","instructions":[{"method":"dns","record":{"name":"_lookupd.acme.com","value":"lookupd-verify=Jd2…"}},{"method":"well-known","url":"https://acme.com/.well-known/lookupd.json"}]}}
```

Errors:

- 400 invalid listing or email; 404 unknown listing; 413 body over 16 KB
- 409 already claimed: another publisher holds this listing
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### GET /v1/registrations/{id}

Status of one claim, for the holder of its order token.

| name | type | required | meaning |
|---|---|---|---|
| id, Authorization | path, header | yes | order_id; Bearer <order token>. |

Output: 200 JSON: order, receipt (null for a claim), verify (while the claim can still be proven).

Example:

```
curl -s https://api.lookupd.com/v1/registrations/ORDER_ID -H 'authorization: Bearer ORDER_TOKEN'
```

Output of the example:

```
{"order":{"id":"0b6f…","offer":"claim","status":"pending_verification","amount":0},"receipt":null,"verify":{"challenge":"Jd2…","expires_at":"2026-12-11T00:00:00.000Z"}}
```

Errors:

- 404 Order not found: unknown id or wrong token

### POST /v1/registrations/{id}/verify

Prove control of the namespace as verify.instructions says: com.acme/* by DNS TXT or .well-known/lookupd.json on acme.com, io.github.<owner>/* by GitHub Actions OIDC here (audience exactly https://lookupd.com/v1/registrations/<order id>), or by GitHub sign-in in a browser (the claim page posts to /register/github/start, not to this route). OIDC is a weaker proof than sign-in: any repository writer with id-token: write in a repository of <owner> can pass it. The first claim to prove control holds the listing; the others close, as does a claim unproven after 30 days. A claim does not expire once it holds the listing.

| name | type | required | meaning |
|---|---|---|---|
| id, Authorization | path, header | yes | order_id; Bearer <order token>. |
| method | JSON enum: dns \| well-known \| github-oidc | yes | Proof to check. |
| token | JSON string | no | github-oidc: the OIDC ID token. |

Output: 200 JSON: order with status active and period_end. Verifying again replays it.

Example:

```
curl -s -X POST https://api.lookupd.com/v1/registrations/ORDER_ID/verify -H 'authorization: Bearer ORDER_TOKEN' -H 'content-type: application/json' -d '{"method":"dns"}'
```

Output of the example:

```
{"order":{"id":"0b6f…","offer":"claim","status":"active","period_end":"2999-12-31T00:00:00.000Z"}}
```

Errors:

- 400 wrong method for the namespace; 404 unknown claim or wrong token; 409 proof not found, another claim won, or the claim closed (body has order, with status)
- 429 Too Many Requests: 60 requests per minute per IP; wait and retry once

### GET /v1/registrations/{id}/analytics

An active registration's listing over the last 7, 30, 90 or 365 days, aggregates only: searches it showed in (organic, average position, featured), the same counts for the window before (previous), a daily series, top queries (queries searched by at least 3 different callers; the text is typed by searchers, so untrusted), server-page views, outcome success rate and the median of other listings with its tag, how agents used it (runtimes, domains, tools, found via lookupd or already installed, first or later in the conversation, error classes, latency, other listings used in the same conversations), and lost_queries: queries where it ranked below 3 and who held the top positions. The same numbers reach the owner by email every 7 days unless paused on the order page.

| name | type | required | meaning |
|---|---|---|---|
| id, Authorization | path, header | yes | order_id; Bearer <order token>. |
| days | enum: 7 \| 30 \| 90 \| 365 | no | The window; default 30. |

Output: 200 JSON: analytics (window_days, searches, previous, series, top_queries, page_views, outcomes, usage, lost_queries), note, lost_note.

Example:

```
curl -s 'https://api.lookupd.com/v1/registrations/ORDER_ID/analytics?days=90' -H 'authorization: Bearer ORDER_TOKEN'
```

Output of the example:

```
{
  "analytics": {
    "searches": {
      "organic": 412,
      "featured": 37,
      "average_position": 2.4
    },
    "top_queries": {
      "min_callers": 3,
      "query_text": "typed by searchers: untrusted text, shown as plain text",
      "items": [
        {
          "query": "acme invoices",
          "count": 58
        }
      ]
    },
    "page_views": 96,
    "outcomes": {
      "reports": 21,
      "success_rate": 0.905,
      "capability_tag": "billing",
      "peer_median_success_rate": 0.82
    }
  }
}
```

Errors:

- 400 days is not one of 7, 30, 90, 365
- 404 Order not found: unknown id or wrong token
- 409 Not registered: the order's registration is not active

Data from the Official MCP Registry (CC0). lookupd is an independent sub-registry and is not affiliated with the MCP project.
