.claude/skills/dynamic-api-integration/SKILL.md
Discover, parse, and call external HTTP APIs at runtime using OpenAPI specs, tool templates, and iterative chaining. Adapted from UTCP (Universal Tool Calling Protocol) patterns for Node.js and Claude Code agents.
npx skillsauth add oimiragieo/agent-studio dynamic-api-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Mode: Cognitive/Prompt-Driven — No standalone utility script; use via agent context.
This skill teaches agents how to dynamically discover, parse, and call external HTTP APIs at runtime. It is adapted from the Universal Tool Calling Protocol (UTCP) patterns, translated into Node.js / Claude Code tool patterns.
Core workflow (5-phase, inspired by UTCP agent state machine):
Use this skill when:
Do NOT use when:
$ENV_VAR) or a secrets manager.When the API provides an OpenAPI spec (most modern APIs do):
# Step 1: Fetch the OpenAPI spec
WebFetch({
url: "https://api.example.com/openapi.json",
prompt: "Extract all API endpoints. For each endpoint, list: HTTP method, path, description, required parameters, optional parameters, authentication requirement. Return as a structured list."
})
What to extract from the spec:
| Field | Location in Spec | Purpose |
| --------------- | ----------------------------- | -------------------------- |
| Base URL | servers[0].url | API root for all requests |
| Endpoints | paths.* | Available operations |
| Methods | paths.*.get/post/put/delete | HTTP verbs per endpoint |
| Parameters | paths.*.*.parameters[] | Query, path, header params |
| Request body | paths.*.*.requestBody | POST/PUT payload schema |
| Auth | components.securitySchemes | API key, Bearer, OAuth |
| Response schema | paths.*.*.responses.200 | Expected response format |
Common OpenAPI spec locations:
https://api.example.com/openapi.jsonhttps://api.example.com/swagger.jsonhttps://api.example.com/v3/api-docshttps://api.example.com/.well-known/openapi.jsonhttps://api.example.com/docs (HTML page may link to spec)When no OpenAPI spec exists, define a tool template manually:
{
"name": "search_books",
"description": "Search Open Library for books by query",
"base_url": "https://openlibrary.org/search.json",
"method": "GET",
"auth": null,
"parameters": {
"q": {
"type": "string",
"required": true,
"in": "query",
"description": "Search query (title, author, ISBN)"
},
"limit": {
"type": "integer",
"required": false,
"in": "query",
"description": "Max results to return (default 10)"
},
"page": {
"type": "integer",
"required": false,
"in": "query",
"description": "Page number for pagination"
}
},
"response_hint": "Returns { numFound, docs: [{ title, author_name, first_publish_year }] }"
}
The tool template format (inspired by UTCP manual_call_templates):
{
"name": "string (required) — unique tool identifier, lowercase_snake_case",
"description": "string (required) — what this tool does, used for semantic matching",
"base_url": "string (required) — full URL including path",
"method": "string (required) — GET | POST | PUT | PATCH | DELETE",
"content_type": "string (optional) — default: application/json",
"auth": {
"type": "string — api_key | bearer | basic | none",
"header": "string — header name (e.g., X-Api-Key, Authorization)",
"env_var": "string — environment variable name holding the secret",
"prefix": "string (optional) — e.g., 'Bearer ' for bearer auth"
},
"parameters": {
"<param_name>": {
"type": "string | integer | boolean | array | object",
"required": "boolean",
"in": "query | path | header | body",
"description": "string — what this parameter does",
"default": "any (optional) — default value if not provided"
}
},
"response_hint": "string (optional) — brief description of response shape"
}
Before calling an API, match the user's intent to the correct endpoint:
Ask yourself: What data does the user want? What action do they want performed?
| User Intent | Likely HTTP Method | Endpoint Pattern |
| ---------------------------- | ------------------ | --------------------------------- |
| "Find / search / list / get" | GET | /search, /list, /{resource} |
| "Create / add / register" | POST | /{resource} |
| "Update / modify / change" | PUT or PATCH | /{resource}/{id} |
| "Delete / remove" | DELETE | /{resource}/{id} |
| "Get details about X" | GET | /{resource}/{id} |
required: true in spec/template)./repos/{owner}/{repo}).?key=value&key2=value2.API Key (Header):
curl -s -X GET "https://api.example.com/data?q=test" \
-H "X-Api-Key: $API_KEY"
Bearer Token:
curl -s -X GET "https://api.example.com/data" \
-H "Authorization: Bearer $AUTH_TOKEN"
Basic Auth:
curl -s -X GET "https://api.example.com/data" \
-u "$USERNAME:$PASSWORD"
No Auth (Public API):
curl -s -X GET "https://api.example.com/data?q=test"
GET with query parameters:
curl -s -X GET "https://api.example.com/search?q=test&limit=10&page=1" \
-H "Accept: application/json" \
-H "Authorization: Bearer $TOKEN"
POST with JSON body:
curl -s -X POST "https://api.example.com/items" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name": "New Item", "category": "tools", "price": 29.99}'
PUT with path parameter:
curl -s -X PUT "https://api.example.com/items/123" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name": "Updated Item", "price": 39.99}'
DELETE:
curl -s -X DELETE "https://api.example.com/items/123" \
-H "Authorization: Bearer $TOKEN"
# Execute and capture response + HTTP status
RESPONSE=$(curl -s -w "\n%{http_code}" -X GET "https://api.example.com/search?q=test" \
-H "Accept: application/json")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
echo "Status: $HTTP_CODE"
echo "Body: $BODY" | head -c 2000 # Truncate to 2KB for context safety
WebFetch({
url: 'https://api.example.com/search?q=test',
prompt:
'Extract the top 5 results with their titles and descriptions. Format as a numbered list.',
});
When to use which:
| Scenario | Tool | Reason | | ------------------------------------ | ---------------------- | ----------------------------------- | | Need raw JSON for further processing | Bash (curl) | Full control, parseable output | | Need summarized/extracted data | WebFetch | AI processes response inline | | Need to check HTTP status codes | Bash (curl) | WebFetch abstracts status away | | Large response (>50KB) | Bash (curl) + truncate | WebFetch may timeout on large pages | | HTML page (not JSON) | WebFetch | Converts HTML to markdown |
HTTP Status Code Handling:
| Status | Meaning | Action | | ------- | ------------ | ----------------------------------------------- | | 200-299 | Success | Parse response, continue | | 400 | Bad Request | Check parameters, fix and retry | | 401 | Unauthorized | Check API key/token, re-authenticate | | 403 | Forbidden | Check permissions, report to user | | 404 | Not Found | Check URL/resource ID, try alternative endpoint | | 429 | Rate Limited | Wait (check Retry-After header), then retry | | 500-599 | Server Error | Wait and retry up to 3 times |
Retry with Exponential Backoff:
# Retry pattern for transient errors (429, 5xx)
for attempt in 1 2 3; do
RESPONSE=$(curl -s -w "\n%{http_code}" -X GET "$URL" -H "Authorization: Bearer $TOKEN")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
if [ "$HTTP_CODE" -lt 400 ]; then
break # Success
fi
echo "Attempt $attempt failed ($HTTP_CODE), retrying in $((attempt * 2))s..."
sleep $((attempt * 2))
done
Parse JSON response (Bash):
# Extract specific fields from JSON response
echo "$BODY" | node -e "
const data = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));
console.log('Total:', data.totalResults);
data.items?.slice(0, 5).forEach((item, i) => {
console.log(\`\${i+1}. \${item.title} — \${item.description?.substring(0, 80)}\`);
});
"
Truncate large responses:
# Safety: never pass >10KB of API response into context
BODY_TRUNCATED=$(echo "$BODY" | head -c 10000)
if [ ${#BODY} -gt 10000 ]; then
echo "[TRUNCATED: Response was $(echo "$BODY" | wc -c) bytes, showing first 10KB]"
fi
Many tasks require multiple API calls chained together. Use the UTCP-inspired iterative pattern:
Iteration 1: Search -> Get list of results
Iteration 2: Get details for top result
Iteration 3: Perform action on result
(max_iterations guard: stop at 5)
MAX_ITERATIONS = 5
Before each API call:
IF iteration_count >= MAX_ITERATIONS:
STOP. Summarize what was gathered so far and respond.
ELSE:
Execute the call, increment counter, re-analyze task.
Why this matters: Without an iteration guard, an agent could loop indefinitely calling APIs. UTCP uses a default of 3; we recommend 5 for more complex multi-step workflows.
Example 1: Search and Get Details
User: "Find information about the book '1984' by George Orwell"
Iteration 1:
Call: GET https://openlibrary.org/search.json?q=1984+george+orwell&limit=5
Result: Found 5 matches, top result has key "/works/OL1168083W"
Iteration 2:
Call: GET https://openlibrary.org/works/OL1168083W.json
Result: Full book details (title, description, subjects, covers)
Task complete: Return summarized book information.
Example 2: GitHub — Find and Analyze a Repository
User: "What are the most recent issues in the react repository?"
Iteration 1:
Call: GET https://api.github.com/repos/facebook/react/issues?state=open&per_page=10&sort=created
Headers: Authorization: Bearer $GITHUB_TOKEN
Result: 10 most recent open issues
Task complete: Summarize issue titles, labels, and dates.
Example 3: Multi-API Chain
User: "Find news about AI safety and summarize the top article"
Iteration 1:
Call: GET https://newsapi.org/v2/everything?q=AI+safety&sortBy=publishedAt&pageSize=5
Headers: X-Api-Key: $NEWS_API_KEY
Result: 5 articles with titles, URLs
Iteration 2:
Call: WebFetch({ url: articles[0].url, prompt: "Summarize this article in 3 bullet points" })
Result: Article summary
Task complete: Return article title + summary.
# List repositories for a user
curl -s -X GET "https://api.github.com/users/octocat/repos?sort=updated&per_page=5" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN"
Tool Template:
{
"name": "github_list_repos",
"description": "List repositories for a GitHub user, sorted by most recently updated",
"base_url": "https://api.github.com/users/{username}/repos",
"method": "GET",
"auth": {
"type": "bearer",
"header": "Authorization",
"env_var": "GITHUB_TOKEN",
"prefix": "Bearer "
},
"parameters": {
"username": {
"type": "string",
"required": true,
"in": "path",
"description": "GitHub username"
},
"sort": {
"type": "string",
"required": false,
"in": "query",
"description": "Sort field: created, updated, pushed, full_name",
"default": "updated"
},
"per_page": {
"type": "integer",
"required": false,
"in": "query",
"description": "Results per page (max 100)",
"default": 10
}
}
}
# Search for books
curl -s -X GET "https://openlibrary.org/search.json?q=george+orwell&limit=5" \
-H "Accept: application/json"
# GET all posts
curl -s https://jsonplaceholder.typicode.com/posts?_limit=5
# POST new item
curl -s -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "Test Post", "body": "Hello world", "userId": 1}'
# Get current weather
curl -s "https://api.open-meteo.com/v1/forecast?latitude=51.5074&longitude=-0.1278¤t_weather=true"
API responses can be very large. Apply these rules to prevent context overflow:
| Response Size | Action | | ------------- | --------------------------------------- | | < 5 KB | Use full response | | 5-20 KB | Extract relevant fields only | | 20-50 KB | Summarize via WebFetch or node script | | > 50 KB | Truncate to first 5KB + count remaining |
# Instead of dumping full response, extract what you need
curl -s "https://api.example.com/search?q=test" | node -e "
const data = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));
// Extract only what the user asked for
const results = data.results.slice(0, 5).map(r => ({
id: r.id,
title: r.title,
summary: r.description?.substring(0, 200)
}));
console.log(JSON.stringify(results, null, 2));
"
curl --max-time 30)Before completing a dynamic API integration task:
| Anti-Pattern | Why It Fails | Correct Approach |
| -------------------------------------- | ------------------------------------------- | ---------------------------------- |
| Hardcoding API keys | Security risk, breaks when rotated | Use $ENV_VAR in all commands |
| Calling API without reading spec first | Wrong endpoint, wrong parameters | Discover first (Phase 1) |
| Passing full 100KB response to context | Context overflow, degraded performance | Truncate/extract (Phase 4) |
| No iteration guard on chained calls | Infinite loops burning tokens | Always enforce max_iterations |
| Guessing parameter names | 400 errors, wasted calls | Read spec/docs before constructing |
| Ignoring HTTP error codes | Silent failures, wrong results | Check status, handle 4xx/5xx |
| Using POST when GET is correct | API rejects or creates unintended resources | Match method to intent (Phase 2) |
DISCOVER → WebFetch(spec_url) or define manual tool template
MATCH → Map user intent to endpoint + method + params
CONSTRUCT → Build curl command with URL, headers, auth, body
EXECUTE → Bash(curl) for JSON, WebFetch for HTML/summarize
CHAIN → Re-analyze task, call again (max 5 iterations)
Auth Quick Reference:
API Key: -H "X-Api-Key: $KEY"
Bearer: -H "Authorization: Bearer $TOKEN"
Basic: -u "$USER:$PASS"
None: (no auth header needed)
Tool Template Quick Create:
{
"name": "...",
"description": "...",
"base_url": "...",
"method": "GET",
"auth": { "type": "api_key", "header": "...", "env_var": "..." },
"parameters": { "q": { "type": "string", "required": true, "in": "query" } }
}
This skill is adapted from:
auth-security-expert — OAuth 2.1, JWT, encryption patternsnodejs-expert — Node.js HTTP patternsdebugging — API call failure investigationresearch-synthesis — Researching new APIs before integrationBefore starting:
Read .claude/context/memory/learnings.md
After completing:
.claude/context/memory/learnings.md.claude/context/memory/issues.md.claude/context/memory/decisions.md.claude/context/memory/named/api-templates.mdAssume interruption: if it is not in memory, it did not happen.
tools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.