Skip to content
Musher Docs

Python SDK Reference

Complete API reference for the musher-sdk package. The SDK requires Python 3.13+.

Module-Level Functions

pull(ref: str) -> Bundle

Resolve, fetch, and verify a bundle. Returns a Bundle with typed access to its contents. Uses the global configuration set via configure().

pull_async(ref: str) -> Bundle

Async version of pull().

resolve(ref: str) -> ResolveResult

Resolve a bundle reference to its manifest metadata without fetching asset content. Useful for inspecting versions, listing assets, or validating a reference.

resolve_async(ref: str) -> ResolveResult

Async version of resolve().

configure(**kwargs) -> None

Set global SDK configuration. Accepts any MusherConfig field as a keyword argument.

python
import musher

musher.configure(
    token="mush_...",           # API token (auto-discovered if omitted)
    verify_checksums=True,     # default
    timeout=30.0,              # seconds
    max_retries=2,
)

get_config() -> MusherConfig

Retrieve the active global configuration with auto-discovered credentials.

resolve_registry_url() -> str

Get the registry URL from the MUSHER_API_URL environment variable, or return the default (https://api.musher.dev).

cache_info() -> CacheInfo

Return a snapshot of the local cache contents including path, total size, bundle count, and per-bundle version details. Does not require authentication.

cache_path() -> Path

Return the active cache directory path.

cache_remove(ref: str) -> None

Remove all cached data for a specific bundle reference.

cache_clear() -> None

Delete the entire cache directory.

cache_clean() -> int

Remove expired manifests and refs, then garbage-collect unreferenced blobs. Returns the number of removed entries.

Client Classes

Client

Synchronous bundle registry client. Uses a background event loop internally, so it works in notebooks and other environments where an event loop is already running.

python
from musher import Client

with Client() as client:
    bundle = client.pull("acme/code-review-kit")
    result = client.resolve("acme/code-review-kit:^1.0")

Constructor

Client(config: MusherConfig | None = None)

If config is None, uses the global configuration from configure() with automatic credential discovery.

Methods

MethodReturnsDescription
pull(ref)BundleResolve, fetch, and verify a bundle
resolve(ref)ResolveResultResolve metadata without fetching
fetch_asset(logical_path, *, namespace, slug, version=None)AssetFetch a single asset by logical path
cache_info()CacheInfoInspect cached bundles and sizes
cache_path()PathGet cache directory path
cache_remove(ref)Remove a bundle from cache
cache_clear()Clear entire cache
cache_clean()Remove expired entries and GC blobs
close()Release resources and stop background loop

Supports context manager protocol (with Client() as client:).

AsyncClient

Asynchronous bundle registry client with the same method signatures as Client.

python
from musher import AsyncClient

async with AsyncClient() as client:
    bundle = await client.pull("acme/code-review-kit")

Supports async context manager protocol (async with AsyncClient() as client:). Same methods as Client, all returning awaitables.

Bundle

A resolved bundle with its fetched assets. Returned by pull() and client.pull().

Typed Accessors

MethodReturnsDescription
files()list[FileHandle]All files in the bundle
file(logical_path)FileHandle | NoneSingle file by logical path
skills()list[SkillHandle]All skills in this bundle
skill(name)SkillHandleSingle skill by name (raises KeyError)
prompts()list[PromptHandle]All prompts in this bundle
prompt(name)PromptHandleSingle prompt by name (raises KeyError)
toolsets()list[ToolsetHandle]All toolsets in this bundle
toolset(name)ToolsetHandleSingle toolset by name (raises KeyError)
agent_specs()list[AgentSpecHandle]All agent specs in this bundle
agent_spec(name)AgentSpecHandleSingle agent spec by name (raises KeyError)

Selection

select() returns a BundleSelection — a filtered view with the same accessor methods as Bundle.

python
subset = bundle.select(
    skills=["code-review"],
    prompts=["system"],
    toolsets=["lint-rules"],
    agent_specs=["reviewer"],
)

for skill in subset.skills():
    print(skill.name)

Export Methods

MethodReturnsDescription
export_claude_plugin(plugin_name, skills=None, dest=None)ClaudePluginExportExport as a Claude plugin directory
install_claude_skills(dest, skills=None, *, clean=False)Install skills to a Claude skills directory

Handle Types

FileHandle

MemberTypeDescription
.logical_pathstrPath within the bundle
.media_typestr | NoneMIME type, if known
.text(encoding="utf-8")strDecode content as text
.bytes()bytesRaw content bytes

SkillHandle

MemberTypeDescription
.namestrSkill name
.descriptionstrSkill description
.root_pathstrRoot path within the bundle
.files()list[FileHandle]All files in this skill
.file(relative_path)FileHandle | NoneSingle file by relative path
.skill_md()FileHandleThe SKILL.md file
.export_openai_local_skill(dest=None)OpenAILocalSkillExport for local OpenAI use
.export_openai_inline_skill()OpenAIInlineSkillExport as base64 zip for OpenAI
.export_path(dest=None)PathWrite skill files to a directory
.export_zip(dest=None)PathWrite skill files to a zip archive

PromptHandle

MemberTypeDescription
.namestrPrompt name
.fileFileHandleUnderlying file handle
.text()strPrompt text content

ToolsetHandle

MemberTypeDescription
.namestrToolset name
.fileFileHandleUnderlying file handle
.text()strToolset text content
.parse_json()dict[str, object]Parse content as JSON

AgentSpecHandle

MemberTypeDescription
.namestrAgent spec name
.fileFileHandleUnderlying file handle
.text()strAgent spec text content
.parse_json()dict[str, object]Parse content as JSON

Export Result Types

Frozen dataclasses returned by export methods.

ClaudePluginExport

  • .plugin_name: str — Name of the exported plugin
  • .path: Path — Directory where plugin files were written

OpenAILocalSkill

  • .name: str
  • .description: str
  • .path: Path — Directory where skill files were written
  • .to_dict() -> dict[str, str] — Serialize for tool registration

OpenAIInlineSkill

  • .name: str
  • .description: str
  • .content_base64: str — Base64-encoded zip content
  • .to_dict() -> dict[str, object] — Generate OpenAI inline skill format

Cache Info Types

Frozen dataclasses returned by cache_info().

CacheInfo

FieldTypeDescription
pathPathCache directory path
total_size_bytesintTotal cache size in bytes
bundle_countintNumber of cached bundles
version_countintNumber of cached versions
blob_countintNumber of content-addressable blobs on disk
bundlestuple[CachedBundle, ...]Per-bundle details

CachedBundle

FieldTypeDescription
namespacestrBundle namespace
slugstrBundle slug
hoststrRegistry host
versionstuple[CachedBundleVersion, ...]Cached versions
total_size_bytesintTotal size of this bundle's cached data in bytes

CachedBundleVersion

FieldTypeDescription
versionstrSemver version string
size_bytesintVersion size in bytes
fetched_atdatetime | NoneWhen this version was fetched from the registry
is_freshboolWhether the cached entry is within its TTL

Configuration

MusherConfig

FieldTypeDefaultDescription
tokenstr | NoneNoneAPI token — if unset, auto-discovered
registry_urlstr"https://api.musher.dev"OCI registry URL
verify_checksumsboolTrueVerify SHA-256 content integrity on pull
timeoutfloat30.0Request timeout in seconds
max_retriesint2Number of retries for failed requests
cache_dirPathAutoBundle cache directory
config_dirPathAutoConfiguration directory
data_dirPathAutoData directory (credentials, etc.)
state_dirPathAutoState directory

Credential Discovery

When token is not set, MusherConfig.resolve_token() checks these sources in order:

  1. MUSHER_API_KEY environment variable
  2. OS keyring (musher/<host>)
  3. Credential file at <data_dir>/credentials/<host_id>/api-key (mode 0600)

BundleRef

Parsed bundle reference with slots. Frozen dataclass.

FieldTypeDescription
namespacestrOrganization or user namespace
slugstrBundle slug
versionstr | NoneSemver version or range
digeststr | NoneContent digest for pinning

BundleRef.parse(ref: str) -> BundleRef — Parse a reference string like "acme/code-review-kit:2.0.0".

Exceptions

All exceptions inherit from MusherError.

ExceptionWhen
MusherErrorBase class for all SDK errors
AuthenticationErrorAuthentication failed or token is invalid
BundleNotFoundErrorRequested bundle does not exist
VersionNotFoundErrorRequested version does not exist
IntegrityErrorSHA-256 checksum verification failed (includes expected and actual hashes)
RegistryErrorFailed to communicate with OCI registry
CacheErrorProblems accessing local bundle cache
RateLimitErrorAPI rate limit exceeded (may include retry timing)
APIErrorRFC 9457 Problem Details response (captures status, title, detail, type URI)
python
from musher import pull, BundleNotFoundError, IntegrityError

try:
    bundle = pull("acme/code-review-kit:2.0.0")
except BundleNotFoundError:
    print("Bundle not found")
except IntegrityError as e:
    print(f"Checksum mismatch: expected {e.expected}, got {e.actual}")

Enums

String enums (all inherit from StrEnum).

AssetType

AGENT_SPEC, SKILL, TOOLSET, PROMPT, CONFIG, OTHER

BundleVisibility

PRIVATE, PUBLIC

BundleVersionState

PUBLISHED, YANKED

BundleSourceType

CONSOLE, REGISTRY