bcli
A Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs, with agent-friendly endpoint discovery, browser auth, custom API registries, and a built-in dlt source for ETL backup pipelines.
Status: Alpha (0.1.x). Public surface may change before 1.0. Track CHANGELOG.md for breaking changes. Independent project by @igor-ctrl — not affiliated with Microsoft.
SDK Quick Start
from bcli import AsyncBCClient
# Programmatic auth — no config files needed
async with AsyncBCClient(
tenant_id="your-tenant-id",
client_id="your-app-id",
client_secret="your-secret",
environment="Sandbox",
company_id="your-company-id",
) as client:
# Query with fluent OData builder
customers = await client.query("customers").filter("city eq 'Chicago'").top(10).get()
# Write with safety gate
async with client.safe_write("Sandbox", "your-company-id") as sw:
await sw.post("salesInvoices", body={"customerNumber": "10000"}, domain="finance")
Or use TOML profiles for the CLI and repeated SDK use:
from bcli import AsyncBCClient
async with AsyncBCClient(profile="production") as client:
vendors = await client.query("vendors").select("displayName", "balance").get()
CLI Quick Start
# Install (PyPI distribution name is "bc-cli"; the binary is "bcli")
pip install bc-cli
# or
uv tool install bc-cli
# Configure with browser auth (no client secret)
bcli config init
# Query standard APIs immediately
bcli get customers --top 5
bcli get vendors --filter "displayName eq 'Fabrikam'" --format json
bcli get salesInvoices --select number,totalAmountIncludingTax --top 10
# Import custom APIs from a Postman collection
bcli registry import --from-postman ./my_collection.json
# Query custom endpoints (route auto-resolved)
bcli get myCustomEntities --top 5
Features
- Works out of the box — 79 standard BC v2.0 entities (customers, vendors, items, GL entries, ...) with zero configuration beyond auth
- Custom API support — Import your custom API pages from Postman collections, JSON, or live
$metadata - Three-tier endpoint resolution — Custom registry -> standard v2.0 -> fuzzy suggestions
- Multi-company — Assign aliases to companies and query across all entities
- OData query builder —
--filter,--select,--expand,--orderby,--top,--skipon every query - Multiple output formats — table, JSON, CSV, NDJSON for pipeline use
- Secure auth — Browser PKCE by default, OS keychain support for automation secrets, token caching, client credentials + device code fallback
- Write safety — SafeContext gate prevents wrong-environment writes, enforces draft status on financial documents
- Programmatic auth — Pass credentials directly for MCP servers, Airflow DAGs, and containers (no config files required)
- Batch operations — Execute sequences of API calls from YAML files
- ETL pipeline — Built-in dlt source for incremental backup to Parquet / DuckDB / Iceberg / Postgres
- Structured logging — JSON request logs with correlation IDs for observability
ETL Pipeline (dlt source)
Sync BC data incrementally to any dlt-supported destination — useful as a Fivetran backup, a warehouse backfill tool, or a standalone ETL runner.
Standalone (any BC tenant, no bcli config required):
import dlt
from bcli.etl import business_central, EntityDef, fivetran_stamper
source = business_central(
tenant_id="...", client_id="...", client_secret="...",
environment="Production",
entities=[
EntityDef(name="customers"),
EntityDef(name="vendors"),
],
multi_company=True, # iterate all BC companies
stampers=[fivetran_stamper()], # add _fivetran_synced / _fivetran_deleted
)
pipeline = dlt.pipeline(destination="duckdb", dataset_name="bc_raw")
pipeline.run(source)
Or use the bcli bridge (reuses your profile and custom-API registry):
# List entities the pipeline will sync
bcli --profile prod etl entities
# Incremental sync (uses systemModifiedAt cursor)
bcli --profile prod etl sync --destination filesystem
# Full refresh
bcli --profile prod etl sync --destination filesystem --full-refresh
# Schedule via cron (every 10 min incremental, nightly full refresh)
*/10 * * * * bcli --profile prod etl sync --destination filesystem
0 0 * * * bcli --profile prod etl sync --destination filesystem --full-refresh
Installation
Requires Python 3.11+.
Heads-up on the package name. The PyPI name is
bc-cli, notbcli. An unrelated 2018-era package squats on thebcliname (an "EC2 Cluster Creator" with no relation to this project) —pip install bcliwill install that, not this. Alwayspip install bc-cli. Once installed, the import name (import bcli) and the CLI binary (bcli) work as documented.
# CLI + SDK
pip install bc-cli
# SDK + ETL (dlt source for backup pipelines)
pip install "bc-cli[etl]"
# Everything
pip install "bc-cli[etl,mcp,telemetry]"
# Via uv (recommended)
uv tool install bc-cli
# From source
git clone https://github.com/igor-ctrl/bcli.git
cd bcli
pip install -e ".[dev,etl]"
Documentation
| Guide | Description |
|---|---|
| Getting Started | First-time setup, authentication, your first query |
| Business Central Admin Setup | Entra app registration and BC permissions from scratch |
| Configuration | Profiles, environments, config file format |
| Authentication | Browser auth, client credentials, device code fallback |
| Querying Data | GET, OData filters, pagination, output formats |
| Write Operations | POST, PATCH, DELETE |
| Custom APIs | Importing from Postman, JSON, or $metadata |
| Multi-Company | Company aliases, cross-entity queries |
| Batch Operations | YAML batch files |
| SDK Usage | Python SDK for developers and MCP servers |
| MCP Server | Drive bcli from Claude Desktop via the bcli-mcp server (preview) |
| Command Reference | Complete CLI command reference |
| For AI Agents | Quick discovery recipes for Claude Code, Cursor, etc. driving bcli on a user's behalf |
| Contributing | Development setup, architecture, testing |
License
Licensed under the Apache License, Version 2.0. See the NOTICE file for attribution requirements.
Metadata
Release files for bc-cli 0.8.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bc_cli-0.8.2.tar.gz | 471.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bc_cli-0.8.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 825.3 kB
Release files / bc_cli-0.8.2.tar.gz
| Download URL | bc_cli-0.8.2.tar.gz |
|---|---|
| Size | 471.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
455b3737bbd116532eca25079068377450e300bc40319575f823f402c9cdc3a1
|
|
BLAKE2b-256 checksum How to use checksums |
49b26c6fb76d714980531d2f2d9aeafb5f858e1d05b1d639bb4980b68a3c934a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / bc_cli-0.8.2-py3-none-any.whl
| Download URL | bc_cli-0.8.2-py3-none-any.whl |
|---|---|
| Size | 354.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cc9e92734dbdc6de62904a29cc4c97ae6ae7e1e1303d4768473fd829990ca0e5
|
|
BLAKE2b-256 checksum How to use checksums |
0a9111071c45cdae4b4a227ce466297cd58af503e20ba6b397563ee4f7635095
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency log