GET /.well-known/agent-skills/index.json
Parametric chaos for the Agent Skills Discovery v0.2.0 index. Four modes covering digest mismatch, missing schema reference, dead skill URLs, and stale schema-version declarations. The spec is at v0.2.0 (cloudflare/agent-skills-discovery-rfc) — less volatile than MCP but still pre-1.0; modes target the stable core shape.
curl -i '{url}'
# The canonical agent-skills consumer flow: fetch index, walk skills,
# fetch each, verify sha256. Most modes break a different step.
import urllib.request, json, hashlib
index = json.loads(urllib.request.urlopen("{url}").read())
print("$schema:", index.get("$schema"))
for skill in index.get("skills", []):
print(f"\n name: {skill['name']}")
try:
body = urllib.request.urlopen(skill["url"]).read()
actual = hashlib.sha256(body).hexdigest()
ok = actual == skill["sha256"]
print(f" url: {skill['url']}")
print(f" digest: declared={skill['sha256']}")
print(f" actual ={actual} {'OK' if ok else 'MISMATCH'}")
except Exception as e:
print(f" url: {skill['url']} -> {type(e).__name__}: {e}")
const res = await fetch("{url}");
const index = await res.json();
console.log("Mode:", res.headers.get("X-Chaos-Skills-Mode"));
console.log("$schema:", index.$schema);
for (const skill of index.skills || []) {
try {
const r = await fetch(skill.url);
const bytes = new Uint8Array(await r.arrayBuffer());
const digest = await crypto.subtle.digest("SHA-256", bytes);
const actual = [...new Uint8Array(digest)].map(b => b.toString(16).padStart(2, "0")).join("");
console.log(`${skill.name}: ${actual === skill.sha256 ? "OK" : "MISMATCH"}`);
} catch (e) {
console.log(`${skill.name}: fetch failed - ${e.message}`);
}
}
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
)
type Skill struct {
Name, URL, SHA256 string
}
type Index struct {
Schema string `json:"$schema"`
Skills []Skill `json:"skills"`
}
func main() {
resp, _ := http.Get("{url}")
defer resp.Body.Close()
var idx Index
json.NewDecoder(resp.Body).Decode(&idx)
fmt.Println("$schema:", idx.Schema)
for _, s := range idx.Skills {
r, err := http.Get(s.URL)
if err != nil { fmt.Printf("%s: fetch failed - %v\n", s.Name, err); continue }
body, _ := io.ReadAll(r.Body); r.Body.Close()
sum := sha256.Sum256(body)
actual := hex.EncodeToString(sum[:])
status := "OK"
if actual != s.SHA256 { status = "MISMATCH" }
fmt.Printf("%s: %s\n", s.Name, status)
}
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// sha2 = "0.10"
use sha2::{Digest, Sha256};
fn main() -> Result<(), Box> {
let index: serde_json::Value = reqwest::blocking::get("{url}")?.json()?;
for skill in index["skills"].as_array().unwrap_or(&vec![]) {
let url = skill["url"].as_str().unwrap();
let declared = skill["sha256"].as_str().unwrap();
let body = reqwest::blocking::get(url)?.bytes()?;
let actual = format!("{:x}", Sha256::digest(&body));
println!("{}: {}", skill["name"], if actual == declared { "OK" } else { "MISMATCH" });
}
Ok(())
}
import com.fasterxml.jackson.databind.*;
import java.net.URI;
import java.net.http.*;
import java.security.MessageDigest;
public class AgentSkillsIndex {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("{url}")).build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
var idx = new ObjectMapper().readTree(resp.body());
for (var skill : idx.path("skills")) {
var body = client.send(
HttpRequest.newBuilder(URI.create(skill.get("url").asText())).build(),
HttpResponse.BodyHandlers.ofByteArray()
).body();
var actual = bytesToHex(MessageDigest.getInstance("SHA-256").digest(body));
System.out.println(skill.get("name").asText() + ": " +
(actual.equals(skill.get("sha256").asText()) ? "OK" : "MISMATCH"));
}
}
static String bytesToHex(byte[] b) {
var sb = new StringBuilder();
for (byte x : b) sb.append(String.format("%02x", x));
return sb.toString();
}
}
using System.Security.Cryptography;
using System.Text.Json;
using var client = new HttpClient();
var raw = await client.GetStringAsync("{url}");
using var idx = JsonDocument.Parse(raw);
foreach (var skill in idx.RootElement.GetProperty("skills").EnumerateArray()) {
var url = skill.GetProperty("url").GetString();
var declared = skill.GetProperty("sha256").GetString();
var body = await client.GetByteArrayAsync(url);
var actual = Convert.ToHexString(SHA256.HashData(body)).ToLowerInvariant();
Console.WriteLine($"{skill.GetProperty("name")}: {(actual == declared ? "OK" : "MISMATCH")}");
}
require "net/http"
require "json"
require "digest"
index = JSON.parse(Net::HTTP.get(URI("{url}")))
puts "$schema: #{index['$schema']}"
(index["skills"] || []).each do |skill|
body = Net::HTTP.get(URI(skill["url"])) rescue nil
if body
actual = Digest::SHA256.hexdigest(body)
puts "#{skill['name']}: #{actual == skill['sha256'] ? 'OK' : 'MISMATCH'}"
else
puts "#{skill['name']}: fetch failed"
end
end
$idx = (Invoke-RestMethod -Uri '{url}')
$idx.'$schema'
foreach ($skill in $idx.skills) {
try {
$body = (Invoke-WebRequest -Uri $skill.url).Content
$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)
$hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash($bytes)
$actual = [BitConverter]::ToString($hash).Replace('-','').ToLower()
"$($skill.name): " + ($(if ($actual -eq $skill.sha256) { 'OK' } else { 'MISMATCH' }))
} catch {
"$($skill.name): fetch failed - $($_.Exception.Message)"
}
}