Install
$ agentstack add skill-drknght4-hermes-sports-skill-hermes-sports-skill ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Sports Scores
Live scores, standings, odds, and F1 data from free public APIs plus The Odds API for betting lines.
API Keys
| API | Required | Location | |-----|----------|----------| | ESPN | No | — | | Ergast / OpenF1 | No | — | | The Odds API | Yes — for odds queries | ~/.hermes/skills/tools/sports-scores/.env → ODDS_API_KEY |
If ODDS_API_KEY is missing, odds commands will fail gracefully with a message to configure the key. Scores and standings work without it.
Installation
# From GitHub repo (recommended)
hermes skills install https://github.com/Drknght4/hermes-sports-skill
# Or manually
cp SKILL.md ~/.hermes/skills/tools/sports-scores/SKILL.md
Repo: github.com/Drknght4/hermes-sports-skill
Restart Hermes or reload skills — the skill activates automatically when sports questions are detected.
When to Use
- User asks about any sports score, game result, or standings
- User asks "how did [team] do" or "what's the score"
- User asks about F1 race results, driver standings, or next race
- User asks about betting odds, lines, spreads, or over/unders
- User says "odds on", "what are the odds", "betting line", "Vegas line", "spread"
- User says team name, sport name, or league name casually ("Knicks game", "F1 standings", "Premier League table")
API Endpoints
ESPN (no key required)
| Sport | League | Scores | Standings | |-------|--------|--------|-----------| | Basketball | NBA | https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard | https://site.api.espn.com/apis/site/v2/sports/basketball/nba/standings | | Football | NFL | https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard | https://site.api.espn.com/apis/site/v2/sports/football/nfl/standings | | Baseball | MLB | https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard | https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/standings | | Hockey | NHL | https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard | https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/standings | | Soccer | La Liga | https://site.api.espn.com/apis/site/v2/sports/soccer/esp.1/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/esp.1/standings | | Soccer | Premier League | https://site.api.espn.com/apis/site/v2/sports/soccer/eng.1/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/eng.1/standings | | Soccer | Champions League | https://site.api.espn.com/apis/site/v2/sports/soccer/UEFA.CHAMPIONS/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/UEFA.CHAMPIONS/standings | | Soccer | MLS | https://site.api.espn.com/apis/site/v2/sports/soccer/usa.1/scoreboard | — | | Soccer | Bundesliga | https://site.api.espn.com/apis/site/v2/sports/soccer/ger.1/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/ger.1/standings | | Soccer | Serie A | https://site.api.espn.com/apis/site/v2/sports/soccer/ita.1/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/ita.1/standings | | Soccer | Ligue 1 | https://site.api.espn.com/apis/site/v2/sports/soccer/fra.1/scoreboard | https://site.api.espn.com/apis/site/v2/sports/soccer/fra.1/standings | | Soccer | Liga MX | https://site.api.espn.com/apis/site/v2/sports/soccer/mex.1/scoreboard | — | | Soccer | Copa Libertadores | https://site.api.espn.com/apis/site/v2/sports/soccer/CONMEBOL.LIBERTADORES/scoreboard | — | | Soccer | FIFA World Cup | https://site.api.espn.com/apis/site/v2/sports/soccer/fifa.world/scoreboard | — | | Soccer | International Friendlies | https://site.api.espn.com/apis/site/v2/sports/soccer/fifa.friendly/scoreboard | — |
| MMA/UFC | UFC | https://site.api.espn.com/apis/site/v2/sports/mma/ufc/scoreboard | — | | Tennis | ATP/WTA | https://site.api.espn.com/apis/site/v2/sports/tennis/scoreboard | — | | Golf | PGA | https://site.api.espn.com/apis/site/v2/sports/golf/pga/scoreboard | — | | Rugby | Rugby | https://site.api.espn.com/apis/site/v2/sports/rugby/scoreboard | — | | Basketball | WNBA | https://site.api.espn.com/apis/site/v2/sports/basketball/wnba/scoreboard | — |
Ergast F1 API (no key required)
| Data | Endpoint | |------|-----------| | Last race results | https://ergast.com/api/f1/current/last/results.json | | Driver standings | https://ergast.com/api/f1/current/driverStandings.json | | Constructor standings | https://ergast.com/api/f1/current/constructorStandings.json | | Next race | https://ergast.com/api/f1/current/next.json | || Live timing | https://api.openf1.org/v1/position?session_key=latest |
The Odds API (API key required)
Base URL: https://api.the-odds-api.com Auth: apiKey query parameter (stored in ~/.hermes/skills/tools/sports-scores/.env as ODDS_API_KEY)
Key endpoint:
GET /v4/sports/{sport_key}/odds/?apiKey={KEY}®ions=us,eu&markets=h2h
Parameters:
regions—us,eu,uk,au, or comma-separated (default:us,eu)markets—h2h(moneyline),spreads,totals(over/under), or comma-separated (default:h2h)oddsFormat—decimal(default),americandateFormat—iso(default)
Available sports — fetch the full list with:
curl -s "https://api.the-odds-api.com/v4/sports/?apiKey=$ODDS_API_KEY"
Common sport keys for odds:
| Sport | Odds API Key | Notes | |-------|-------------|-------| | NFL | americanfootball_nfl | Active Sep–Feb | | NBA | basketball_nba | Active Oct–Jun | | MLB | baseball_mlb | Active Mar–Oct | | NHL | icehocey_nhl | Active Oct–Jun | | La Liga | soccer_spain_la_liga | In-season only (active=true) | | Premier League | soccer_epl | In-season only | | Champions League | soccer_uefa_champs_league | In-season only | | MLS | soccer_usa_mls | In-season only | | Bundesliga | soccer_germany_bundesliga | In-season only | | Serie A | soccer_italy_serie_a | In-season only | | Ligue 1 | soccer_france_ligue_one | In-season only | | Copa Libertadores | soccer_conmebol_copa_libertadores | Year-round | | FIFA World Cup | soccer_fifa_world_cup | Tournament only | | WNBA | basketball_wnba | Active May–Oct | | UFC/MMA | mma_mixed_martial_arts | Event-based |
Seasonality: Many sports return empty results ([]) when out of season. Always check the /v4/sports/ endpoint — the active field shows whether a sport currently has odds available.
Sport Detection
Map the user's question to the right endpoint:
|| Keywords | Sport | League Code | Odds API Key | ||----------|-------|-------------|-------------| || nba, basketball, knicks, lakers, celtics, warriors, bucks, etc. | Basketball | nba | basketball_nba | || nfl, football, superbowl, chiefs, 49ers, cowboys, patriots, etc. | Football | nfl | americanfootball_nfl | || mlb, baseball, yankees, dodgers, mets, red sox, etc. | Baseball | mlb | baseball_mlb | || nhl, hockey, rangers, avalanche, panthers, oilers, etc. | Hockey | nhl | icehocey_nhl | || la liga, barcelona, barca, real madrid, atletico, spanish league | Soccer | esp.1 | soccer_spain_la_liga | || premier league, epl, manchester, liverpool, arsenal, chelsea, english league | Soccer | eng.1 | soccer_epl | || champions league, ucl, champions | Soccer | UEFA.CHAMPIONS | soccer_uefa_champs_league | || mls, inter miami, lafc, american soccer | Soccer | usa.1 | soccer_usa_mls | || world cup, fifa, fifa world cup, mundial | Soccer | fifa.world | soccer_fifa_world_cup | || international friendly, friendlies, friendly match, morocco, norway, national team friendly | Soccer | fifa.friendly | — | || bundesliga, bayern, dortmund, german league | Soccer | ger.1 | soccer_germany_bundesliga | || serie a, ac milan, inter, juve, juventus, italian league | Soccer | ita.1 | soccer_italy_serie_a | || ligue 1, psg, paris saint-germain, french league | Soccer | fra.1 | soccer_france_ligue_one | || liga mx, mexican league, club america, chivas | Soccer | mex.1 | — | || copa libertadores, libertadores | Soccer | CONMEBOL.LIBERTADORES | soccer_conmebol_copa_libertadores | || ufc, mma, ufc fight night, octagon | MMA | ufc | mma_mixed_martial_arts | || tennis, atp, wta, wimbledon, roland garros, us open tennis, australian open | Tennis | tennis | — | || golf, pga, masters, pga tour | Golf | pga | — | || rugby, rugby world cup, six nations | Rugby | rugby | — | || wnba, women's basketball | Basketball | wnba | basketball_wnba | || f1, formula 1, formula one, grand prix, gp, race, verstappen, hamilton, leclerc, etc. | F1 | f1 | — |
Fetching Data
Always use terminal with curl + python3 for JSON parsing. Example patterns:
Scores (ESPN)
curl -s "https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for ev in d.get('events', []):
name = ev.get('name', '')
status = ev.get('status', {}).get('type', {}).get('description', '')
short_detail = ev.get('status', {}).get('type', {}).get('shortDetail', '')
for c in ev.get('competitions', []):
for comp in c.get('competitors', []):
team = comp.get('team', {}).get('abbreviation', '')
score = comp.get('score', '?')
home_away = 'HOME' if comp.get('homeAway') == 'home' else 'AWAY'
print(f'{home_away} {team}: {score}')
print(f' Status: {status} - {short_detail}')
print()
"
Standings (ESPN — NBA/NFL/MLB/NHL)
curl -s "https://site.api.espn.com/apis/site/v2/sports/basketball/nba/standings" | python3 -c "
import sys, json
d = json.load(sys.stdin)
# Standings structure varies by sport — parse entries appropriately
# NBA: entries[].teamStats[] with stats like wins, losses, gamesBehind, etc.
for entry in d.get('standings', {}).get('entries', [])[:10]:
team = entry.get('team', {})
abbr = team.get('abbreviation', '')
name = team.get('shortDisplayName', '')
stats = {}
for s in entry.get('stats', []):
stats[s.get('name')] = s.get('value')
wins = stats.get('wins', '?')
losses = stats.get('losses', '?')
pct = stats.get('winPercent', '?')
gb = stats.get('gamesBehind', '?')
print(f'{abbr:4} {name:20} {wins}-{losses} PCT: {pct} GB: {gb}')
"
Standings (ESPN — Soccer / La Liga / Premier League)
Soccer standings use a different structure — group entries with team stats inside.
curl -s "https://site.api.espn.com/apis/site/v2/sports/soccer/esp.1/standings" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for entry in d.get('standings', {}).get('entries', []):
team = entry.get('team', {})
name = team.get('shortDisplayName', team.get('displayName', ''))
abbr = team.get('abbreviation', '')
stats = {}
for s in entry.get('stats', []):
stats[s.get('name')] = s.get('value')
wins = stats.get('wins', '?')
losses = stats.get('losses', '?')
draws = stats.get('ties', stats.get('draws', '?'))
pts = stats.get('points', '?')
gd = stats.get('goalDiff', stats.get('goalDifference', '?'))
played = stats.get('gamesPlayed', stats.get('matchesPlayed', '?'))
pos = stats.get('rank', stats.get('position', '?'))
print(f'{str(pos):>2}. {name:2}. {name:2}: {driver:2}: {driver:2} Pts:{points:>4}')
"
F1 — Next Race
curl -s "https://ergast.com/api/f1/current/next.json" | python3 -c "
import sys, json
d = json.load(sys.stdin)
race = d['MRData']['RaceTable']['Races'][0]
print(f'Next: {race[\"raceName\"]}')
print(f'Date: {race[\"date\"]}')
print(f'Circuit: {race[\"Circuit\"][\"circuitName\"]}')
print(f'Location: {race[\"Circuit\"][\"Location\"][\"locality\"]}, {race[\"Circuit\"][\"Location\"][\"country\"]}')
"
F1 — Live Timing (during race sessions)
curl -s "https://api.openf1.org/v1/position?session_key=latest" | python3 -c "
import sys, json
d = json.load(sys.stdin)
# Sort by position
positions = sorted(d, key=lambda x: x.get('position', 999))
if not positions:
print('No live session currently active')
else:
session = positions[0].get('session_key', 'unknown')
print(f'Session: {session}')
for p in positions:
pos = p.get('position', '?')
driver = p.get('driver_number', '?')
print(f'P{pos}: #{driver}')
"
Odds — H2H / Moneyline (The Odds API)
# Read API key from skill .env
ODDS_API_KEY=$(grep ODDS_API_KEY ~/.hermes/skills/tools/sports-scores/.env | cut -d= -f2)
# Fetch odds for a sport (example: NBA)
curl -s "https://api.the-odds-api.com/v4/sports/basketball_nba/odds/?apiKey=${ODDS_API_KEY}®ions=us,eu&markets=h2h" | python3 -c "
import sys, json
d = json.load(sys.stdin)
if not d:
print('No upcoming odds found — sport may be off-season.')
for ev in d[:8]:
home = ev.get('home_team', '?')
away = ev.get('away_team', '?')
commence = ev.get('commence_time', '?')[:16]
print(f'{home} vs {away} ({commence})')
for bk in ev.get('bookmakers', [])[:3]:
for m in bk.get('markets', []):
if m['key'] == 'h2h':
prices = ' | '.join([f\"{o['name']}: {o['price']}\" for o in m.get('outcomes', [])])
print(f' {bk[\"key\"]}: {prices}')
print()
"
Odds — Spreads & Totals (The Odds API)
ODDS_API_KEY=$(grep ODDS_API_KEY ~/.hermes/skills/tools/sports-scores/.env | cut -d= -f2)
# Spreads (point spreads for US sports)
curl -s "https://api.the-odds-api.com/v4/sports/basketball_nba/odds/?apiKey=${ODDS_API_KEY}®ions=us&markets=spreads" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for ev in d[:5]:
home = ev.get('home_team', '?')
away = ev.get('away_team', '?')
print(f'{home} vs {away}')
for bk in ev.get('bookmakers', [])[:2]:
for m in bk.get('markets', []):
if m['key'] == 'spreads':
lines = ' | '.join([f\"{o['name']}: {o.get('point','?')} ({o['price']})\" for o in m.get('outcomes', [])])
print(f' {bk[\"key\"]}: {lines}')
print()
"
# Totals (over/under)
curl -s "https://api.the-odds-api.com/v4/sports/basketball_nba/odds/?apiKey=${ODDS_API_KEY}®ions=us&markets=totals" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for ev in d[:5]:
home = ev.get('home_team', '?')
away = ev.get('away_team', '?')
print(f'{home} vs {away}')
for bk in ev.get('bookmakers', [])[:2]:
for m in bk.get('markets', []):
if m['key'] == 'totals':
lines = ' | '.join([f\"{o['name']}: {o.get('point','?')} ({o['price']})\" for o in m.get('outcomes', [])])
print(f' {bk[\"key\"]}: {lines}')
print()
"
Odds — Check Active Sports
ODDS_API_KEY=$(grep ODDS_API_KEY ~/.hermes/skills/tools/sports-scores/.env | cut -d= -f2)
# List only currently active sports with odds
curl -s "https://api.the-odds-api.com/v4/sports/?apiKey=${ODDS_API_KEY}" | python3 -c "
import sys, json
sports = json.load(sys.stdin)
active = [s for s in sports if s.get('active')]
for s in sorted(active, key=lambda x: x['key']):
print(f\"{s['key']:<40} {s['title']}\")
print(f'Active: {len(active)} / {len(sports)}')
"
Odds — American Format
Append &oddsFormat=american to any odds URL to get American odds (e.g., -110, +150) instead of decimal (e.g., 1.91, 2.50).
ODDS_API_KEY=$(grep ODDS_API_KEY ~/.hermes/skills/tools/sports-scores/.env | cut -d= -f2)
curl -s "https://api.the-odds-api.com/v4/sports/basketball_nba/odds/?apiKey=${ODDS_API_KEY}®ions=us&markets=h2h&oddsFormat=american"
Output Rules
- Clean format. Never dump raw JSON. Parse and present human-readable summaries.
- Live games. If a game status is "In Progress" or similar, clearly note it: 🔴 LIVE.
- Scheduled games. Show date, time, and broadcast info.
- Completed games. Show final score with winner bold or marked.
- Standings. Top 10 only unless user asks
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Drknght4
- Source: Drknght4/hermes-sports-skill
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.