# What 300 MCP services actually declare

Counts over `smithery-use-300-2026-08-02`, derived from [`derived/structural.json`](derived/structural.json) by `scripts/research/derive-structural.js`, from the observation snapshot at `2026-08-03T02:23:59.604Z`.

This is the evidence that the problem Class Profiles addresses is real. It does not establish that Class Profiles is the right answer.

## How to read this

Every number below is a count over captured declarations. None of it observes runtime behavior — no sampled service was installed, connected, or invoked (`DATA-NOTICE.md`, "Safe observation").

**Name signals are lexical.** A name containing `cursor` does not establish that the value is opaque; a name containing `dry_run` does not verify that effects are suppressed. Each signal's interpretation limit is declared in `derived/structural.json` under `signal_definitions` and applies to every use below.

**This cohort only.** Smithery `useCount` is a directory-specific ranking signal. These are not prevalence, adoption, install, or quality estimates.

**Member counts are concentrated.** One listing contributes 2,530 members — 26.9% of all 9,390. The top 5 contribute 41.1%, the top 10 contribute 49.2%. The median listing has 8 members and 111 of 300 have five or fewer. **Any member-denominated figure below is dominated by a handful of large surfaces**, and should be read alongside its listing-denominated companion where one exists. The cohort's family-weighted sensitivity view addresses duplicate and forked *listings*; it does not address one listing being large. Listing-incidence figures are unaffected.

---

## F1 — No captured member declares a tool annotation

| Measure | Value |
|---|---|
| Members with a retained `annotations` object | **0** |
| Member denominator | 9,390 |

Not one `readOnlyHint`, `destructiveHint`, `idempotentHint`, or `openWorldHint` across 300 of the most-used listings in this directory.

**Why this is unlikely to be a capture artifact.** The collector retains optional declaration keys rather than normalizing them away: 1,902 members carry `outputSchema`, and retained declaration key-sets vary (`{inputSchema, name}`, `{inputSchema, name, outputSchema}`, `{arguments, name}`, `{mimeType, name, uri}`). A wholesale strip would have removed those too.

**What would still falsify it.** The capture is a directory-indexed surface, not a direct protocol enumeration of each server. It remains possible that this particular index drops `annotations` specifically. Confirming against direct `tools/list` enumeration would require a separate approved protocol; under the current one, no service is connected. **Treat F1 as a strong indication about the indexed surface, not as proof about live servers.**

### Why it matters

MCP defines the annotation vocabulary, and revision `2026-07-28` additionally requires clients to treat annotations as **untrusted** unless they come from a trusted server ([`research/mcp-spec/2026-07-28-capture.md`](../mcp-spec/2026-07-28-capture.md) E5).

The declarative affordance that exists is, in this cohort, both **unused** and **untrusted by specification**. That is an argument about which direction a fix should take: adding further optional declarative fields to the wire has an observed uptake here of zero, whereas making the existing ones checkable addresses the trust half directly. It is not an argument that any particular contract is correct.

It also bears on a design rule. `spec/05 §4` makes absent annotations pessimistic — mutating, destructive, non-idempotent, open-world. Against this cohort that default classifies effectively the entire captured surface as maximally risky. The rule is right; the **migration cost of applying it strictly is the whole ecosystem**, and any consumer-side policy built on it needs a graduated posture rather than a hard gate.

## F2 — One primitive, 894 spellings

| Signal | Listings (of 300) | Distinct matched names |
|---|---|---|
| `stable_reference` — identifier / reference / handle / URI-like tokens | **164** | **894** |
| `pagination` — page / cursor / offset / limit-like tokens | **161** | 150 |
| `cursor` — cursor or continuation-like tokens | 26 | 16 |
| `batch` | 31 | 63 |
| `confirmation` | 11 | 23 |
| `dry_run` | 9 | 2 |
| `preview` | 8 | 17 |
| `safety` | 5 | 6 |

Candidate name denominator: 57,385 inspected member names, schema property names, and annotation names.

**F2a — the reference.** More than half the cohort exposes something reference-shaped, under 894 distinct spellings. This is the vocabulary-fragmentation problem measured rather than asserted. It says nothing about whether those references are stable, opaque, or comparable — only that a recurring primitive is spelled many ways.

