agentskills-http
HTTP static-file skill provider for the Agent Skills SDK.
Serves Agent Skills from any static HTTP file host - S3, Azure Blob, CDN, GitHub Pages, Nginx, etc. Expects the same directory-tree layout as the filesystem provider, served over HTTP.
Installation
pip install agentskills-http
Requires Python 3.12 or newer. Installs agentskills-core, httpx, and pyyaml as dependencies.
Expected URL Layout
https://cdn.example.com/skills/
├── incident-response/
│ ├── SKILL.md
│ ├── references/severity-levels.md
│ ├── scripts/page-oncall.sh
│ └── assets/flowchart.mermaid
└── another-skill/
└── SKILL.md
Usage
from agentskills_core import SkillRegistry
from agentskills_http import HTTPStaticFileSkillProvider
async with HTTPStaticFileSkillProvider("https://cdn.example.com/skills") as provider:
registry = SkillRegistry()
await registry.register("incident-response", provider)
skill = registry.get_skill("incident-response")
meta = await skill.get_metadata()
body = await skill.get_body()
Custom Headers
Pass authentication or other headers:
from agentskills_http import HTTPStaticFileSkillProvider
provider = HTTPStaticFileSkillProvider(
"https://cdn.example.com/skills",
headers={"Authorization": "Bearer <token>"},
)
Bring Your Own Client
Supply a pre-configured httpx.AsyncClient for full control over timeouts, proxies, etc.:
import httpx
from agentskills_http import HTTPStaticFileSkillProvider
client = httpx.AsyncClient(timeout=30, headers={"Authorization": "Bearer <token>"})
provider = HTTPStaticFileSkillProvider("https://cdn.example.com/skills", client=client)
# caller is responsible for closing the client
Note:
clientandheadersare mutually exclusive. Configure headers on the client directly when providing your own.
API
HTTPStaticFileSkillProvider(base_url, *, client=None, headers=None, params=None, require_tls=False, max_response_bytes=10_485_760, revalidate=False)
| Parameter | Type | Default | Description |
|---|---|---|---|
base_url |
str |
- | Root URL where the skill tree is hosted |
client |
AsyncClient | None |
None |
Pre-configured httpx client (caller manages lifecycle) |
headers |
dict | None |
None |
Extra headers sent with every request |
params |
dict | None |
None |
Query parameters appended to every request |
require_tls |
bool |
False |
Reject http:// URLs with ValueError |
max_response_bytes |
int |
10_485_760 |
Maximum allowed response size in bytes |
revalidate |
bool |
False |
Re-check cached SKILL.md on every access with If-None-Match / If-Modified-Since |
resource_manifest |
bool |
False |
Enable list_resources() by reading a per-skill index.json |
timeout |
float |
30.0 |
Request timeout in seconds (ignored when you supply client) |
max_retries |
int |
2 |
Retries after the initial attempt, for retryable failures only |
retry_backoff |
float |
0.5 |
Base delay in seconds for exponential backoff |
max_retry_delay |
float |
30.0 |
Ceiling on any single backoff sleep |
Note:
clientandheaders/paramsare mutually exclusive. Configure headers and params on the client directly when providing your own.
| Method | Returns | Description |
|---|---|---|
get_metadata(skill_id) |
dict[str, Any] |
Parsed YAML frontmatter from SKILL.md |
get_body(skill_id) |
str |
Markdown body after the frontmatter |
get_script(skill_id, name) |
bytes |
Raw script content |
get_asset(skill_id, name) |
bytes |
Raw asset content |
get_reference(skill_id, name) |
bytes |
Raw reference content |
list_resources(skill_id) |
dict[str, list[str]] |
Resource names from index.json (requires resource_manifest=True) |
invalidate(skill_id=None) |
None |
Drop cached SKILL.md content for one skill, or all skills |
aclose() |
None |
Close the HTTP client (if owned by the provider) |
Supports async with for automatic cleanup.
Resource Discovery
A static file host cannot be enumerated: there is no portable directory listing over plain HTTP. By default this provider therefore reports that it cannot list resources — list_resources() raises ResourceListingNotSupportedError — rather than returning an empty mapping that would look like a skill with no resources.
If you control the host, publish a small manifest at {base_url}/{skill_id}/index.json:
{
"references": ["severity-levels.md"],
"scripts": ["page-oncall.sh"],
"assets": ["flowchart.mermaid"]
}
Then opt in:
provider = HTTPStaticFileSkillProvider(BASE, resource_manifest=True)
listing = await provider.list_resources("incident-response")
Missing categories default to empty lists. A manifest is host-supplied data whose entries are later interpolated into URLs, so names failing the identifier-safety check are dropped. If a given skill has no index.json, list_resources() raises ResourceListingNotSupportedError for that skill — again, not an empty result.
Caching
SKILL.md responses are cached per provider instance. Without it a single skill costs up to five round-trips per agent session — twice during registration, once per catalog build, and again on each tool call. Scripts, assets and references are not cached.
By default the cache is served until you call invalidate(). If your host serves mutable skills and the process is long-lived, opt into conditional revalidation instead:
provider = HTTPStaticFileSkillProvider(BASE, revalidate=True)
That sends If-None-Match / If-Modified-Since on every access and reuses the cached body on 304. It costs one cheap round-trip per access, so prefer the default plus an explicit invalidate() when you control publishing.
Error Handling
| Scenario | Exception | Retried |
|---|---|---|
404 / 410 on SKILL.md |
SkillNotFoundError |
No |
404 / 410 on a resource |
ResourceNotFoundError |
No |
5xx, 408, 425, 429 |
SkillUnavailableError |
Yes |
| Timeouts, connection and protocol errors | SkillUnavailableError |
Yes |
401 / 403 |
AgentSkillsError |
No |
Other 4xx, oversized responses |
AgentSkillsError |
No |
All exceptions inherit from AgentSkillsError.
The split between SkillNotFoundError and SkillUnavailableError is the point of the taxonomy: a 503 means the skill may well exist and the same request could succeed in a moment, whereas a 404 means it is gone. Collapsing both into "not found" turns a retryable blip into a permanent-looking failure, and nothing downstream can tell the difference.
Retries
Retryable failures are retried with exponential backoff and full jitter:
provider = HTTPStaticFileSkillProvider(
BASE,
max_retries=2, # attempts after the first; 0 disables
retry_backoff=0.5, # base delay in seconds
max_retry_delay=30.0, # ceiling on any single sleep
)
Jitter matters because a registry builds its catalog concurrently — without it, every skill fetch would retry in lockstep and hit the recovering server as one wave.
Retry-After is honoured in both the delay-seconds and HTTP-date forms. If the server asks for longer than max_retry_delay, the request is not retried: blocking a request path for minutes is worse than failing fast. The advised delay is still available to the caller as SkillUnavailableError.retry_after, so a scheduler can act on it.
Security
- Input validation - Skill IDs and resource names are validated against a safe-character pattern (
^[a-zA-Z0-9][a-zA-Z0-9._-]*$) to prevent path-traversal and injection attacks. - TLS warnings - A
UserWarningis emitted whenbase_urluses unencrypted HTTP. Setrequire_tls=Trueto reject HTTP URLs entirely. - Redirect protection - The internally-created HTTP client does not follow redirects by default, preventing open-redirect SSRF.
- Timeouts - Default 30-second timeout on all HTTP requests. Configure via
timeout. - Response size limits - Responses exceeding 10 MB (default) are rejected before processing. Configure via
max_response_bytes. - Error-message sanitization - Messages carry the status code and the path relative to
base_url— never the host, never a query string. The underlyinghttpxexception is deliberately not chained (from None), becausehttpx.HTTPStatusErrorrenders the full request URL including its query string, which is exactly where SAS tokens and signed-URL signatures live. Chaining it leaked credentials into every traceback.
For the full security policy, see SECURITY.md.
Deployment Considerations
- Rate limiting - The SDK does not enforce rate limits on MCP tool calls or HTTP requests. Deploy behind a reverse proxy or API gateway that provides rate limiting in production environments.
- Credential management - Do not store secrets (API keys, SAS tokens, Authorization headers) in config files committed to version control. Use environment variables or a secret manager instead.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentskills_http-0.3.0.tar.gz.
File metadata
- Download URL: agentskills_http-0.3.0.tar.gz
- Upload date:
- Size: 14.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0460e608d0357c8031c312747f0122ad3ddf082660cae5dbf069a7dbf1d3888c
|
|
| MD5 |
9b3363ce6442e289f03b7e7da188348b
|
|
| BLAKE2b-256 |
503e21cf4b4d9dbf73c463ee6b2cbbeb8275e3397b99ebca2634c0fe3a474f65
|
Provenance
The following attestation bundles were made for agentskills_http-0.3.0.tar.gz:
Publisher:
publish.yml on pratikxpanda/agentskills-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentskills_http-0.3.0.tar.gz -
Subject digest:
0460e608d0357c8031c312747f0122ad3ddf082660cae5dbf069a7dbf1d3888c - Sigstore transparency entry: 2298808069
- Sigstore integration time:
-
Permalink:
pratikxpanda/agentskills-sdk@8c402728312bf75b0a4457f4adf917cc18651a70 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/pratikxpanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c402728312bf75b0a4457f4adf917cc18651a70 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentskills_http-0.3.0-py3-none-any.whl.
File metadata
- Download URL: agentskills_http-0.3.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
136355850ed9c57e055e564d787122acd0fc77a29681fc1b0f6eee19642c6735
|
|
| MD5 |
5a8f8d7cdb2e5c558e253a2af2ed332a
|
|
| BLAKE2b-256 |
63f1dddf137848c85c2ae253d37221cd09108d4769b4c83313332ee570fd065f
|
Provenance
The following attestation bundles were made for agentskills_http-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on pratikxpanda/agentskills-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentskills_http-0.3.0-py3-none-any.whl -
Subject digest:
136355850ed9c57e055e564d787122acd0fc77a29681fc1b0f6eee19642c6735 - Sigstore transparency entry: 2298808118
- Sigstore integration time:
-
Permalink:
pratikxpanda/agentskills-sdk@8c402728312bf75b0a4457f4adf917cc18651a70 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/pratikxpanda
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c402728312bf75b0a4457f4adf917cc18651a70 -
Trigger Event:
push
-
Statement type: