ATSS Standard
Afro Tools Spec Standard — the machine-readable format powering the registry.
# specs/ — CLAUDE.md (ATSS Specification v1.0)
## What is ATSS
ATSS (Afro.tools Spec Specification) defines the structure, required fields, and rules
for every spec in this registry.
A spec is a structured, machine-readable description of one API capability.
It is not a wrapper. It is not an SDK. It is a declarative description.
> **Terminology note:** In this repo, "spec" refers to an ATSS API description
> (`schema.json` + `canonical_example.ts`).
> This is distinct from Claude Code "skills" (SKILL.md files in `plugin/skills/`),
> which are instruction files for AI agents.
---
## Folder structure
```
specs/{category}/{provider_slug}/
├── provider.json ← provider-level metadata
└── {capability}/
├── schema.json ← capability spec
└── canonical_example.ts ← working TypeScript example
```
**category** — `payment` or `sms` (extensible to other categories)
**provider_slug** — lowercase, no spaces, no dashes (e.g. `paycard`, `wave`, `nimbasms`)
**capability** — snake_case verb (e.g. `create_payment`, `verify_payment`, `send_otp`)
Every capability folder must contain exactly these two files. Nothing else.
---
## provider.json — required fields
One `provider.json` per provider directory. Carries provider-level metadata migrated from `schema.json`.
```json
{
"slug": "paycard",
"name": "Paycard",
"category": "payment",
"country_code": ["GN"],
"description": "Paycard is a Guinean mobile money payment gateway that lets you accept payments via MTN, Orange, and Cellcom mobile wallets.",
"example_prompt": "Integrate Paycard to accept mobile money payments in Guinea. Create a payment, extract the operation_reference, and verify the transaction server-side before fulfilling the order."
}
```
| Field | Type | Rule |
|---|---|---|
| slug | string | Lowercase, no spaces, no dashes — matches the directory name |
| name | string | Display name of the provider |
| category | string | `"payment"` or `"sms"` |
| country_code | string[] | ISO 3166-1 alpha-2 codes — non-empty array |
| description | string | One-sentence description of the provider for display and MCP context |
| example_prompt | string | Full-flow usage prompt for AI agents — describes a complete integration scenario |
---
## schema.json — required fields
```json
{
"spec_version": "1.0",
"provider_api_version": "2024-01-01",
"capability": "create_payment",
"capability_type": "synchronous",
"status": "ready",
"currency": ["GNF"],
"sandbox": false,
"docs_url": "https://paycard.com/docs/create-payment",
"docs_public": false,
"auth": {
"type": "api_key",
"location": "body",
"field": "c",
"format": "{token}",
"env_var": "PAYCARD_API_KEY"
},
"endpoint": {
"method": "POST",
"url": "https://mapaycard.com/epay/create/"
},
"example_prompt": "Initiate a Paycard mobile money payment for a given amount in GNF, extract the operation_reference from the response, and store it immediately before redirecting the user.",
"input_schema": {},
"response_schema": {},
"error_schema": {},
"gotchas": [
"Always verify payment status server-side before fulfilling an order."
]
}
```
### Field rules
| Field | Type | Rule |
|---|---|---|
| spec_version | string | Always "1.0" |
| provider_api_version | string | YYYY-MM-DD if provider has no version string |
| capability | string | snake_case — matches the directory name |
| capability_type | enum | `synchronous`, `asynchronous`, `webhook`, or `sdk` |
| status | enum | `unverified`, `draft`, `ready`, `verified`, `deprecated`, `archived` |
| currency | string[] | ISO 4217 codes |
| sandbox | boolean | true if provider has a sandbox environment |
| docs_url | string | Can be `""` — provider has no public docs URL |
| docs_public | boolean | false if provider does not publish API docs publicly |
| auth | object | Auth descriptor — see auth variants below |
| endpoint | object | `{ method, url }` |
| example_prompt | string | Required for `ready`/`verified` — describes this specific capability usage for AI agents |
| input_schema | object | JSON Schema describing the request body |
| response_schema | object | JSON Schema describing the success response |
| error_schema | object | JSON Schema describing the error response |
| gotchas | string[] | MANDATORY — minimum 1 entry, no exceptions |
**Fields NOT in schema.json** (migrated to provider.json): `provider_slug`, `provider_name`, `category`, `country_code`.
### `evidence` — required only when `status` is `"unverified"`
```jsonc
"evidence": {
"source_url": "https://provider.com/docs/create-payment",
"source_reference": "Section 'Create a payment'",
"extracted_at": "2026-09-15T10:00:00Z",
"snapshot_hash": "a1b2c3…",
"spans": ["POST /v1/payments", "Authorization: Bearer {api_key}"]
}
```
Every entry in `spans[]` must appear literally (whitespace/case-normalized) in the source document at `source_url`. This is what lets a reviewer verify an `unverified` spec wasn't hallucinated without re-reading the provider's docs from scratch.
### capability_type definitions
- `synchronous` — request/response, result available immediately in the HTTP response
- `asynchronous` — provider processes in background, poll a separate endpoint for result
- `webhook` — provider sends an HTTP POST to your endpoint when an event occurs
- `sdk` — capability delivered through a client-side SDK script (no direct HTTP endpoint). `endpoint.url` holds the CDN URL of the SDK script; `endpoint.method` is `null`. The `canonical_example.ts` exports a server-side configuration helper; the actual SDK invocation is shown in the usage comment block. Auth credential goes in `auth.location: "sdk_param"` (the SDK's config object).
---
## canonical_example.ts — rules
### Mandatory rules
1. TypeScript strict — must compile with `tsc --noEmit`, zero errors
2. Native fetch only — no axios, no node-fetch, no external HTTP libraries
3. All credentials via `process.env` — checked at the top, throw if missing
4. Explicit TypeScript interfaces for input, response, and error types
5. Export the main function
6. Include a usage example in a comment block at the bottom
7. JSDoc header: provider, capability, ATSS version, capability_type
### Template
```typescript
/**
* @provider Paycard
* @capability create_payment
* @atss 1.0
* @capability_type synchronous
*/
const PAYCARD_API_KEY = process.env.PAYCARD_API_KEY;
if (!PAYCARD_API_KEY) throw new Error("Missing env: PAYCARD_API_KEY");
interface CreatePaymentInput {
amount: number;
currency: string;
reference: string;
callback_url: string;
}
interface CreatePaymentResponse {
id: string;
payment_url: string;
status: string;
}
interface PaycardError {
code: string;
message: string;
}
export async function createPayment(
input: CreatePaymentInput
): Promise<CreatePaymentResponse> {
const response = await fetch("https://mapaycard.com/epay/create/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ...input, c: PAYCARD_API_KEY }),
});
if (!response.ok) {
const error: PaycardError = await response.json();
throw new Error(`Paycard error ${response.status}: ${error.message}`);
}
return response.json() as Promise<CreatePaymentResponse>;
}
/*
Usage example:
const payment = await createPayment({
amount: 100000,
currency: "GNF",
reference: "order_123",
callback_url: "https://myapp.com/callback",
});
// Store payment.operation_reference immediately
// Always verify payment status server-side before fulfilling the order
*/
```
### What must never appear in canonical_example.ts
- Any npm import (`axios`, `node-fetch`, etc.) — native fetch only
- `require()` — ES modules only
- Hardcoded API keys or secrets
- `any` type without a comment explaining why
---
## Status lifecycle
```
unverified → draft → ready → verified → deprecated → archived
```
### unverified — criteria
- [ ] `schema.json` passes `npm run validate` (structure + evidence block)
- [ ] `evidence` block present with `source_url`, `source_reference`, `extracted_at`, `snapshot_hash`, and at least one grounded `spans[]` entry
- [ ] Generated automatically by the discovery pipeline (`core/tools/discovery`) — contributors do not hand-write `unverified` specs
- [ ] Not yet human-reviewed for correctness beyond the automated gates — a maintainer reviewing the PR promotes it to `draft` (needs more work) or `ready` (looks correct) by editing `status` and removing or keeping `evidence` as documentation
### ready — criteria
- [ ] `schema.json` passes `npm run validate`
- [ ] `canonical_example.ts` compiles with `tsc --noEmit`
- [ ] `gotchas[]` has at least 1 specific, actionable entry
- [ ] `example_prompt` is non-empty
- [ ] `provider.json` exists in the parent provider directory
- [ ] `status` field set to `ready`
A `ready` spec is visible in the MCP server.
### verified — criteria
- [ ] All `ready` criteria met
- [ ] **One of:**
- Working example exists in `afrotools/examples`, **or**
- The provider has a sandbox (`provider.json.sandbox: true`) and `npm run test:live -- --provider <slug>` passes (exit 0) against it — link the GitHub Actions run in the PR/commit as evidence
- [ ] Maintainer has set status to `verified`
`test:live` only ever targets a sandbox — it refuses to run against a provider with no sandbox (real production traffic, no automated safety net). Providers without a sandbox are verified through `afrotools/examples` only; see `CONTRIBUTING.md` § Live verification.
A `verified` spec contributes to the provider's "AI Ready" badge.
### AI Ready badge rule
A provider is AI Ready only when ALL its capabilities are `verified`.
---
## Gotchas — writing guide
**Good:**
```
"Always verify payment status server-side before fulfilling an order.
The callback URL alone is not sufficient — it can be forged."
```
**Bad:**
```
"Read the docs carefully."
```
Gotchas must be: specific, actionable, based on real integration experience.
---
## Provider index
See the auto-generated table in [README.md](./README.md) — it is regenerated by CI
(`scripts/generate-readme.js`) on every merge to `main` and is never out of sync.
This file used to keep a second, hand-maintained copy of that table; it drifted
from reality within weeks and is not worth re-introducing here.