**F2b — pagination without cursors.** 161 listings show pagination-like naming; only 26 show cursor-like naming. Most paginate by page, offset, or limit. `spec/05 §6` mandates opaque cursors and prohibits offsets and page numbers, and makes cursor *absence* a proof that the returned view is complete.

That completeness guarantee is the part that does not survive projection cheaply. Over an offset-paginated upstream without a stable ordering guarantee, a capsule can synthesize a cursor but cannot honestly promise what its absence means. The coverage dispositions in `spec/04 §3` classify upstream *tools*; a kernel-wide guarantee has no disposition vocabulary, so this loss currently has nowhere honest to be declared. Recorded as a specification gap.

**F2c — preview, dry-run, and confirmation are rare.** 8, 9, and 11 listings respectively. `spec/05 §10` places preview and undo outside the kernel. The measurement supports that exclusion: these are not common shapes a floor can assume.

## F3 — Declared surfaces are large, and mostly optional

| Measure | Value | Per listing (mean) |
|---|---|---|
| Retained member declarations | 3,249,396 UTF-8 bytes | ~10.8 KB |
| Captured description text | 2,027,533 UTF-16 code units | ~6.8 K units |
| Members | 9,390 | 31.3 |
| Largest single member description | 6,637 UTF-16 code units | — |

Input schemas (8,881 captured):

| Measure | Value |
|---|---|
| Object properties | 29,709 |
| Required properties | 1,082 |
| Optional properties | 28,627 (**96.4%**) |
| `enum` definitions / values | 2,318 / 10,152 |
| Maximum nesting depth | 10 |
| Nodes without a declared `type` | 1,240 |

**F3a — optionality is already the norm.** `spec/02 §4` prefers optional arguments, requiring one only when no safe default exists. At 96.4% optional, the cohort already works this way; the profile rule codifies existing practice rather than asking for a change.

**F3b — surface size is the measurable agent cost.** `spec/02 §9` requires three separately reported byte counts and forbids combining them. These figures are the ecosystem baseline those counts are measured against. Token conversion is deliberately not attempted here — it is tokenizer-dependent and would be an interpretation, not a measurement.

Note that MCP `2026-07-28` added `ttlMs`, `cacheScope`, and a deterministic-ordering recommendation for `tools/list`. Declared surfaces are now cacheable, so this cost is no longer necessarily paid per turn. **The size argument for profiles weakens accordingly; the semantic-identity argument does not.**

## F4 — Composition keywords the profile surface prohibits are already in use

| Keyword | Input schemas | Output schemas |
|---|---|---|
| `anyOf` | 1,074 | 2,855 |
| `$ref` | 31 | 134 |
| `oneOf` | 13 | 2 |
| `allOf` | 3 | 0 |
| Maximum depth | 10 | 13 |

`spec/02 §4` prohibits all of these on the advertised profile surface — deliberately stricter than MCP, which loosened `inputSchema` and `outputSchema` to permit any JSON Schema 2020-12 keyword in this revision.

The measurement cuts both ways and should be reported as such. It shows the constraint is **not free**: a projection over these upstreams must flatten composition, and flattening is lossy. It also shows the constraint is **not exotic**: composition appears in a small minority of input schemas.

As with F2b, schema-level loss has no coverage disposition in `spec/04 §3`. Second instance of the same gap.

## F5 — `outputSchema` is published by about a fifth of tools

1,902 of 8,881 tools (**21.4%**) declare an `outputSchema`. `spec/02 §4` requires a profile surface to omit it, and `spec/03 §1` fixture-checks the absence.

This is recorded because it bears on an open decision ([`decisions/0001` D8](../../decisions/0001-mcp-2026-07-28-alignment.md)): projecting such a server *removes* a declaration its publisher chose to make, and the client most affected is the zero-knowledge one that cannot consult the private contract. MCP encourages `outputSchema` in this revision.

Also worth noting for anyone building on captured schemas: 125 output schemas name a `required` property that the schema does not define, and 3,363 output-schema nodes declare no `type`. Declared schemas in this cohort are not uniformly self-consistent.

