Metadata-Version: 2.4
Name: agentic-ard-kit
Version: 0.10.0
Summary: Self-hosted Agentic Resource Discovery over local scripts, agent skills and MCP servers.
Author: ameobius
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/ameobius-ai/ard-kit
Project-URL: Source, https://gitlab.com/ameobius-ai/ard-kit
Project-URL: Issues, https://gitlab.com/ameobius-ai/ard-kit/-/issues
Keywords: ard,agentic-resource-discovery,mcp,agent-skills,registry,discovery
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# ard-kit

Self-hosted [Agentic Resource Discovery (ARD)](https://agenticresourcediscovery.org/)
for local and private resources: catalog your own scripts/tools into an
`ai-catalog.json`, then serve it through the two surfaces the spec defines —
a static manifest at `/.well-known/ard.json` and a dynamic
`POST /search` endpoint. `python3 selfcheck.py` asserts the pipeline
end-to-end, and `.gitlab-ci.yml` runs it plus the official conformance CLI
(`ards-project/ard-spec`) on every push where runner quota allows.

ARD is the discovery layer that sits in front of MCP (tools), Skills
(instructions), and A2A (agents). It answers one question: *"what capability
can help with this task?"* — invocation stays with the resource's own
mechanism.

## Why this exists

The pieces started life as a private in-house integration — a catalogizer
over a large pile of local scripts and a registry serving them for
intent-based lookup. When the ARD specification was announced
(HF blog: [Agentic Resource Discovery: Let agents search](https://huggingface.co/blog/agentic-resource-discovery-launch)),
the salvageable pieces were pulled out, generalized, and aligned to the
spec. The original registry server code was lost in a workspace cleanup;
`registry.py` here is a faithful rebuild against the public shape.

## Pieces

| File | Role |
|------|------|
| `catalogize.py` | Scans directories for `.py`/`.sh` scripts (AST docstrings, argparse flags, shell header comments), `SKILL.md` agent skills (YAML frontmatter → `text/markdown; profile="urn:air:agent-skills"` entries) and `.mcp.json` MCP client configs (→ `application/mcp-server-card+json` per server; env values never cataloged), emits ARD entries into `ai-catalog.json` plus an `ai-catalog.inspect.json` invoke-schema sidecar. Stdlib only. |
| `registry.py` | Minimal HTTP registry: serves the manifest and `POST /search` (token-overlap ranking), `POST /explore` (501 opt-out), optional `--upstreams` federation fan-out, `GET /inspect` over the sidecar, optional `--token` bearer auth. Stdlib only. |
| `mcp_server.py` | MCP stdio front over the same catalog: `ard_search` (ranked summaries) and `ard_inspect` (invoke command + CLI arguments) as MCP tools, so an editor mounts a command instead of being handed a URL. Stdlib only. |
| `selfcheck.py` | End-to-end pipeline check: catalogize a temp dir, run search, assert ranking. |
| `systemd/ard-registry.service` | Unit template for running the registry as a user service. |

No dependencies beyond Python 3.10+ stdlib.

## Install

```bash
pipx install agentic-ard-kit  # isolated venv, three commands on PATH
pipx install .         # same, from a checkout
```

This installs `ard-catalogize`, `ard-registry` and `ard-mcp` — the same
entry points as `python3 catalogize.py`, `python3 registry.py` and
`python3 mcp_server.py`. Running straight from a clone stays supported and
needs no install at all.

## Qoder plugin

ard-kit also ships as a self-contained [Qoder](https://qoder.com) plugin —
the same code plus an agent-facing wrapper:

| Component | What it gives the agent |
|-----------|-------------------------|
| `skills/ard-registry` | The discover → inspect → run → discard contract (env-driven endpoint: `ARD_REGISTRY_URL`, optional `ARD_REGISTRY_TOKEN`). |
| `/ard-catalogize` | Slash command: index a script directory into `ai-catalog.json` and re-verify live. |
| `/ard-serve` | Slash command: serve a catalog, confirm via `/health`. |
| `bin/ard-registry`, `bin/ard-catalogize` | Entry points added to PATH (stdlib-only, no install step). |

```bash
qoder plugins install ./ard-kit   # then /plugins reload
qoder plugins validate ./ard-kit  # manifest + component check
```

## Quickstart

```bash
# 1. Catalog your scripts and agent skills (default scan dir: ./scripts)
python3 catalogize.py --dir scripts --dir .agents/skills --host myhost.example.com

# 2. Serve it
python3 registry.py --catalog ai-catalog.json --port 8390

# 3. Search (the ARD registry API shape)
curl -s http://127.0.0.1:8390/search \
  -H 'Content-Type: application/json' \
  -d '{"query": {"text": "scan subdomains"}, "pageSize": 5}'

# 4. Inspect before running (invoke command + CLI arguments)
curl -s 'http://127.0.0.1:8390/inspect?identifier=urn:air:myhost.example.com:script:scripts:scan_subdomains'

# 5. Static manifest (for crawlers / federation)
curl -s http://127.0.0.1:8390/.well-known/ard.json
```

The discover → inspect → run flow mirrors commercial directories like
monid.ai, minus the marketplace: `POST /search` finds the capability,
`GET /inspect?identifier=<urn>` returns its invoke command and parsed
argparse flags (404 for unknown identifiers, 501 when the sidecar is
absent), and the run itself stays with your shell.

**For clients:** the registry is stateless and nothing attaches to your
session. `POST /search` returns ranked summaries only; pull a full invoke
schema via `/inspect` for the single entry you actually run. Discard both
afterwards — keep the URN if you might reuse the tool, not the payload.
Discovery costs a query, not a mounting: there is nothing to unload because
nothing was loaded.

Require a bearer token on every endpoint except `/health` when the registry
leaves loopback. For service deployments prefer the `ARD_REGISTRY_TOKEN`
environment variable over `--token` — the command line is world-readable:

```bash
python3 registry.py --catalog ai-catalog.json --port 8390 --token "$(openssl rand -hex 16)"
# or: ARD_REGISTRY_TOKEN=<token> python3 registry.py --catalog ai-catalog.json --port 8390
```

A token that is set but empty is rejected at startup (fail closed).

Federate with peer registries — with `"federation": "auto"` (default) queries
fan out to `--upstreams` and results merge (every result names its `source`
registry and carries a clamped score); `"federation": "referrals"` returns the peers in
a `referrals` array instead; `"federation": "none"` stays local:

```bash
python3 registry.py --catalog ai-catalog.json --port 8390 \
  --upstreams https://peer.example.com --public-url https://me.example.com
```

Self-check the whole pipeline:

```bash
python3 selfcheck.py
```

## MCP front

The registry is HTTP, which means something has to hand the agent a URL
first. Editors that speak MCP mount a command instead, so the same catalog
is also served over stdio — no server to start, no port, no token:

```json
{
  "mcpServers": {
    "ard": {
      "command": "python3",
      "args": ["/path/to/ard-kit/mcp_server.py",
               "--catalog", "/path/to/ai-catalog.json"]
    }
  }
}
```

Installed through pipx, the same front is `"command": "ard-mcp"` with no
path to keep in sync.

Two tools, the same discover → inspect → run contract:

| Tool | Returns |
|------|---------|
| `ard_search` | Ranked summaries (`identifier`, `displayName`, `description`, `type`, `score`) for an intent, optionally filtered by media type. |
| `ard_inspect` | The invoke command and parsed CLI arguments for one `urn:air:` identifier. |

Search deliberately returns summaries only: the invoke schema arrives once,
for the single entry the agent actually runs. The catalog is re-read per
call, so re-running `catalogize.py` is picked up without a restart, and the
host owns the process lifetime — the server exits when stdin closes.

## Entry shape

Each catalog entry carries the fields ARD consumers key on:

```json
{
  "@context": "https://agenticresourcediscovery.org/context/v1",
  "identifier": "urn:air:myhost.example.com:script:scripts:scan_subdomains",
  "displayName": "scan_subdomains",
  "description": "Enumerate subdomains via passive sources",
  "type": "application/vnd.ard-kit.script+json",
  "url": "file:///path/to/scripts/scan_subdomains.py",
  "tags": ["scan", "subdomains", "recon"],
  "aliases": ["scan_subdomains"],
  "representativeQueries": [
    "Enumerate subdomains via passive sources",
    "scan", "subdomains", "recon"
  ],
  "metadata": { "invoke": "python3 scripts/scan_subdomains.py", ... }
}
```

`representativeQueries` lead with the natural description phrase and pad
with keywords, so both sentence-style and token-style agent queries hit.
Every entry carries the field: when a resource offers no keywords, the file
or skill name is split to pad the list, which is always 2–5 phrases.
Entries keep exactly one of `url`/`data` (spec §3.4) and scalar-only
`metadata` values, and the manifest envelope carries only
`specVersion`/`host`/`entries` — the shape the official
`ai-catalog.schema.json` validates.

The `type` media type is free-form per the spec, but entries use a standard
name wherever one exists — `application/mcp-server-card+json` for MCP servers,
`text/markdown; profile="urn:air:agent-skills"` for agent skills — so any
conformant registry routes them without a local mapping. Plain local scripts
have no standard type yet and keep the vendor one.
Identifiers follow the spec's Appendix C form
(`urn:air:<publisher>:<namespace>:<name>`), and the served manifest prepends
a self-advert entry of type `application/ai-registry+json` so peers can
discover this registry's search base URL. Search results carry a `score` in
the spec's 0–100 relevance band (relevance only — ARD decouples trust into
the trust manifest, §5).

## Prior art / positioning

- [HF Discover](https://github.com/huggingface/hf-discover) — reference
  implementation over the Hugging Face Hub; federated, semantic search.
- [ARD spec](https://github.com/ards-project/ard-spec) — the standard itself
  (Apache-2.0).
- [monid.ai](https://monid.ai/docs/guide/quickstart-skill) — commercial take
  on the same pattern: a CLI/Skill teaching an agent to `discover → inspect →
  run` endpoints by intent. The difference: monid is a paid marketplace of
  third-party data endpoints behind an API key; ard-kit is the self-hosted,
  no-network, no-account version for resources you already own.

Known limitation: search here is lexical token overlap, not semantic ranking.
That is deliberate — zero dependencies, and good enough for a few thousand
private entries. The upgrade path is embeddings behind the same endpoint.

## License

MIT.

## Acknowledgements

Developed and hardened in [Qoder](https://qoder.com) — from the ARD v0.91
conformance rebuild through the ultra-review cycle.
