This release is a pre-release and may not be stable for production use.
cliver-core
The embeddable agent core behind CLIver: a Re-Act loop with tool calling, per-purpose model bindings, media capabilities, and first-class MCP support.
No CLI, no database, no config files — you pass Python objects in and get messages back.
Install
pip install cliver-core==0.3.0a4 # core library + MCP client (stdio / streamable HTTP)
pip install "cliver-core[local]==0.3.0a4" # + in-process local embedding / rerank models
The project is on pre-releases, so pin the exact version (an exact pin selects the pre-release
without --pre). Python 3.10+.
Quick start
import asyncio
from cliver_core import AgentCore, ModelBinding, ModelBindings
async def main():
agent = AgentCore(
ModelBindings(
llm=ModelBinding.llm(
model="deepseek-v4-flash",
api_key="...",
base_url="https://api.deepseek.com",
)
)
)
response = await agent.chat("Summarize the Re-Act pattern in one sentence.")
print(response.message.content)
await agent.aclose()
asyncio.run(main())
Ready-made bindings for live-verified providers live in cliver_core.presets:
from cliver_core import ModelBindings, presets
bindings = ModelBindings(llm=presets.deepseek(api_key="..."))
agent = AgentCore(bindings)
Tools
cliver_core.tools ships nine file, shell, todo, and web tools. Register them directly:
from cliver_core.tools import discover_builtin_tools
agent = AgentCore(bindings, builtin_tools=discover_builtin_tools())
ToolRegistry and the @tool decorator build custom tools.
Capabilities
Every purpose works standalone — no AgentCore required:
from cliver_core import ModelBinding
from cliver_core.capabilities import embed, generate_image
vectors = await embed(
texts=["hello", "world"],
binding=ModelBinding.embed(model="text-embedding-3-small", api_key="...", base_url="..."),
)
image = await generate_image(
prompt="a red panda on a skateboard",
binding=ModelBinding.image(model="gpt-image-1", api_key="...", base_url="..."),
)
Purposes: image, video, text-to-speech, speech-to-text, embed, rerank. Long generations can return
a MediaJob future via AgentCore's submit_* variants.
Local models
Run embedding and rerank models in-process — no endpoint. The backends pull torch, so they are opt-in:
pip install "cliver-core[local-st]" # sentence-transformers embeddings
pip install "cliver-core[local-flag]" # FlagEmbedding embeddings + rerank
pip install "cliver-core[local]" # both
from cliver_core.capabilities import embed, rerank
from cliver_core.local import (
LocalEmbeddingProvider,
LocalRerankProvider,
local_embed,
local_rerank,
)
embedder = LocalEmbeddingProvider(
model="BAAI/bge-base-zh-v1.5", # Hugging Face id or local directory
backend="sentence-transformers", # required; never inferred from the model name
)
reranker = LocalRerankProvider(
model="BAAI/bge-reranker-v2-m3",
backend="flag-embedding",
)
vectors = await embed(
texts=["hello", "world"],
binding=local_embed(provider=embedder, normalize=True),
)
items = await rerank(
query="hello",
documents=["hi", "hey"],
binding=local_rerank(provider=reranker, top_k=1),
)
await embedder.close()
await reranker.close()
A provider owns one loaded model and is meant to be long-lived: create it once, share it across
bindings and calls, and close it at shutdown. There is no process-global model cache.
ModelBinding.aclose() never closes a provider you supplied.
The API is deliberately explicit: model, backend, device, and dtype are parameters, not
name-based defaults. Call options are generic — dim (optional output-size check), normalize
(off by default; raw vectors/scores), batch_size, and top_k (rerank). Query/document prefixes
and instruction templates belong to the application.
provider.info() reports the resolved model directory, revision, device, dtype, and load time.
Hugging Face downloads honor HF_TOKEN, cache_dir, revision, and local_files_only.
MCP
from cliver_core.mcp import MCPClient
client = MCPClient.from_discovery() # ~/.cliver/mcp.json, .cliver/mcp.json, drop-ins
agent = AgentCore(bindings, mcp_client=client)
MCP tools are advertised to the model as mcp__<server>__<tool>. Prompts and resources are read on
demand — list_prompts, get_prompt, list_resources, read_resource — never registered as
tools.
Skills
Agent Skills: a skill is a directory with a SKILL.md
(frontmatter + instructions) plus optional scripts/, references/, assets/. cliver-core reads
skills from explicit sources the application supplies — a directory of skill directories or a
single skill directory; nothing is discovered implicitly, and later sources override earlier ones
by name.
from cliver_core import AgentCore, SkillManager
skills = SkillManager(sources=["./skills"]) # routing metadata: skills.list_skills()
agent = AgentCore(bindings, builtin_tools=[...], skill_manager=skills)
result = await agent.run_skill("Refund", "订单 12345 想退货", conversation=history)
run_skill (name or an already-loaded Skill object) appends an activation header — skill
name and location, so bundled scripts/ are runnable — plus the skill body to the single
system message and runs the normal Re-Act loop; a declared allowed-tools frontmatter narrows
the advertised tools to that set — a non-empty allowlist matching zero available tools fails
fast, while an absent or empty list leaves tools unchanged. The other call parameters mirror
chat() (except tool_filter and skill_dispatch). run_skill() adds none of the
model-driven skill tools (FindSkills, ActivateSkill, ReadSkillFile); an allowed-tools
entry naming one of those is unavailable in this path.
Progressive disclosure: list_skills() exposes only routing metadata (name + description —
format_skill_catalog() renders the catalogue for prompts); the body enters context only when a
skill is activated; bundled files are listed (skill.files) and readable on demand
(skill.read_file("references/policy.md"), path escapes rejected).
With a SkillManager configured, chat()/stream() give the model that catalogue in the system
prompt (skill_summary_limit, default 10), FindSkills(query, limit) to search the full
catalogue, and ActivateSkill(name) to load a skill's body as a tool result for the rest of the
run. Activation is one skill at a time; a declared allowed-tools narrows the advertised tools
for the remaining iterations, and an activated skill that bundles files also gets
ReadSkillFile(path) — confined to that skill's directory (escapes refused, UTF-8 text, 256 KiB
cap), while workspace files stay on Read. Apps may rank the catalogue with
skill_selector(skills, user_input), replace lexical search with
skill_search(query, skills), and narrow both candidate pools with
candidate_selector(tools, skills, user_input) / tool_selector(tools, user_input).
run_skill() disables dispatch for its run.
The dependency-free frontmatter parser covers the spec's surface plus what cross-agent skills
(workbuddy, accio, ...) carry — flat scalars, quoted strings, > / | blocks, and nested maps
of any depth (unknown keys are kept in skill.frontmatter for forward compatibility; no anchors
or - sequences). Validation follows the agentskills.io rules (name
pattern, 64/1024/500-char limits) as load-time warnings; skills stay loadable unless name or
the frontmatter itself is missing. (Anthropic's product-specific extras — reserved words in
names — are not enforced.) Script execution sandboxes remain future work.
Permissions & execution policy
Two independent axes, both opt-in — with neither set, every tool runs exactly as before.
Permission axis (may this tool run?). Every tool carries a category: SAFE or ASK.
Read-only builtins (Read, LS, Grep, TodoRead/TodoWrite) are SAFE; mutating ones
(Write, Bash, Exec, WebFetch) are ASK by default. MCP tools map the spec's
self-declared readOnlyHint annotation to SAFE — a hint, never a permission boundary.
An application-supplied async permission_callback is consulted before any ASK tool runs:
from cliver_core import AgentCore, Decision, EnvSpec, ExecutionPolicy, SAFE_ENV_VARS, ToolCategory
async def ask_user(ctx) -> Decision: # ctx: tool_name, args, server, category
answer = await my_ui.confirm(f"Run {ctx.tool_name}?")
return Decision.allow() if answer else Decision.deny("user said no")
agent = AgentCore(
bindings,
builtin_tools=[...],
permission_callback=ask_user, # None → ASK tools run unchecked
execution_policy=ExecutionPolicy( # environment axis, ambient per call
env=EnvSpec(mode="allowlist", names=set(SAFE_ENV_VARS)),
cwd=project_root,
write_paths=[project_root / "src"],
),
safety_overrides={"mcp__github__create_issue": ToolCategory.ASK},
)
A denial never raises: it returns {"error": "Permission denied: …", "denied": true} to the
model through the normal tool-result path and also emits ToolEventType.PERMISSION_DENIED
through on_event before the call's TOOL_END. Callback exceptions fail closed. Denials share
the consecutive cap with errors (5 in a row stops the loop). safety_overrides reclassify
individual tools per AgentCore instance — builtins are shared singletons and never mutated.
Environment axis (where does it run, what does it see?). Pass execution_policy= to the
constructor or per chat()/stream() call (call-site wins). While ambient: Bash and Exec
run with the policy env (allowlist/denylist/inherit) and pinned cwd — Bash logs an INFO
line when the pin overrides its directory argument; Read/LS/Grep/Write enforce
read_paths/write_paths (resolved against the policy cwd, symlinks followed). run_skill
applies a call-site policy verbatim; otherwise an agent-level policy is reused with cwd pinned
to the skill's directory.
The two axes are complementary, not a hard sandbox: read_paths/write_paths bind the file
tools only — an approved Bash/Exec call runs as your process user and can touch anything
that user can, under the filtered env and pinned working directory. Use the permission axis
(callback, safety_overrides) to decide whether shell access is granted at all; the paths are
a guardrail against accidental access, not an OS-level confinement.
License
Apache-2.0
Metadata
Release files for cliver-core 0.3.0a4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cliver_core-0.3.0a4.tar.gz | 199.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cliver_core-0.3.0a4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 361.4 kB
Release files / cliver_core-0.3.0a4.tar.gz
| Download URL | cliver_core-0.3.0a4.tar.gz |
|---|---|
| Size | 199.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e8d3177b6f85aa05d69ad0f81fd8ac5d748076cd75526157af02482635b4bdf8
|
|
BLAKE2b-256 checksum How to use checksums |
65f21f80b85d2f6a7f171a04a4be03e6de468ccd666990e80dc7a44fec8b795f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.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}
|
Release files / cliver_core-0.3.0a4-py3-none-any.whl
| Download URL | cliver_core-0.3.0a4-py3-none-any.whl |
|---|---|
| Size | 162.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1c279fa7ff2203e48fbda21b55453d8b785a43873f26daa45bc3e5fae25f2852
|
|
BLAKE2b-256 checksum How to use checksums |
1a937f1e0b7413253e5b331e0cb7ac1826bb2df05d69d88c14072cca89779614
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.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}
|
Security for cliver-core 0.3.0a4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.