## F6 — Two thirds of the declared surface is retrieval

5,313 tools carry a recognizable action token in their name. The distribution:

| Family | Share | Leading tokens |
|---|---|---|
| **Retrieval** | **68.0%** | get 1,898 · search 735 · list 634 · lookup 105 · find 76 · fetch 57 · query 56 · read 51 |
| Record write | 12.3% | create 212 · delete 118 · update 108 · add 74 · set 43 · post 36 · send 36 · remove 25 |
| Compute / transform | 6.4% | generate 86 · compare 52 · compute 49 · calculate 45 · convert 41 · analyze 25 · extract 23 · parse 19 |

Tool **names** and **input-schema property names** only. Descriptions are `rights-withheld` for 9,384 of 9,390 members, so no prose was read. Names were split on camelCase, snake_case, dots, and hyphens, then matched against hand-written token sets — a different analyst choosing different tokens gets different families.

## F7 — The dialect problem is sharpest on the commonest capability

183 of 300 listings expose at least one retrieval-shaped tool. Their argument vocabulary:

| Concept | Spellings observed | Occurrences |
|---|---|---|
| **the query** | `query` 539 · `q` 53 · `search` 34 · `keyword` 21 | ~647 |
| **how many** | `limit` 340 · `max_results` 50 · `size` 33 · `page_size` 32 · `count` 20 · `per_page` 15 | ~490 |
| **where from** | `page` 124 · `offset` 52 · `skip` 17 | ~193 |

**Three concepts, eleven spellings.** This is a sharper instance than F2's 894 reference spellings, because here the *same three arguments* recur with the *same meanings* under different names. If any capability class is cheap to agree on, it is this one: a query, a bound, a position, and an ordered result carrying references.

The caveat has to travel with it: **shared arguments are not shared substitutability.** Web search, code search, and vector search can take identical arguments over completely disjoint target spaces. A shared contract buys vocabulary, not interchangeability.

## F8 — Names do not carry function

Assigning each listing its dominant action signature (≥30% of its tools, minimum 4 tools):

| Result | Listings |
|---|---|
| **No dominant signature** | **133** |
| Surface too small to judge (<4 tools) | 50 |
| retrieval | 28 |
| record | 12 |
| messaging · finance | 6 each |
| compute | 3 |
| filestore · calendar · code · media | 2 each |
| sql | 1 |

Under a name-token method, **61% of judgeable listings have no dominant functional shape.**

The obvious reading is that a capability map will not fall out of surface analysis. The sharper reading is that this measures the gap rather than the method: *names do not carry intent, which is the premise of the whole proposal* ([`decisions/0004`](../../decisions/0004-capability-is-a-job.md)). If names carried function, no semantic contract would be needed — an agent would read them and know. This analysis failed to recover function for the same reason an agent struggles with the same surfaces.

A capability map is therefore **declared by authors**, not derived from tool names.

### The frame cannot see every class

Searching this corpus for *browser* vocabulary — `click`, `browser`, `page`, `element`, `selector`, `dom`, `locator` — returns 2 of 300. Searching for the operator **job shape** — a service that both perceives and actuates over the same addressed handle — returns **31**. A 15× swing from what was looked for, not from what is there, and both numbers are unreliable: `get`/`set`/`send` produce false positives, and only 2 of the 31 carry a settle verb.

Separately, the sampling frame is *servers published to a directory*. Browser and computer control usually ships as a host feature or an extension, so a near-zero count for that shape is **evidence about the frame, not about the ecosystem** ([`README.md`](README.md)).

## What none of this establishes

- That any service behaves as its declaration suggests. Nothing here is behavioral evidence; no sampled service was installed, connected, or invoked.
- That these proportions hold outside this cohort.
- That Class Profiles is the right answer to anything measured here. F1 and F2 describe a gap; they do not select a fix.

## Reproduction

```powershell
node scripts/research/derive-structural.js
node scripts/check-research.js
```

Concentration figures in "How to read this", and the token families in F6–F8, are computed from `observations/members.jsonl` rather than emitted into `derived/structural.json`. Moving them into the derivation would let every member-denominated figure carry its dominance caveat mechanically.
