Metadata-Version: 2.4
Name: sm-listing
Version: 0.7.0
Summary: An open profile for member listings — how a community publishes the members who opted in to being discoverable, so an agent can find them by what they offer and where they are.
Project-URL: Homepage, https://github.com/Sharathvc23/sm-listing
Project-URL: Source, https://github.com/Sharathvc23/sm-listing
Author: Sharath VC
License-Expression: MIT
License-File: LICENSE
Keywords: a2a,ai-agents,directory,discovery,internet-of-agents,nanda
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: conformance
Requires-Dist: jsonschema[format-nongpl]>=4.21; extra == 'conformance'
Requires-Dist: pytest>=8.0; extra == 'conformance'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: jsonschema[format-nongpl]>=4.21; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff==0.15.13; extra == 'dev'
Description-Content-Type: text/markdown

# sm-listing

**How a community publishes the members who asked to be findable** — so an agent
that has never met them can discover them by *what they offer* and *where they
are*.

This is the second hop of agent discovery.
[`sm-federation`](https://github.com/Sharathvc23/sm-federation) finds the
**community**; this profile finds the **member**. They are separate because
federation's own §1 says it is *not* a directory, and a member lookup service is
exactly that.

## A listing is not a member directory

A **directory** is the roster: everyone, including people who never asked to be
listed anywhere. A **listing** contains only members who *actively opted in*,
carrying only the fields they chose. Its default state is empty, and a community
that enables it with nobody opted in publishes an empty document — which is
correct, not a misconfiguration.

That distinction is the whole security model, so the spec states it normatively:

- **A non-consenting member is ABSENT** — not an opaque entry, not a redacted
  placeholder. An opaque entry in a list is a census of the non-consenting: it
  publishes the one fact those members declined to publish, which is that they
  are there.
- **No count of excluded members**, or anything one can be derived from. A count
  computed and then withheld is one refactor away from being emitted; excluding
  rows so that no count exists *by construction* is the mechanism.
- **The contact route is the member's agent endpoint**, never a human contact
  detail. A consented listing carrying phone numbers is still a harvesting
  surface — one people opted into without understanding what they opted into.
- **No member-authored free text.** Every field is a code, an identifier, a URL
  or a number. Free text is what leaks, and prose does not match across
  languages, so it was never the discovery mechanism.

## What it carries

| | |
| --- | --- |
| geography | ISO 3166-2 for matching, plus resolvable place references for the precision ISO cannot express |
| offering | `(scheme, code)` pairs — **multi-encodable**, so one member can publish NAICS *and* UNSPSC *and* a schema.org type |
| trade terms | ships-to, currencies, minimum order, lead time — all on free, stable standards |

## What it does not promise

Matching is **exact** and **hierarchical-prefix within one scheme**. Cross-
vocabulary matching is out of scope and stays out: a consumer querying JSIC will
not match a publisher who listed only NAICS. Crosswalk tables are lossy,
permanently maintained, and produce confidently wrong matches when stale — worse
than no match. §6's multi-encoding is the mitigation, and it works by moving the
translation burden onto the only participant who cannot get it wrong: the member
describing itself.

## Layout

| | |
| --- | --- |
| `spec/listing/0.1/listing.md` | the normative profile |
| `src/sm_listing/wire/schema/listing/0.1/*.json` | JSON Schemas — the published wire contract, **shipped inside the wheel** (`sm_listing.wire.load_schema`) |
| `src/sm_listing/wire/vectors/listing/v01/*.json` | conformance vectors, also shipped (`sm_listing.wire.load_vectors`) |
| `src/sm_listing/conformance/` | the over-the-wire suite, **shipped inside the wheel** — what an implementer runs against their own endpoint |
| `conformance/` | the schema and corpus-coverage suite for this repository; **not** in the wheel |

## Measuring your implementation

```bash
pip install "sm-listing[conformance]"
SM_LISTING_ADAPTER=your.module:make_adapter pytest --pyargs sm_listing.conformance
```

`make_adapter` returns an implementation of `sm_listing.conformance.ListingAdapter`.
Every run prints the subject it measured. **With `SM_LISTING_ADAPTER` unset the
suite fails** rather than measuring anything — it does not fall back to the
reference implementation, which is what 0.3.0 and earlier did while reporting the
result as though it were about your system. Use `SM_LISTING_ADAPTER=reference` to
exercise the suite against the reference on purpose.

MIT.
