Python SDK
The Musher Python SDK lets you pull bundles, inspect manifests, and access typed assets from Python applications. It provides both synchronous and asynchronous clients with automatic credential discovery, SHA-256 verification, and local caching.
Prerequisites
- Python 3.13 or later
- A Musher API key — create one in the Console
Installation
Install the package
pip install musher-sdkSet your API key
export MUSHER_API_KEY="mush_..."Authentication
The SDK discovers credentials automatically, checking these sources in order:
- Programmatic — pass a token directly via
configure()or the client constructor MUSHER_API_KEYenvironment variable- OS keyring (
musher/<host>) - Credential file (
~/.local/share/musher/credentials/<host>/api-key)
To pass a token programmatically:
import musher
musher.configure(token="mush_...")Pull Your First Bundle
The pull() function downloads a bundle from the registry and returns a Bundle object with typed access to its contents.
import musher
bundle = musher.pull("acme/code-review-kit")
for fh in bundle.files():
print(f"{fh.logical_path} ({fh.media_type or 'unknown'})")Work with Typed Handles
Bundles provide typed handles for each asset category. Each handle gives you structured access to the asset content and metadata.
import musher
bundle = musher.pull("acme/code-review-kit")
# Access skills
for skill in bundle.skills():
print(f"Skill: {skill.name}")
print(skill.skill_md().text())
# Access prompts
for prompt in bundle.prompts():
print(f"Prompt: {prompt.name}")
print(prompt.text())
# Access toolsets
for toolset in bundle.toolsets():
print(f"Toolset: {toolset.name}")
print(toolset.parse_json())
# Access all files
for fh in bundle.files():
print(f"{fh.logical_path} ({fh.media_type or 'unknown'})")Resolve Without Pulling
Use resolve() to inspect a bundle's metadata without downloading its full contents. This
is useful for checking versions, listing assets, or validating a reference.
import musher
result = musher.resolve("acme/code-review-kit:^1.0")
if result.manifest:
for layer in result.manifest.layers:
print(f"{layer.logical_path} ({layer.asset_type})")Sync vs Async
The SDK provides both synchronous and asynchronous clients. Use the async variants in applications
that already use asyncio.
Synchronous
from musher import Client, pull
# Using the convenience function
bundle = pull("acme/code-review-kit")
# Using the client directly
with Client() as client:
bundle = client.pull("acme/code-review-kit")Asynchronous
from musher import AsyncClient, pull_async
# Using the convenience function
bundle = await pull_async("acme/code-review-kit")
# Using the client directly
async with AsyncClient() as client:
bundle = await client.pull("acme/code-review-kit")Key Exports
| Export | Description |
|---|---|
pull() / pull_async() | Pull a bundle by reference |
resolve() / resolve_async() | Resolve metadata without downloading |
configure() | Set global SDK configuration (token, registry URL, cache, timeouts) |
Client / AsyncClient | Sync and async bundle registry clients |
Bundle | Loaded bundle with typed asset access via methods |
ResolveResult | Manifest metadata returned by resolve() |
MusherConfig | Configuration with credential discovery and cache settings |
BundleRef | Parsed bundle reference (namespace, slug, version, digest) |
| Handle types | FileHandle, SkillHandle, PromptHandle, ToolsetHandle, AgentSpecHandle |
| Export types | ClaudePluginExport, OpenAILocalSkill, OpenAIInlineSkill |
MusherError | Base exception — see Reference for full hierarchy |