
Ipd Skill
- 4 installs
- 4.9k repo stars
- Updated July 14, 2026
- openai/plugins
ipd-skill skill documents Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API.
About
ipd-skill skill documents Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API. Use when a user wants concise IPD summaries; save raw JSON or text only on request.. name: ipd-skill description: Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API. Use when a user wants concise IPD summaries; save raw JSON or text only on request.
- Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API.
- Platform-specific setup patterns for ipd-skill.
- Evidence-backed steps from upstream SKILL.md.
- When-to-use criteria for ipd-skill versus alternatives.
Ipd Skill by the numbers
- 4 all-time installs (skills.sh)
- Ranked #13,349 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
ipd-skill capabilities & compatibility
- Capabilities
- ipd skill quick start · ipd skill when to use guidance · ipd skill integration patterns
- Use cases
- orchestration
What ipd-skill says it does
Use `scripts/rest_request.py` for all IPD calls.
Use `base_url=https://www.ebi.ac.uk/cgi-bin/ipd/api`.
npx skills add https://github.com/openai/plugins --skill ipd-skillAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 4.9k |
| Last updated | July 14, 2026 |
| Repository | openai/plugins ↗ |
How do I use ipd-skill correctly?
Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API. Use when a user wants concise IPD summaries; save raw JSON or text only on request.
Who is it for?
Teams implementing ipd-skill workflows from the catalog.
Skip if: Skip when requirements clearly match a different specialized stack.
When should I use this skill?
User asks about ipd-skill, submit compact ipd rest requests for hla allele and cell-level metadata using the public i.
What you get
Working ipd-skill setup with validated configuration and next steps.
Files
Operating rules
- Use
scripts/rest_request.pyfor all IPD calls. - Use
base_url=https://www.ebi.ac.uk/cgi-bin/ipd/api. - The most stable public routes are
alleleandcell. - For HLA allele browsing, pass
project=HLAand keeplimitmodest. - Re-run requests in long conversations instead of relying on older tool output.
Execution behavior
- Return concise markdown summaries from the script JSON by default.
- Return raw JSON or text only if the user explicitly asks for machine-readable output.
- Prefer these paths:
allele,cell, andallele/download.
Input
- Read one JSON object from stdin.
- Required fields:
base_url,path - Optional fields:
method,params,headers,json_body,form_body,record_path,response_format,max_items,max_depth,timeout_sec,save_raw,raw_output_path - Common IPD patterns:
{"base_url":"https://www.ebi.ac.uk/cgi-bin/ipd/api","path":"allele","params":{"project":"HLA","limit":10},"record_path":"data","max_items":10}{"base_url":"https://www.ebi.ac.uk/cgi-bin/ipd/api","path":"allele","params":{"project":"HLA","query":"contains(name,\"A*01\")","limit":10},"record_path":"data","max_items":10}{"base_url":"https://www.ebi.ac.uk/cgi-bin/ipd/api","path":"cell","params":{"limit":10},"record_path":"data","max_items":10}
Output
- Success returns
ok,source,path,method,status_code,warnings, and either compactrecords, a compactsummary, ortext_head. - Use
raw_output_pathwhensave_raw=true. - Failure returns
ok=falsewitherror.codeanderror.message.
Execution
echo '{"base_url":"https://www.ebi.ac.uk/cgi-bin/ipd/api","path":"allele","params":{"project":"HLA","limit":10},"record_path":"data","max_items":10}' | python scripts/rest_request.pyReferences
- No additional runtime references are required; keep the import package limited to this file and
scripts/rest_request.py.
interface:
display_name: "IPD"
short_description: "Fetch IPD allele summaries"
#!/usr/bin/env python3
"""Generic compact REST client for ChatGPT-imported skills."""
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import Any
try:
import requests
except ImportError as exc: # pragma: no cover
requests = None
REQUESTS_IMPORT_ERROR = exc
else:
REQUESTS_IMPORT_ERROR = None
def error(code: str, message: str, warnings: list[str] | None = None) -> dict[str, Any]:
return {"ok": False, "error": {"code": code, "message": message}, "warnings": warnings or []}
def _require_object(name: str, value: Any) -> dict[str, Any]:
if value is None:
return {}
if not isinstance(value, dict):
raise ValueError(f"`{name}` must be an object.")
return value
def _require_bool(name: str, value: Any, default: bool) -> bool:
if value is None:
return default
if not isinstance(value, bool):
raise ValueError(f"`{name}` must be a boolean.")
return value
def _require_int(name: str, value: Any, default: int) -> int:
if value is None:
return default
if not isinstance(value, int) or value <= 0:
raise ValueError(f"`{name}` must be a positive integer.")
return value
def _require_str(name: str, value: Any, required: bool = False) -> str | None:
if value is None:
if required:
raise ValueError(f"`{name}` is required.")
return None
if not isinstance(value, str) or not value.strip():
raise ValueError(f"`{name}` must be a non-empty string.")
return value.strip()
def _service_name(base_url: str) -> str:
host = base_url.split("://", 1)[-1].split("/", 1)[0]
return host.replace(".", "-")
def _build_url(base_url: str, path: str) -> str:
if path.startswith(("http://", "https://")):
return path
return base_url.rstrip("/") + "/" + path.lstrip("/")
def _get_by_path(value: Any, path: str) -> Any:
current = value
for part in path.split("."):
if isinstance(current, list):
if not part.isdigit():
raise ValueError(f"`record_path` segment {part!r} must be a list index.")
index = int(part)
if index >= len(current):
raise ValueError(f"`record_path` index {index} is out of range.")
current = current[index]
elif isinstance(current, dict):
if part not in current:
raise ValueError(f"`record_path` key {part!r} was not present in the response.")
current = current[part]
else:
raise ValueError(f"`record_path` segment {part!r} could not be applied.")
return current
def _infer_target(data: Any) -> tuple[str | None, Any]:
if isinstance(data, list):
return "$", data
if isinstance(data, dict):
embedded = data.get("_embedded")
if isinstance(embedded, dict):
for key, value in embedded.items():
if isinstance(value, list):
return f"_embedded.{key}", value
for key in (
"collection",
"results",
"structures",
"activities",
"molecules",
"mechanisms",
"records",
"items",
):
value = data.get(key)
if isinstance(value, list):
return key, value
return None, data
def _compact(value: Any, max_items: int, max_depth: int) -> Any:
if isinstance(value, str):
return value if len(value) <= 240 else value[:240] + "..."
if max_depth <= 0:
if isinstance(value, (dict, list)):
return "..."
return value
if isinstance(value, list):
out = [_compact(item, max_items, max_depth - 1) for item in value[:max_items]]
if len(value) > max_items:
out.append(f"... (+{len(value) - max_items} more)")
return out
if isinstance(value, dict):
out: dict[str, Any] = {}
items = list(value.items())
for key, item in items[:max_items]:
out[str(key)] = _compact(item, max_items, max_depth - 1)
if len(items) > max_items:
out["_truncated_keys"] = len(items) - max_items
return out
return value
def _save_raw_output(
raw_output: str, raw_output_path: str | None, base_url: str, suffix: str
) -> str:
path = Path(raw_output_path or f"/tmp/{_service_name(base_url)}-raw.{suffix}")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(raw_output, encoding="utf-8")
return str(path)
def parse_input(payload: Any) -> dict[str, Any]:
if not isinstance(payload, dict):
raise ValueError("Input must be one JSON object.")
base_url = _require_str("base_url", payload.get("base_url"), required=True)
path = _require_str("path", payload.get("path"), required=True)
method = (_require_str("method", payload.get("method")) or "GET").upper()
if method not in {"GET", "POST"}:
raise ValueError("`method` must be GET or POST.")
json_body = payload.get("json_body")
form_body = payload.get("form_body")
if json_body is not None and form_body is not None:
raise ValueError("Provide only one of `json_body` or `form_body`.")
response_format = (
_require_str("response_format", payload.get("response_format")) or "auto"
).lower()
if response_format not in {"auto", "json", "text"}:
raise ValueError("`response_format` must be auto, json, or text.")
return {
"base_url": base_url,
"path": path,
"method": method,
"params": _require_object("params", payload.get("params")),
"headers": _require_object("headers", payload.get("headers")),
"json_body": json_body,
"form_body": _require_object("form_body", form_body) if form_body is not None else None,
"record_path": _require_str("record_path", payload.get("record_path")),
"response_format": response_format,
"max_items": _require_int("max_items", payload.get("max_items"), 5),
"max_depth": _require_int("max_depth", payload.get("max_depth"), 3),
"timeout_sec": _require_int("timeout_sec", payload.get("timeout_sec"), 30),
"save_raw": _require_bool("save_raw", payload.get("save_raw"), False),
"raw_output_path": _require_str("raw_output_path", payload.get("raw_output_path")),
}
def execute(payload: Any) -> dict[str, Any]:
if requests is None:
return error("missing_dependency", f"`requests` is required: {REQUESTS_IMPORT_ERROR}")
config = parse_input(payload)
session = requests.Session()
session.headers.update(config["headers"])
url = _build_url(config["base_url"], config["path"])
request_kwargs: dict[str, Any] = {"params": config["params"], "timeout": config["timeout_sec"]}
if config["json_body"] is not None:
request_kwargs["json"] = config["json_body"]
if config["form_body"] is not None:
request_kwargs["data"] = config["form_body"]
try:
response = session.request(config["method"], url, **request_kwargs)
response.raise_for_status()
content_type = (response.headers.get("content-type") or "").lower()
wants_json = config["response_format"] == "json"
wants_text = config["response_format"] == "text"
auto_json = not wants_text and (
"json" in content_type or response.text.lstrip().startswith(("{", "["))
)
if wants_json or auto_json:
data = response.json()
raw_output = json.dumps(data, indent=2)
raw_output_path = None
if config["save_raw"]:
raw_output_path = _save_raw_output(
raw_output, config["raw_output_path"], config["base_url"], "json"
)
record_path = config["record_path"]
path_used, target = (
_infer_target(data)
if record_path is None
else (record_path, _get_by_path(data, record_path))
)
out = {
"ok": True,
"source": _service_name(config["base_url"]),
"path": config["path"],
"method": config["method"],
"status_code": response.status_code,
"record_path": path_used,
"raw_output_path": raw_output_path,
"warnings": [],
}
if isinstance(target, list):
records = target[: config["max_items"]]
out.update(
{
"record_count_returned": len(records),
"record_count_available": len(target),
"truncated": len(records) < len(target),
"records": _compact(records, config["max_items"], config["max_depth"]),
}
)
else:
out["summary"] = _compact(target, config["max_items"], config["max_depth"])
if isinstance(target, dict):
out["top_keys"] = list(target)[: config["max_items"]]
return out
raw_output_path = None
if config["save_raw"]:
raw_output_path = _save_raw_output(
response.text, config["raw_output_path"], config["base_url"], "txt"
)
text_head = response.text[:800]
return {
"ok": True,
"source": _service_name(config["base_url"]),
"path": config["path"],
"method": config["method"],
"status_code": response.status_code,
"content_type": content_type,
"text_head": None if raw_output_path else text_head,
"text_head_truncated": False
if raw_output_path
else len(text_head) < len(response.text),
"raw_output_path": raw_output_path,
"warnings": [],
}
except ValueError as exc:
return error("invalid_response", str(exc))
except requests.RequestException as exc:
return error("network_error", f"Request failed: {exc}")
finally:
session.close()
def main() -> int:
try:
payload = json.load(sys.stdin)
except Exception as exc: # noqa: BLE001
sys.stdout.write(json.dumps(error("invalid_json", f"Could not parse JSON input: {exc}")))
return 2
try:
output = execute(payload)
except ValueError as exc:
output = error("invalid_input", str(exc))
code = 2
else:
code = 0 if output.get("ok") else 1
sys.stdout.write(json.dumps(output))
return code
if __name__ == "__main__":
raise SystemExit(main())
Related skills
FAQ
What does ipd-skill do?
ipd-skill skill documents Submit compact IPD REST requests for HLA allele and cell-level metadata using the public IPD query API.
When should I use ipd-skill?
User asks about ipd-skill, submit compact ipd rest requests for hla allele and cell-level metadata using the public i.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.