Connecting External APIs
Build skills that connect to external services safely. Learn credential isolation, API authentication patterns, and the cardinal rules of API security for agent skills.
Premium Course Content
This lesson is part of a premium course. Upgrade to Pro to unlock all premium courses and content.
- Access all premium courses
- 1000+ AI skill templates included
- New content added weekly
The 7.1% Problem
🔄 Quick Recall: In the last lesson, you learned shell expansion — running commands that inject live data into your skill. Now we’re connecting to external services. And this is where most skill creators make their first serious security mistake.
Snyk’s research found that 283 ClawHub skills — 7.1% of the marketplace — were leaking API keys and personally identifiable information directly in their SKILL.md files or bundled code. The Register confirmed that skills contain “API keys stored in plaintext within skill configurations.”
These weren’t malicious skills. They were well-intentioned creators who hardcoded their credentials for convenience. The result: anyone who downloaded the skill got the creator’s API keys.
By the end of this lesson, you’ll be able to:
- Connect skills to external APIs without exposing credentials
- Choose the right authentication method for your use case
The Cardinal Rules of API Security in Skills
Before we write any code, internalize these four rules:
Rule 1: Never put credentials in SKILL.md. Not in the frontmatter, not in the instructions, not in comments, not “temporarily.”
Rule 2: Never put credentials in bundled files. Not in scripts, not in config files, not in assets. If it’s in the skill folder, assume it will be shared.
Rule 3: Use environment variables. Store credentials in .env files that are excluded from version control via .gitignore.
Rule 4: Prefer short-lived tokens. OAuth tokens that expire in an hour are vastly safer than API keys that last forever.
✅ Quick Check: You’re building a skill that needs a GitHub personal access token. Where do you store it? (Answer: In an environment variable like
GITHUB_TOKENdefined in a.envfile. The.envfile should be in.gitignore. The skill references$GITHUB_TOKENbut never contains the actual value.)
Pattern 1: Environment Variable References
The simplest and most common pattern. Your skill references environment variables, and the user sets them up locally.
In the SKILL.md:
# Weather Briefing Skill
Fetch the weather forecast using the OpenWeatherMap API.
## Setup
The user must set these environment variables:
- WEATHER_API_KEY — OpenWeatherMap API key
- WEATHER_CITY — Default city (e.g., "New York")
## Instructions
Use the weather API to fetch the 3-day forecast:
- Endpoint: https://api.openweathermap.org/data/2.5/forecast
- Authentication: Pass the API key as a query parameter 'appid'
- Include temperature, conditions, and rain probability
In the user’s .env file (NOT in the skill folder):
WEATHER_API_KEY=abc123def456
WEATHER_CITY=San Francisco
The skill tells the agent how to use the API. The credentials live in the user’s environment, never touching the skill files.
Pattern 2: Script-Based API Calls
For complex API interactions, bundle a script that handles the messy parts:
project-stats/
├── SKILL.md
└── scripts/
└── fetch_stats.py
SKILL.md:
# Project Stats
When the user asks for project statistics, run the fetch script:
!python scripts/fetch_stats.py
Present the results in a clear summary table.
scripts/fetch_stats.py:
import os
import json
import urllib.request
api_key = os.environ.get("GITHUB_TOKEN")
if not api_key:
print("ERROR: GITHUB_TOKEN not set. Run: export GITHUB_TOKEN=your_token")
exit(1)
repo = os.environ.get("GITHUB_REPO", "owner/repo")
url = f"https://api.github.com/repos/{repo}"
req = urllib.request.Request(url, headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/vnd.github.v3+json"
})
with urllib.request.urlopen(req) as response:
data = json.loads(response.read())
print(f"Stars: {data['stargazers_count']}")
print(f"Forks: {data['forks_count']}")
print(f"Open Issues: {data['open_issues_count']}")
The script reads credentials from environment variables, makes the API call, and outputs clean data. The agent sees only the output — never the credentials.
Pattern 3: MCP Server Integration
For persistent service connections, MCP (Model Context Protocol) servers handle authentication and provide tools the agent can call:
# Jira Task Manager
This skill uses the Jira MCP server for task management.
## Prerequisites
- Jira MCP server must be configured and running
- No additional credentials needed in this skill
## Instructions
When the user asks about tasks or issues:
1. Use the Jira MCP tool to search for matching issues
2. Format results as a table with: Key, Summary, Status, Assignee
3. For creation requests, use the Jira MCP create_issue tool
With MCP, the authentication layer is separate from the skill. The skill just says “use the Jira tool” — how that tool authenticates is handled by the MCP server configuration.
Authentication Methods Compared
| Method | Security | Complexity | Best For |
|---|---|---|---|
| Environment variables | Medium | Low | Personal skills, local use |
| OAuth 2.0 | High | Medium | Production, team use, sensitive APIs |
| MCP servers | High | Medium-High | Persistent service integrations |
API keys in .env | Medium | Low | Quick prototyping (rotate regularly) |
| Hardcoded in skill | Dangerous | Lowest | Never do this |
The decision tree:
- Personal use only? → Environment variables
- Shared with a team? → OAuth or MCP
- Connecting to a persistent service? → MCP server
- Just prototyping? →
.envfile with quick rotation plan
✅ Quick Check: Your skill calls a paid API that charges per request. Why is
disable-model-invocation: trueimportant here? (Answer: Without it, the agent might invoke the skill on its own whenever it thinks it’s relevant — racking up API charges. With the flag set, only the user can trigger it, keeping costs controlled.)
Error Handling in API Skills
APIs fail. Tokens expire. Rate limits hit. Your skill should handle these gracefully:
## Error Handling
If the API call fails:
- **401 Unauthorized:** Tell the user their API key may be expired or invalid
- **429 Rate Limited:** Wait 60 seconds and retry once. If still limited, inform the user
- **500 Server Error:** Tell the user the service is temporarily unavailable
- **Network Error:** Check if the user has internet connectivity
Never retry more than twice. Never expose the raw error response to the user
(it may contain credential information in headers).
That last rule matters: raw API error responses sometimes include authentication headers, request tokens, or other sensitive data in their bodies. Always instruct the agent to summarize errors, not pass them through raw.
Key Takeaways
- Never hardcode credentials — 7.1% of ClawHub skills leak API keys this way
- Use environment variables for personal skills, OAuth or MCP for shared/production
- OAuth tokens expire (~1 hour), making them safer than static API keys
- Scripts handle complexity — put API logic in
scripts/, keep SKILL.md focused on behavior - MCP servers separate auth from skills — the skill says what to do, MCP handles how to connect
- Handle errors gracefully and never expose raw API responses (they may contain credentials)
Up Next
You’ve built skills that take input, read live data, and connect to APIs. But how do you know they work correctly? In the next lesson, you’ll learn to test and debug skills using Promptfoo for output quality and Cisco Skill Scanner for security — before your users find the bugs.
Knowledge Check
Complete the quiz above first
Lesson completed!