From twig
Use when querying or debugging the Twig Online Content Service (TOCS) REST API — version/resource lookups, the `relations` query param, `public_key` tenanting, full-text search, and the V1 vs V2 endpoint split. Hosts at tree.twig.world / tocs.twig-world.com (prod), staging-tocs.twig-world.com, uat-tocs.twig-world.com. Triggers on mentions of TOCS, twig-world.com, tree.twig.world, `/api/content/v1/`, `/api/content/v2/`, version IDs like `USENVISUAL16470`, or resource codes like VISUAL/FILM/AUDIO.
How this skill is triggered — by the user, by Claude, or both
Slash command
/twig:tocs-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Twig Online Content Service is a read-only API hosting Twig's educational
Twig Online Content Service is a read-only API hosting Twig's educational content. There is no OpenAPI schema exposed, so use this skill as the contract.
| Env | Base URL | Notes |
|---|---|---|
| Prod | https://tree.twig.world | Primary prod host. Also answers at https://tocs.twig-world.com (same backend). |
| Staging | https://staging-tocs.twig-world.com | |
| UAT | https://uat-tocs.twig-world.com | |
| Local | http://localhost:8000 |
Sanity check connectivity with GET /health/ (no auth required).
Authorization: Bearer <JWT> — a client-credentials JWT from the
Twig Science auth server (gty: "client-credentials"). You mint this from
the TWIG_TOCS_OAUTH_* env vars — see the setup flow below.?public_key=<app_key> — a tenant/app identifier. This is not
optional; the server looks it up against ContentAPIApplication and uses
it to filter every query to that tenant's published resources, languages,
and partner. Use TWIG_MCP_TOCS_APP_NAME (e.g. tscore-pilot).Auth is a token minted from OAuth client-credentials. The plugin env vars
TWIG_TOCS_OAUTH_CLIENT_ID / TWIG_TOCS_OAUTH_CLIENT_SECRET hold the creds, and
TWIG_MCP_TOCS_APP_NAME is the public_key. The minted token is a service
token — not tenant-scoped — so it works for any public_key the client is
permitted for, on both V1 and V2.
Check the creds are set (first Bash call in any TOCS task):
echo "client_id:${TWIG_TOCS_OAUTH_CLIENT_ID:+set} secret:${TWIG_TOCS_OAUTH_CLIENT_SECRET:+set} app:${TWIG_MCP_TOCS_APP_NAME:-unset}"
If any are unset, the user hasn't configured them — point them at
/twig:check-env (which lists where to get each) and stop; don't invent
credentials or read them from another repo's .env.
Mint a token, then make the request — in one Bash call. Each Bash tool
call is a fresh shell, so a $TOK set in one call won't survive to the next.
Mint and use it in the same compound command:
TOK=$(curl -sS -m10 -X POST "${TWIG_TOCS_OAUTH_TOKEN_URL:-https://api.twigscience.com/svc/auth/oauth/token/}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode grant_type=client_credentials \
--data-urlencode "client_id=$TWIG_TOCS_OAUTH_CLIENT_ID" \
--data-urlencode "client_secret=$TWIG_TOCS_OAUTH_CLIENT_SECRET" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])') \
&& curl -sS "https://tree.twig.world/api/content/v1/version/USENVISUAL16470/?public_key=$TWIG_MCP_TOCS_APP_NAME" \
-H "Authorization: Bearer $TOK"
Every request sends both the Authorization: Bearer $TOK header and the
?public_key=$TWIG_MCP_TOCS_APP_NAME query param. The token is valid ~18h,
so within a single Bash call you can mint once and reuse $TOK across several
chained curls.
Run /twig:check-env to validate this end-to-end — it mints from the same
creds and probes TOCS V1, reporting OK / 403 (no permission for that app) / 401.
id_tokenThe content-API routes require gty: "client-credentials". A QA-login
id_token is not accepted — on prod it typically fails with
{"detail":"JWT Missing kid"} (a signing-key mismatch, not a scheme problem;
swapping Bearer↔JWT won't fix it). The minted token above is the right kind
and works on V1 and V2. If minting fails, the client id/secret are wrong or
lack scope — check with the TOCS/content team.
/api/content/v1/) — rich content graph fetches. Use when you need:
relations param to fetch nested related versions in one call.filter__<attribute> filtering on Version fields.version/search/<ietf_tag>/)./api/content/v2/) — application/manifest/published-content
browsing. Use when you need:
/applications/<public_key>/…).page_size up to 1000)./api/content/v2_experimental/) — in flux, ignore
unless the user asks for it explicitly. Has a graph-node view.The harness auto-approves curl -sS https://tree.twig.world/*, the two
twig-world.com hosts, and python3 -m json.tool (no args, stdin-only). To
stay inside that allowlist:
python3 -m json.tool — nothing else.
curl … | python3 -m json.tool is fine.head, tail, jq, grep, sed — each would either
re-prompt or open file-read holes if added to the allowlist.-o /tmp/tocs.json, then
use the Read tool (with offset/limit if needed) to view it. Read
needs no Bash permission.curl -o - piped through other tools; the wildcard rule
matches the whole command line, and any extra pipeline stage re-triggers
approval.These assume $TOK is a minted token (see the Auth setup flow) and use
$TWIG_MCP_TOCS_APP_NAME as the public_key.
Get a version + its images (V1 — the README example, decoded for clarity):
curl -sS "https://staging-tocs.twig-world.com/api/content/v1/version/USENVISUAL16470/?public_key=$TWIG_MCP_TOCS_APP_NAME&relations=$(python3 -c 'import urllib.parse,json;print(urllib.parse.quote(json.dumps([["content_of__IMAGE","images"]])))')" \
-H "Authorization: Bearer $TOK"
List published versions for an application (V2):
curl -sS "https://staging-tocs.twig-world.com/api/content/v2/applications/$TWIG_MCP_TOCS_APP_NAME/versions/?language=en-GB&page=1&page_size=100" \
-H "Authorization: Bearer $TOK"
Full-text search in a language (V1):
curl -sS "https://staging-tocs.twig-world.com/api/content/v1/version/search/en-GB/?public_key=$TWIG_MCP_TOCS_APP_NAME&q=photosynthesis&limit=25" \
-H "Authorization: Bearer $TOK"
relations paramrelations is a JSON array. Raw JSON contains characters that must be
URL-encoded before going into a query string. Don't trust your shell to do it.
Either use --url-query with modern curl, or encode via python:
python3 -c 'import urllib.parse,json; print(urllib.parse.quote(json.dumps([["content_of__IMAGE","images"]])))'
# → %5B%5B%22content_of__IMAGE%22%2C%20%22images%22%5D%5D
Load these on demand; they are not auto-loaded:
references/endpoints-v1.md — every V1 URL, view, method, params.references/endpoints-v2.md — every V2 URL, view, method, params.references/relations.md — the relations param in depth (2/3/4-tuple
forms, chain syntax, worked examples).references/resource-codes.md — the ~21 resource codes (VISUAL, FILM, etc.)
and the version-ID format.references/filters-and-search.md — filter__* params, pagination, the
Haystack search endpoint, staging/promoted flags.references/common-queries.md — 12 copy-pasteable curl recipes for the most
common developer questions.GET /api/content/v1/meta/versions/ — list all resource codes and their
attributes, with can_filter: true|false per attribute.GET /api/content/v1/meta/versions/<RESOURCE_CODE>/ — attributes for a
single resource code.GET /api/content/v1/meta/relations/ — every ResourceRelationshipType
known to the server, including the identifier you'd use in a relations
chain.GET /api/content/v2/applications/ — every content-API application (the
tenants whose public_key every request needs). applications/<public_key>/
for one. See the next section.Prefer querying these over guessing attribute or relation names — the lists drift over time.
public_key values)The ?public_key=<app_key> every request requires (V1 query param / V2 path
segment) is a ContentAPIApplication identifier — a tenant/app that scopes
content to what's published for it. The same identifier appears as
TWIG_MCP_TOCS_APP_NAME in the twig-graphql MCP server and as the
settings.TOCS_API dict keys in content-service.
There is no static list to memorise — discover it:
/twig:check-env → step 3 runs
hooks/scripts/check-twig-env.sh --list-apps, which mints a token from the
TWIG_TOCS_OAUTH_CLIENT_ID / TWIG_TOCS_OAUTH_CLIENT_SECRET env vars and
prints the active values grouped by type. Easiest path if those creds are set.GET /api/content/v2/applications/?page_size=50 (see recipe in
references/common-queries.md). Returns {publicKey, name, …} per app.https://tree.twig.world/555-2368/content_api/contentapiapplication/.Auth caveat: the applications endpoint needs a client-credentials JWT. The QA
id_tokenmay return401 {"detail":"Missing JWT token"}orJWT Missing kidon V2 prod routes (same gotcha documented above). If the token bounces, use the admin UI, or list against staging/UAT.
Common examples (not exhaustive): tscore-pilot (the default for most prod
work), tscore-ca, tscore-live-ng. The authoritative set is whatever the
endpoint/UI returns.
twig-content — the content domain model. Use it for what a resource
code means, the full code catalog, the Resource/Version pattern, subtypes,
relationship semantics (content_of, follow/grant_access, permission
propagation), and the publication model. This skill (tocs-api) covers how to
call TOCS; twig-content covers what the data is.content-delivery — the service topology in front of TOCS. Use it to
decide which service/repo owns a given GraphQL field or REST path, how a
resource reaches the client, and where caching/public_key resolution
happens across the stack. Reach for it when the question is "which service
serves X", not "how do I query TOCS directly".npx claudepluginhub imaginelearning/dp-claude-plugin --plugin twigCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.