LLM-framework-agnostic skill search library with fast full-text search
Project description
skill-search
LLM-framework-agnostic skill search library.
Parses SKILL.md files in a skills directory, builds a fast full-text search index using tantivy (Rust-based), and provides tool definitions + execution handlers that work with any LLM library (OpenAI SDK, Anthropic SDK, LangChain, LiteLLM, etc.).
Installation
pip install skill-search
Usage
Basic
from skill_search import SkillSearch
# Initialize with skill directories
ss = SkillSearch(skills_dirs=["./skills"])
# Get tool definitions (OpenAI function calling format)
tools = ss.get_tool_definitions()
# Get system prompt with skill listing
system_prompt = ss.get_system_prompt()
# Execute tool calls from LLM
result = ss.call_tool("search_skills", {"query": "API reference", "top_k": 3})
OpenAI SDK
import json
from openai import OpenAI
from skill_search import SkillSearch
client = OpenAI()
ss = SkillSearch(skills_dirs=["./skills"])
messages = [
{"role": "system", "content": ss.get_system_prompt()},
{"role": "user", "content": "How do I use the Figma API?"},
]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=ss.get_tool_definitions(),
)
# Handle tool calls
for choice in response.choices:
if choice.message.tool_calls:
for tc in choice.message.tool_calls:
result = ss.call_tool(
tc.function.name,
json.loads(tc.function.arguments),
)
messages.append({"role": "tool", "content": result, "tool_call_id": tc.id})
Anthropic SDK
from anthropic import Anthropic
from skill_search import SkillSearch
client = Anthropic()
ss = SkillSearch(skills_dirs=["./skills"])
# Convert to Anthropic format
tools = [
{
"name": t["function"]["name"],
"description": t["function"]["description"],
"input_schema": t["function"]["parameters"],
}
for t in ss.get_tool_definitions()
]
response = client.messages.create(
model="claude-sonnet-4-20250514",
system=ss.get_system_prompt(),
messages=[{"role": "user", "content": "How do I search in Jira?"}],
tools=tools,
)
# Handle tool use
for block in response.content:
if block.type == "tool_use":
result = ss.call_tool(block.name, block.input)
Available Tools
| Tool | Description |
|---|---|
list_skills |
List all available skills |
read_skill |
Read full SKILL.md content |
search_skills |
Full-text search (BM25 via tantivy) |
read_resource |
Read supplementary resource files |
SKILL.md Format
---
name: my-skill
description: Brief description of the skill
---
# My Skill
## Usage
1. Step 1
2. Step 2
How It Works
- Discovery — Recursively scans for
SKILL.mdfiles and parses YAML frontmatter - Indexing — Splits documents into heading-level chunks and indexes with tantivy
- Search — BM25 scoring returns results ranked by relevance
- Tool Execution —
call_tool()executes LLM tool calls and returns results as strings
Security
LLM tool calls are treated as untrusted input and protected with defense-in-depth.
Path Traversal Prevention
| Layer | Location | Protection |
|---|---|---|
| Discovery | discover_resources() |
Symlinks resolved; paths outside skill directory excluded |
| Input validation | read_resource handler |
Resource names containing .. are rejected |
| Path resolution | read_resource handler |
Resolved paths verified to be within skill directory |
| Whitelist | read_resource handler |
Only pre-discovered resources are accessible |
Extension Filter
Only these file extensions are indexed as resources:
.md, .json, .yaml, .yml, .csv, .xml, .txt
Executable files (.py, .sh, .exe, etc.) and binaries are excluded.
Design Principles
- Read-only — No write or execute capabilities
- Whitelist-based — Only pre-validated resources are accessible
- Defense-in-depth — Input validation → path resolution → directory boundary check
Development
uv run pytest tests/ -v
License
MIT
Project details
Release history Release notifications | RSS feed
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 skill_search-0.1.0.tar.gz.
File metadata
- Download URL: skill_search-0.1.0.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b0c3113a33731cf0ba6cffc49898f169b84959073fb47eb8589a70eb256276d
|
|
| MD5 |
f47495ba368a0313f4ae0b0346b38316
|
|
| BLAKE2b-256 |
273960782fc9620e7eef3804a791c92ca714909bcfa659b6187fba6a29afd2d2
|
File details
Details for the file skill_search-0.1.0-py3-none-any.whl.
File metadata
- Download URL: skill_search-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
422bd992bae4260ce5d972a0d99bce6753818f757a59d63d1f0cd6dd2f3b7058
|
|
| MD5 |
897721f627c7d5cda8747c0a76f9b4a9
|
|
| BLAKE2b-256 |
bb1a5c834832ab0ce3f954aa36cd9ded9bbb0296c335af3263ad79b50df14207
|