bc-cli 0.1.3__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.gitignore +1 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/AGENTS.md +102 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/CHANGELOG.md +49 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/PKG-INFO +15 -25
- {bc_cli-0.1.3 → bc_cli-0.2.0}/README.md +9 -9
- bc_cli-0.2.0/docs/authentication.md +104 -0
- bc_cli-0.2.0/docs/business-central-admin-setup.md +121 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/command-reference.md +13 -4
- bc_cli-0.2.0/docs/configuration.md +202 -0
- bc_cli-0.2.0/docs/getting-started.md +112 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/mcp-server.md +2 -2
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/saved-queries.md +3 -3
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/write-operations.md +41 -3
- {bc_cli-0.1.3 → bc_cli-0.2.0}/pyproject.toml +8 -12
- bc_cli-0.2.0/src/bcli/_version.py +18 -0
- bc_cli-0.2.0/src/bcli/audit/__init__.py +27 -0
- bc_cli-0.2.0/src/bcli/audit/_factory.py +74 -0
- bc_cli-0.2.0/src/bcli/audit/_protocol.py +126 -0
- bc_cli-0.2.0/src/bcli/audit/_redact.py +60 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_browser.py +1 -1
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_secure_io.py +3 -3
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/client/_async.py +6 -19
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/config/_model.py +44 -26
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/registry/_importers.py +54 -2
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/registry/_schema.py +11 -0
- bc_cli-0.2.0/src/bcli_cli/_audit_wrap.py +153 -0
- bc_cli-0.2.0/src/bcli_cli/_dry_run.py +124 -0
- bc_cli-0.2.0/src/bcli_cli/_url_resolve.py +57 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/attach_cmd.py +52 -17
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/auth_cmd.py +22 -22
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/config_cmd.py +97 -19
- bc_cli-0.2.0/src/bcli_cli/commands/delete_cmd.py +76 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/endpoint_cmd.py +3 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/get_cmd.py +3 -3
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/patch_cmd.py +31 -9
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/post_cmd.py +26 -9
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_mcp/_runner.py +2 -2
- bc_cli-0.2.0/tests/test_audit/test_factory.py +71 -0
- bc_cli-0.2.0/tests/test_audit/test_redact.py +87 -0
- bc_cli-0.2.0/tests/test_audit/test_sink.py +134 -0
- bc_cli-0.2.0/tests/test_cli/test_audit_wrap.py +288 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_config_cmd.py +72 -0
- bc_cli-0.2.0/tests/test_cli/test_dry_run.py +227 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_config/test_config.py +20 -0
- bc_cli-0.2.0/tests/test_registry/test_caution.py +99 -0
- bc_cli-0.2.0/tests/test_workflow/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/uv.lock +11 -40
- bc_cli-0.1.3/docs/authentication.md +0 -194
- bc_cli-0.1.3/docs/configuration.md +0 -118
- bc_cli-0.1.3/docs/getting-started.md +0 -117
- bc_cli-0.1.3/src/bcli/_version.py +0 -1
- bc_cli-0.1.3/src/bcli/auth/_workos.py +0 -313
- bc_cli-0.1.3/src/bcli_cli/commands/delete_cmd.py +0 -52
- bc_cli-0.1.3/tests/test_auth/test_workos_cache.py +0 -93
- bc_cli-0.1.3/tests/test_auth/test_workos_callback_state.py +0 -284
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.env.example +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.github/workflows/publish.yml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/.github/workflows/tests.yml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/CODE_OF_CONDUCT.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/CONTRIBUTING.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/LICENSE +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/NOTICE +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/SECURITY.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/batch-operations.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/contributing.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/custom-apis.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/demo-setup.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/multi-company.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/querying.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/docs/sdk-usage.md +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/examples/ap-monthly-review.yaml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/examples/attach-purchase-invoice-pdf.yaml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/examples/create-purchase-invoice.yaml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/examples/month-end-cronus.yaml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/examples/queries/sample.yaml +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/_url.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_base.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_credentials.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_device_code.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/auth/_token_cache.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/client/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/client/_safety.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/client/_sync.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/client/_transport.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/config/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/config/_defaults.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/config/_loader.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/errors.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_auth.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_bridge.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_client.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_generic.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_polaris.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/etl/_stampers.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/_escape.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/_filter_fields.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/_pagination.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/_query.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/odata/_response.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/py.typed +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/registry/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/registry/_registry.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/registry/standard_v2.json +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/telemetry/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/telemetry/_azure_monitor.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/telemetry/_factory.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/telemetry/_protocol.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/telemetry/events.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/workflow/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/workflow/_loader.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/workflow/_models.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli/workflow/_resolver.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/_safety.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/_state.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/app.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/batch_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/company_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/context_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/env_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/etl_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/query_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/registry_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/commands/test_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/output/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/output/_display.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_cli/output/_formatters.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_mcp/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_mcp/__main__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/src/bcli_mcp/_server.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/conftest.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/fixtures/sample_postman_collection.json +0 -0
- {bc_cli-0.1.3/tests/test_auth → bc_cli-0.2.0/tests/test_audit}/__init__.py +0 -0
- {bc_cli-0.1.3/tests/test_cli → bc_cli-0.2.0/tests/test_auth}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_auth/test_browser_auth.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_auth/test_secure_io.py +0 -0
- {bc_cli-0.1.3/tests/test_client → bc_cli-0.2.0/tests/test_cli}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_batch_safety.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_company_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_output_format.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_query_cmd.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_safety.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_cli/test_state.py +0 -0
- {bc_cli-0.1.3/tests/test_config → bc_cli-0.2.0/tests/test_client}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_client/test_resolve_url.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_client/test_safety.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_client/test_transport.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_client/test_upload_attachment.py +0 -0
- {bc_cli-0.1.3/tests/test_etl → bc_cli-0.2.0/tests/test_config}/__init__.py +0 -0
- {bc_cli-0.1.3/tests/test_mcp → bc_cli-0.2.0/tests/test_etl}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_etl/test_bridge.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_etl/test_generic.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_etl/test_stampers.py +0 -0
- {bc_cli-0.1.3/tests/test_odata → bc_cli-0.2.0/tests/test_mcp}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_mcp/test_runner.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_mcp/test_server_tools.py +0 -0
- {bc_cli-0.1.3/tests/test_registry → bc_cli-0.2.0/tests/test_odata}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_odata/test_escape.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_odata/test_filter_fields.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_odata/test_query.py +0 -0
- {bc_cli-0.1.3/tests/test_telemetry → bc_cli-0.2.0/tests/test_registry}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_registry/test_importers.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_registry/test_metadata_fields.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_registry/test_registry.py +0 -0
- {bc_cli-0.1.3/tests/test_url → bc_cli-0.2.0/tests/test_telemetry}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_telemetry/test_events.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_telemetry/test_sink.py +0 -0
- {bc_cli-0.1.3/tests/test_workflow → bc_cli-0.2.0/tests/test_url}/__init__.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_url/test_origin_allowlist.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_url/test_url_builder.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_workflow/test_batch_integration.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_workflow/test_loader.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_workflow/test_models.py +0 -0
- {bc_cli-0.1.3 → bc_cli-0.2.0}/tests/test_workflow/test_resolver.py +0 -0
|
@@ -29,6 +29,47 @@ Use `-e` / `--env` only when the user explicitly asks you to hit a
|
|
|
29
29
|
Same for `--company` / `-c`: only pass it when switching off the
|
|
30
30
|
profile's default company.
|
|
31
31
|
|
|
32
|
+
The endpoint **registry** does the same job for the API route. When you
|
|
33
|
+
write `bcli get fixedAssets`, the registry already knows the
|
|
34
|
+
publisher / group / version for `fixedAssets` and builds the URL for
|
|
35
|
+
you. You do **not** need `--publisher … --group … --version …` — those
|
|
36
|
+
are escape hatches for the rare case where an admin hasn't imported the
|
|
37
|
+
endpoint yet, and they're hidden from `--help` for that reason. If
|
|
38
|
+
`bcli get <name>` errors with `RegistryError`, the fix is to import the
|
|
39
|
+
endpoint into the registry, not to pass override flags.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Don't write this — minimal command, please
|
|
44
|
+
|
|
45
|
+
The single biggest tell that an agent is over-pattern-matching:
|
|
46
|
+
redundant flags that the profile + registry already supply.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# ❌ Don't write this:
|
|
50
|
+
bcli -c LLC get fixedAssets --publisher beautech --group finance --version v1.5 --all -f json
|
|
51
|
+
|
|
52
|
+
# ✅ Write this:
|
|
53
|
+
bcli -c LLC get fixedAssets
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Why each flag was wrong:
|
|
57
|
+
|
|
58
|
+
- `--publisher beautech --group finance --version v1.5` — the
|
|
59
|
+
registry resolves these automatically. Only pass them if the
|
|
60
|
+
endpoint isn't in the registry (and even then, prefer importing it).
|
|
61
|
+
- `--all` — pulls **every** page. Most asks need `--top 5` or no
|
|
62
|
+
pagination flag at all. Use `--all` only when the user explicitly
|
|
63
|
+
asks for a full export.
|
|
64
|
+
- `-f json` — bcli already auto-detects agents (`CLAUDECODE=1`,
|
|
65
|
+
`BCLI_AGENT=1`, or non-TTY stdout) and emits markdown. Pass `-f
|
|
66
|
+
json` only when you actually need to feed the result into `jq` or
|
|
67
|
+
another tool call.
|
|
68
|
+
|
|
69
|
+
Rule of thumb: **start with the shortest command that names the
|
|
70
|
+
action** (`bcli get fixedAssets`), then add flags one at a time only
|
|
71
|
+
when the user's question demands them.
|
|
72
|
+
|
|
32
73
|
---
|
|
33
74
|
|
|
34
75
|
## Endpoint discovery — don't guess names
|
|
@@ -120,6 +161,67 @@ user, don't loop.
|
|
|
120
161
|
|
|
121
162
|
---
|
|
122
163
|
|
|
164
|
+
## Dry-run before writes
|
|
165
|
+
|
|
166
|
+
Before any `post` / `patch` / `delete` / `attach upload`, run with `--dry-run`
|
|
167
|
+
and `-f json` to get a structured preview the user can sanity-check:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
bcli --dry-run -f json post customers --data '{"displayName": "Test"}'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The response is a JSON envelope:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"dry_run": true,
|
|
178
|
+
"method": "POST",
|
|
179
|
+
"endpoint": "customers",
|
|
180
|
+
"resolved_url": "https://.../api/v2.0/companies(<id>)/customers",
|
|
181
|
+
"profile": "dev",
|
|
182
|
+
"environment": "Sandbox",
|
|
183
|
+
"company_id": "<id>",
|
|
184
|
+
"body": {"displayName": "Test"}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`PATCH` and `DELETE` envelopes also include `record_id`. The shape is stable —
|
|
189
|
+
field names won't change. Use it to:
|
|
190
|
+
|
|
191
|
+
* Show the user the resolved URL + body before they approve a real write.
|
|
192
|
+
* Catch typos in the endpoint name before any HTTP call goes out.
|
|
193
|
+
* Verify the right environment / company is targeted.
|
|
194
|
+
|
|
195
|
+
## Caution levels for write endpoints
|
|
196
|
+
|
|
197
|
+
Every endpoint exposes a `caution` level (`low` / `medium` / `high`) via
|
|
198
|
+
`bcli endpoint info` and the `list_endpoints` MCP tool. Endpoints whose name
|
|
199
|
+
contains a mutation verb (`post`, `release`, `cancel`, `void`, `reverse`,
|
|
200
|
+
`apply`, `unapply`) are flagged `high` automatically. Treat `high` as: "do
|
|
201
|
+
not write without explicit user confirmation, even if the user previously
|
|
202
|
+
authorised a similar action." Examples:
|
|
203
|
+
|
|
204
|
+
* `customers` → `low` (CRUD on a master-data record)
|
|
205
|
+
* `salesInvoicePost` → `high` (irreversibly posts an invoice)
|
|
206
|
+
* `customerLedgerEntryApply` → `high` (modifies posted ledger state)
|
|
207
|
+
|
|
208
|
+
## Audit log location
|
|
209
|
+
|
|
210
|
+
When the user has `[audit] enabled = true` in `~/.config/bcli/config.toml`,
|
|
211
|
+
every write you trigger appends a JSON line to
|
|
212
|
+
`~/.config/bcli/audit/<profile>.jsonl` (or whatever the user configured).
|
|
213
|
+
Each entry includes `outcome` (`completed` / `failed` / `dry_run`),
|
|
214
|
+
`correlation_id` (BC's `x-ms-correlation-request-id`), `latency_ms`, and the
|
|
215
|
+
redacted request body. Useful for:
|
|
216
|
+
|
|
217
|
+
* Showing the user "here's what just happened" after a multi-step task.
|
|
218
|
+
* Grepping for the BC correlation ID when debugging a 500 the user reported.
|
|
219
|
+
* Reconciling intent (`dry_run` entries) against actual writes.
|
|
220
|
+
|
|
221
|
+
You don't need to enable the log yourself — it's the user's choice. If they
|
|
222
|
+
ask "did that POST go through?" the audit log is the canonical answer when it's
|
|
223
|
+
on, and the CLI exit code is the answer when it's off.
|
|
224
|
+
|
|
123
225
|
## When you have an MCP server
|
|
124
226
|
|
|
125
227
|
If the user has mounted `bcli-mcp` (see [`docs/mcp-server.md`](docs/mcp-server.md)),
|
|
@@ -7,8 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] — 2026-05-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Structured `--dry-run` output** — write commands (`post`, `patch`,
|
|
15
|
+
`delete`, `attach upload`) now emit a stable JSON envelope on stdout
|
|
16
|
+
when `--format json` / `ndjson` / `raw` is selected. Includes
|
|
17
|
+
`dry_run`, `method`, `endpoint`, `resolved_url`, `profile`,
|
|
18
|
+
`environment`, `company_id`, `body`, and `record_id` (when applicable).
|
|
19
|
+
Agents can parse the envelope before deciding whether to proceed. The
|
|
20
|
+
human format keeps the same yellow rich panel on stderr but is now
|
|
21
|
+
augmented with the resolved URL and profile context. See
|
|
22
|
+
`docs/write-operations.md`.
|
|
23
|
+
- **Opt-in audit log** — new `[audit]` config section persists every
|
|
24
|
+
write to a per-profile JSONL file. Each entry captures the resolved
|
|
25
|
+
URL, response status, BC `correlation_id`, latency, redacted request
|
|
26
|
+
body, and outcome (`completed` / `failed` / `dry_run`). Bounded disk
|
|
27
|
+
usage via single-backup rotation. SDK (`AsyncBCClient`) does NOT
|
|
28
|
+
auto-emit; this is a CLI-layer ergonomic on top of BC permission sets.
|
|
29
|
+
See `docs/configuration.md#audit-log`.
|
|
30
|
+
- **Endpoint `caution` flag** — `EndpointMetadata` now carries a
|
|
31
|
+
`caution: low | medium | high` level. Importers populate it
|
|
32
|
+
automatically from a verb-name heuristic (entities containing `post`,
|
|
33
|
+
`release`, `cancel`, `void`, `reverse`, `apply`, `unapply` are flagged
|
|
34
|
+
`high`). Surfaced in `bcli endpoint info` and the `list_endpoints` MCP
|
|
35
|
+
tool so agents can require explicit user confirmation before mutating
|
|
36
|
+
posted/closed records.
|
|
37
|
+
- New `AGENTS.md` recipes for dry-run-first writes, caution-level
|
|
38
|
+
interpretation, and audit-log location.
|
|
39
|
+
|
|
40
|
+
## [0.1.5] — 2026-05-05
|
|
41
|
+
|
|
10
42
|
### Added
|
|
11
43
|
|
|
44
|
+
- **Business Central admin setup guide** — new
|
|
45
|
+
`docs/business-central-admin-setup.md` walks a zero-knowledge user
|
|
46
|
+
through Entra app registration, localhost redirect setup, delegated BC
|
|
47
|
+
permissions, admin consent, BC user permission sets, first `bcli
|
|
48
|
+
config init`, and verification.
|
|
12
49
|
- **`bcli-mcp` preview server** — an MCP (Model Context Protocol) server
|
|
13
50
|
that lets Claude Desktop and other MCP clients drive bcli. Four
|
|
14
51
|
read-only tools: `query`, `list_endpoints`, `describe_endpoint`,
|
|
@@ -18,12 +55,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
55
|
|
|
19
56
|
### Changed
|
|
20
57
|
|
|
58
|
+
- `bcli config init` now defaults to browser PKCE auth for local humans
|
|
59
|
+
and agents. New `--automation` and `--headless` shortcuts create
|
|
60
|
+
client-credentials and device-code profiles respectively.
|
|
61
|
+
- CLI runtime dependencies now ship with the base `bc-cli` install, so
|
|
62
|
+
`pip install bc-cli` and `uv tool install bc-cli` provide a working
|
|
63
|
+
`bcli` command without requiring an extra.
|
|
21
64
|
- `bcli company list` accepts `--format` (`json`, `markdown`, `csv`,
|
|
22
65
|
`ndjson`, `table`). Stable JSON shape:
|
|
23
66
|
`[{"id", "name", "alias", "is_default"}]`.
|
|
24
67
|
- `bcli endpoint list` and `bcli endpoint info` accept `--format json`.
|
|
25
68
|
Stable JSON shapes documented inline in each command's help text.
|
|
26
69
|
|
|
70
|
+
### Removed
|
|
71
|
+
|
|
72
|
+
- Removed WorkOS AuthKit support. Browser PKCE is now the delegated auth
|
|
73
|
+
path, Business Central remains the permission boundary, and
|
|
74
|
+
client-credentials profiles cover automation.
|
|
75
|
+
|
|
27
76
|
## [0.1.2] — 2026-04-29
|
|
28
77
|
|
|
29
78
|
Security release. Closes four findings from a strix.ai run against the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bc-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs
|
|
5
5
|
Project-URL: Homepage, https://github.com/igor-ctrl/bcli
|
|
6
6
|
Project-URL: Repository, https://github.com/igor-ctrl/bcli
|
|
@@ -23,35 +23,25 @@ Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
24
|
Requires-Python: >=3.11
|
|
25
25
|
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: keyring>=25.0
|
|
26
27
|
Requires-Dist: msal>=1.28
|
|
27
28
|
Requires-Dist: pydantic>=2.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: rich>=13.0
|
|
28
31
|
Requires-Dist: tomlkit>=0.13
|
|
32
|
+
Requires-Dist: typer>=0.12
|
|
29
33
|
Provides-Extra: cli
|
|
30
|
-
Requires-Dist: keyring>=25.0; extra == 'cli'
|
|
31
|
-
Requires-Dist: pyyaml>=6.0; extra == 'cli'
|
|
32
|
-
Requires-Dist: rich>=13.0; extra == 'cli'
|
|
33
|
-
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
34
|
-
Requires-Dist: workos>=5.0; extra == 'cli'
|
|
35
34
|
Provides-Extra: dev
|
|
36
|
-
Requires-Dist:
|
|
35
|
+
Requires-Dist: dlt[filesystem,parquet,s3]>=1.0; extra == 'dev'
|
|
37
36
|
Requires-Dist: mcp>=1.0; extra == 'dev'
|
|
38
37
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
39
38
|
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
40
39
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
41
|
-
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
42
|
-
Requires-Dist: rich>=13.0; extra == 'dev'
|
|
43
40
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
44
|
-
Requires-Dist: typer>=0.12; extra == 'dev'
|
|
45
|
-
Requires-Dist: workos>=5.0; extra == 'dev'
|
|
46
41
|
Provides-Extra: etl
|
|
47
42
|
Requires-Dist: dlt[filesystem,parquet,s3]>=1.0; extra == 'etl'
|
|
48
43
|
Provides-Extra: mcp
|
|
49
|
-
Requires-Dist: keyring>=25.0; extra == 'mcp'
|
|
50
44
|
Requires-Dist: mcp>=1.0; extra == 'mcp'
|
|
51
|
-
Requires-Dist: pyyaml>=6.0; extra == 'mcp'
|
|
52
|
-
Requires-Dist: rich>=13.0; extra == 'mcp'
|
|
53
|
-
Requires-Dist: typer>=0.12; extra == 'mcp'
|
|
54
|
-
Requires-Dist: workos>=5.0; extra == 'mcp'
|
|
55
45
|
Provides-Extra: polaris
|
|
56
46
|
Requires-Dist: dlt[filesystem,parquet,s3]>=1.0; extra == 'polaris'
|
|
57
47
|
Requires-Dist: pyarrow>=16.0; extra == 'polaris'
|
|
@@ -67,7 +57,9 @@ Description-Content-Type: text/markdown
|
|
|
67
57
|
[](LICENSE)
|
|
68
58
|
[](pyproject.toml)
|
|
69
59
|
|
|
70
|
-
A Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs, with
|
|
60
|
+
A Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs, with
|
|
61
|
+
agent-friendly endpoint discovery, browser auth, custom API registries, and a
|
|
62
|
+
built-in [dlt](https://dlthub.com) source for ETL backup pipelines.
|
|
71
63
|
|
|
72
64
|
> **Status: Alpha (0.1.x).** Public surface may change before 1.0. Track
|
|
73
65
|
> [CHANGELOG.md](CHANGELOG.md) for breaking changes. Independent project
|
|
@@ -112,7 +104,7 @@ pip install bc-cli
|
|
|
112
104
|
# or
|
|
113
105
|
uv tool install bc-cli
|
|
114
106
|
|
|
115
|
-
# Configure
|
|
107
|
+
# Configure with browser auth (no client secret)
|
|
116
108
|
bcli config init
|
|
117
109
|
|
|
118
110
|
# Query standard APIs immediately
|
|
@@ -135,7 +127,7 @@ bcli get myCustomEntities --top 5
|
|
|
135
127
|
- **Multi-company** — Assign aliases to companies and query across all entities
|
|
136
128
|
- **OData query builder** — `--filter`, `--select`, `--expand`, `--orderby`, `--top`, `--skip` on every query
|
|
137
129
|
- **Multiple output formats** — table, JSON, CSV, NDJSON for pipeline use
|
|
138
|
-
- **Secure auth** — OS keychain
|
|
130
|
+
- **Secure auth** — Browser PKCE by default, OS keychain support for automation secrets, token caching, client credentials + device code fallback
|
|
139
131
|
- **Write safety** — SafeContext gate prevents wrong-environment writes, enforces draft status on financial documents
|
|
140
132
|
- **Programmatic auth** — Pass credentials directly for MCP servers, Airflow DAGs, and containers (no config files required)
|
|
141
133
|
- **Batch operations** — Execute sequences of API calls from YAML files
|
|
@@ -196,17 +188,14 @@ Requires Python 3.11+.
|
|
|
196
188
|
> documented.
|
|
197
189
|
|
|
198
190
|
```bash
|
|
199
|
-
#
|
|
191
|
+
# CLI + SDK
|
|
200
192
|
pip install bc-cli
|
|
201
193
|
|
|
202
|
-
# SDK + CLI
|
|
203
|
-
pip install "bc-cli[cli]"
|
|
204
|
-
|
|
205
194
|
# SDK + ETL (dlt source for backup pipelines)
|
|
206
195
|
pip install "bc-cli[etl]"
|
|
207
196
|
|
|
208
197
|
# Everything
|
|
209
|
-
pip install "bc-cli[
|
|
198
|
+
pip install "bc-cli[etl,mcp,telemetry]"
|
|
210
199
|
|
|
211
200
|
# Via uv (recommended)
|
|
212
201
|
uv tool install bc-cli
|
|
@@ -222,8 +211,9 @@ pip install -e ".[dev,etl]"
|
|
|
222
211
|
| Guide | Description |
|
|
223
212
|
|-------|-------------|
|
|
224
213
|
| [Getting Started](docs/getting-started.md) | First-time setup, authentication, your first query |
|
|
214
|
+
| [Business Central Admin Setup](docs/business-central-admin-setup.md) | Entra app registration and BC permissions from scratch |
|
|
225
215
|
| [Configuration](docs/configuration.md) | Profiles, environments, config file format |
|
|
226
|
-
| [Authentication](docs/authentication.md) |
|
|
216
|
+
| [Authentication](docs/authentication.md) | Browser auth, client credentials, device code fallback |
|
|
227
217
|
| [Querying Data](docs/querying.md) | GET, OData filters, pagination, output formats |
|
|
228
218
|
| [Write Operations](docs/write-operations.md) | POST, PATCH, DELETE |
|
|
229
219
|
| [Custom APIs](docs/custom-apis.md) | Importing from Postman, JSON, or $metadata |
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](pyproject.toml)
|
|
7
7
|
|
|
8
|
-
A Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs, with
|
|
8
|
+
A Python SDK and CLI for Microsoft Dynamics 365 Business Central APIs, with
|
|
9
|
+
agent-friendly endpoint discovery, browser auth, custom API registries, and a
|
|
10
|
+
built-in [dlt](https://dlthub.com) source for ETL backup pipelines.
|
|
9
11
|
|
|
10
12
|
> **Status: Alpha (0.1.x).** Public surface may change before 1.0. Track
|
|
11
13
|
> [CHANGELOG.md](CHANGELOG.md) for breaking changes. Independent project
|
|
@@ -50,7 +52,7 @@ pip install bc-cli
|
|
|
50
52
|
# or
|
|
51
53
|
uv tool install bc-cli
|
|
52
54
|
|
|
53
|
-
# Configure
|
|
55
|
+
# Configure with browser auth (no client secret)
|
|
54
56
|
bcli config init
|
|
55
57
|
|
|
56
58
|
# Query standard APIs immediately
|
|
@@ -73,7 +75,7 @@ bcli get myCustomEntities --top 5
|
|
|
73
75
|
- **Multi-company** — Assign aliases to companies and query across all entities
|
|
74
76
|
- **OData query builder** — `--filter`, `--select`, `--expand`, `--orderby`, `--top`, `--skip` on every query
|
|
75
77
|
- **Multiple output formats** — table, JSON, CSV, NDJSON for pipeline use
|
|
76
|
-
- **Secure auth** — OS keychain
|
|
78
|
+
- **Secure auth** — Browser PKCE by default, OS keychain support for automation secrets, token caching, client credentials + device code fallback
|
|
77
79
|
- **Write safety** — SafeContext gate prevents wrong-environment writes, enforces draft status on financial documents
|
|
78
80
|
- **Programmatic auth** — Pass credentials directly for MCP servers, Airflow DAGs, and containers (no config files required)
|
|
79
81
|
- **Batch operations** — Execute sequences of API calls from YAML files
|
|
@@ -134,17 +136,14 @@ Requires Python 3.11+.
|
|
|
134
136
|
> documented.
|
|
135
137
|
|
|
136
138
|
```bash
|
|
137
|
-
#
|
|
139
|
+
# CLI + SDK
|
|
138
140
|
pip install bc-cli
|
|
139
141
|
|
|
140
|
-
# SDK + CLI
|
|
141
|
-
pip install "bc-cli[cli]"
|
|
142
|
-
|
|
143
142
|
# SDK + ETL (dlt source for backup pipelines)
|
|
144
143
|
pip install "bc-cli[etl]"
|
|
145
144
|
|
|
146
145
|
# Everything
|
|
147
|
-
pip install "bc-cli[
|
|
146
|
+
pip install "bc-cli[etl,mcp,telemetry]"
|
|
148
147
|
|
|
149
148
|
# Via uv (recommended)
|
|
150
149
|
uv tool install bc-cli
|
|
@@ -160,8 +159,9 @@ pip install -e ".[dev,etl]"
|
|
|
160
159
|
| Guide | Description |
|
|
161
160
|
|-------|-------------|
|
|
162
161
|
| [Getting Started](docs/getting-started.md) | First-time setup, authentication, your first query |
|
|
162
|
+
| [Business Central Admin Setup](docs/business-central-admin-setup.md) | Entra app registration and BC permissions from scratch |
|
|
163
163
|
| [Configuration](docs/configuration.md) | Profiles, environments, config file format |
|
|
164
|
-
| [Authentication](docs/authentication.md) |
|
|
164
|
+
| [Authentication](docs/authentication.md) | Browser auth, client credentials, device code fallback |
|
|
165
165
|
| [Querying Data](docs/querying.md) | GET, OData filters, pagination, output formats |
|
|
166
166
|
| [Write Operations](docs/write-operations.md) | POST, PATCH, DELETE |
|
|
167
167
|
| [Custom APIs](docs/custom-apis.md) | Importing from Postman, JSON, or $metadata |
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
bcli supports three Business Central online authentication methods:
|
|
4
|
+
|
|
5
|
+
| Method | Flow | Use case |
|
|
6
|
+
|--------|------|----------|
|
|
7
|
+
| `browser` | Authorization code with PKCE | Default for local humans and AI agents |
|
|
8
|
+
| `client_credentials` | App permissions | CI/CD, servers, scheduled jobs |
|
|
9
|
+
| `device_code` | Delegated device code | SSH/headless fallback |
|
|
10
|
+
|
|
11
|
+
For a full zero-knowledge Entra ID and Business Central setup, start with
|
|
12
|
+
[Business Central Admin Setup](business-central-admin-setup.md).
|
|
13
|
+
|
|
14
|
+
## Browser Auth (Recommended)
|
|
15
|
+
|
|
16
|
+
Browser auth is the default for `bcli config init`. It opens the user's browser,
|
|
17
|
+
uses PKCE, needs no client secret, and Business Central enforces the signed-in
|
|
18
|
+
user's permission sets.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bcli config init
|
|
22
|
+
bcli auth login
|
|
23
|
+
bcli get customers --top 5
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The Entra app registration must be a public/native client with delegated
|
|
27
|
+
Business Central permissions and a localhost redirect URI. See
|
|
28
|
+
[Business Central Admin Setup](business-central-admin-setup.md) for the portal
|
|
29
|
+
steps.
|
|
30
|
+
|
|
31
|
+
Useful login options:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Fresh browser session, useful when switching accounts
|
|
35
|
+
bcli auth login --method browser --incognito
|
|
36
|
+
|
|
37
|
+
# Explicit browser profile setup
|
|
38
|
+
bcli config init --auth browser
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Client Credentials
|
|
42
|
+
|
|
43
|
+
Use client credentials only for automation: CI/CD, background jobs, servers, and
|
|
44
|
+
scheduled exports. This path uses application permissions and a client secret,
|
|
45
|
+
so it should be set up deliberately.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bcli config init --automation
|
|
49
|
+
bcli auth store-secret
|
|
50
|
+
bcli get customers --top 5
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
bcli never stores client secrets in config files. It resolves secrets in this
|
|
54
|
+
order:
|
|
55
|
+
|
|
56
|
+
1. OS keychain: macOS Keychain, Windows Credential Manager, or equivalent.
|
|
57
|
+
2. The configured `client_secret_env` environment variable.
|
|
58
|
+
3. Generic fallback env vars: `BCLI_SECRET` or `BCLI_CLIENT_SECRET`.
|
|
59
|
+
|
|
60
|
+
In CI, use environment variables:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
env:
|
|
64
|
+
BCLI_SECRET: ${{ secrets.BC_CLIENT_SECRET }}
|
|
65
|
+
|
|
66
|
+
steps:
|
|
67
|
+
- run: bcli get customers --top 1 -f json -q
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
No `bcli auth login` is required when a valid secret is available; bcli acquires
|
|
71
|
+
tokens automatically.
|
|
72
|
+
|
|
73
|
+
## Device Code
|
|
74
|
+
|
|
75
|
+
Device code is a fallback for hosts where a localhost browser callback is not
|
|
76
|
+
practical, such as SSH sessions or locked-down remote machines.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
bcli config init --headless
|
|
80
|
+
bcli auth login --method device
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The terminal prints a URL and code. Open the URL in any browser, enter the code,
|
|
84
|
+
and bcli caches the resulting delegated token.
|
|
85
|
+
|
|
86
|
+
## Token Cache
|
|
87
|
+
|
|
88
|
+
After authentication, bcli caches access tokens at
|
|
89
|
+
`~/.config/bcli/tokens.json`. Tokens are reused until shortly before expiry.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bcli auth status
|
|
93
|
+
bcli auth logout
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Common Failures
|
|
97
|
+
|
|
98
|
+
| Symptom | Likely cause | Fix |
|
|
99
|
+
|---------|--------------|-----|
|
|
100
|
+
| Redirect URI mismatch | Entra app lacks localhost redirect URI | Add `http://localhost` as a mobile/desktop redirect URI |
|
|
101
|
+
| Consent required | Tenant admin has not granted API consent | Grant admin consent for Business Central delegated permissions |
|
|
102
|
+
| 403 Forbidden | User lacks BC permission set for that page/company | Assign the user the required Business Central permissions |
|
|
103
|
+
| Wrong tenant/account | Browser reused an existing Microsoft session | Re-run with `--incognito` |
|
|
104
|
+
| Secret missing | Automation profile cannot find `BCLI_SECRET` or keychain secret | Run `bcli auth store-secret` or set the env var |
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Business Central Admin Setup
|
|
2
|
+
|
|
3
|
+
This guide is for users who do not already have an Entra app registration or
|
|
4
|
+
Business Central API permissions ready for bcli.
|
|
5
|
+
|
|
6
|
+
## Recommended Local Setup: Browser Auth
|
|
7
|
+
|
|
8
|
+
Use this path for humans and local AI agents. It needs no client secret.
|
|
9
|
+
|
|
10
|
+
### 1. Create An Entra App Registration
|
|
11
|
+
|
|
12
|
+
1. Open the Microsoft Entra admin center.
|
|
13
|
+
2. Go to **Identity** -> **Applications** -> **App registrations**.
|
|
14
|
+
3. Select **New registration**.
|
|
15
|
+
4. Name it something clear, such as `bcli-local`.
|
|
16
|
+
5. Choose the supported account type for your tenant.
|
|
17
|
+
6. Register the app.
|
|
18
|
+
7. Copy the **Application (client) ID** and **Directory (tenant) ID**.
|
|
19
|
+
|
|
20
|
+
### 2. Configure Browser Redirect
|
|
21
|
+
|
|
22
|
+
1. Open the app registration.
|
|
23
|
+
2. Go to **Authentication**.
|
|
24
|
+
3. Add a platform: **Mobile and desktop applications**.
|
|
25
|
+
4. Add redirect URI: `http://localhost`.
|
|
26
|
+
5. Save.
|
|
27
|
+
|
|
28
|
+
bcli binds an available localhost port at login time. Entra accepts localhost
|
|
29
|
+
redirects for native clients without requiring one fixed port.
|
|
30
|
+
|
|
31
|
+
### 3. Add Business Central API Permission
|
|
32
|
+
|
|
33
|
+
1. Open **API permissions**.
|
|
34
|
+
2. Select **Add a permission**.
|
|
35
|
+
3. Choose **Dynamics 365 Business Central**.
|
|
36
|
+
4. Choose **Delegated permissions**.
|
|
37
|
+
5. Add the Business Central delegated permission your tenant requires, commonly
|
|
38
|
+
`user_impersonation` or `Financials.ReadWrite.All`.
|
|
39
|
+
6. Grant admin consent if your tenant requires it.
|
|
40
|
+
|
|
41
|
+
### 4. Assign Business Central Permissions
|
|
42
|
+
|
|
43
|
+
The browser token carries the signed-in user. Business Central still decides
|
|
44
|
+
what that user can see or change.
|
|
45
|
+
|
|
46
|
+
1. Open Business Central.
|
|
47
|
+
2. Search for **Users**.
|
|
48
|
+
3. Open the user who will run bcli.
|
|
49
|
+
4. Assign the required permission sets for the companies and pages they need.
|
|
50
|
+
5. Start read-only when possible, then add write permissions deliberately.
|
|
51
|
+
|
|
52
|
+
### 5. Configure bcli
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bcli config init
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use:
|
|
59
|
+
|
|
60
|
+
- Tenant ID: the Directory tenant ID from Entra.
|
|
61
|
+
- Environment: `Production`, `Sandbox`, or your BC environment name.
|
|
62
|
+
- Client ID: the Application client ID from Entra.
|
|
63
|
+
- Auth method: browser auth is the default.
|
|
64
|
+
|
|
65
|
+
When bcli asks to authenticate, accept. It opens a browser, completes Microsoft
|
|
66
|
+
sign-in, discovers companies, and lets you choose a default company.
|
|
67
|
+
|
|
68
|
+
### 6. Verify
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bcli test connection
|
|
72
|
+
bcli get customers --top 5
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If this fails with `403 Forbidden`, authentication worked but Business Central
|
|
76
|
+
permissions are missing for that user.
|
|
77
|
+
|
|
78
|
+
## Automation Setup: Client Credentials
|
|
79
|
+
|
|
80
|
+
Use this path for CI/CD, servers, and scheduled jobs.
|
|
81
|
+
|
|
82
|
+
### 1. Create Or Reuse A Confidential App
|
|
83
|
+
|
|
84
|
+
Create an Entra app registration for automation and add Business Central
|
|
85
|
+
**application** permissions. Generate a client secret or certificate according
|
|
86
|
+
to your organization's policy.
|
|
87
|
+
|
|
88
|
+
### 2. Grant Consent And BC Access
|
|
89
|
+
|
|
90
|
+
Grant admin consent for the application permission. In Business Central, ensure
|
|
91
|
+
the application identity has the required API access and permission sets for the
|
|
92
|
+
target companies.
|
|
93
|
+
|
|
94
|
+
### 3. Configure bcli
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
bcli config init --automation
|
|
98
|
+
bcli auth store-secret
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For CI, store the secret in your pipeline secret manager and expose it as
|
|
102
|
+
`BCLI_SECRET` or the `client_secret_env` name you chose during setup.
|
|
103
|
+
|
|
104
|
+
## Headless Fallback: Device Code
|
|
105
|
+
|
|
106
|
+
Use device code only when browser callback auth cannot work, such as SSH hosts.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bcli config init --headless
|
|
110
|
+
bcli auth login --method device
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Troubleshooting
|
|
114
|
+
|
|
115
|
+
| Error | Meaning | Fix |
|
|
116
|
+
|-------|---------|-----|
|
|
117
|
+
| Redirect URI mismatch | Entra does not allow the localhost callback | Add `http://localhost` under Mobile and desktop applications |
|
|
118
|
+
| Consent required | Tenant policy blocks unconsented API permissions | Ask an admin to grant consent |
|
|
119
|
+
| 403 Forbidden | BC rejected the user or app authorization | Assign the right BC permission sets and company access |
|
|
120
|
+
| Wrong account | Browser reused another Microsoft login | Run `bcli auth login --incognito` |
|
|
121
|
+
| Secret missing | Automation profile cannot find a secret | Run `bcli auth store-secret` or set the configured env var |
|
|
@@ -26,12 +26,23 @@ bcli [global-options] <command> [command-options]
|
|
|
26
26
|
|
|
27
27
|
### config init
|
|
28
28
|
|
|
29
|
-
Interactive setup wizard.
|
|
29
|
+
Interactive setup wizard. Defaults to browser auth and discovers companies
|
|
30
|
+
automatically.
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
33
|
bcli config init
|
|
34
|
+
bcli config init --automation
|
|
35
|
+
bcli config init --headless
|
|
33
36
|
```
|
|
34
37
|
|
|
38
|
+
| Option | Description |
|
|
39
|
+
|--------|-------------|
|
|
40
|
+
| `--auth <method>` | `browser`, `client-credentials`, or `device-code` |
|
|
41
|
+
| `--automation` | Shortcut for client credentials |
|
|
42
|
+
| `--headless` | Shortcut for device code |
|
|
43
|
+
| `--scoped` | Hide standard APIs; only imported endpoints are visible |
|
|
44
|
+
| `--import <file>` | Import custom endpoints after profile creation |
|
|
45
|
+
|
|
35
46
|
### config show
|
|
36
47
|
|
|
37
48
|
Print resolved configuration (secrets redacted).
|
|
@@ -77,14 +88,12 @@ bcli auth login [--method <method>] [--incognito]
|
|
|
77
88
|
|
|
78
89
|
| Option | Short | Description |
|
|
79
90
|
|--------|-------|-------------|
|
|
80
|
-
| `--method <method>` | `-m` | `
|
|
91
|
+
| `--method <method>` | `-m` | `browser`, `device`, or `client_credentials` (default: profile's `auth_method`) |
|
|
81
92
|
| `--incognito` | `-i` | Open the browser in incognito/private mode — useful for logging in as a different user |
|
|
82
93
|
|
|
83
94
|
Examples:
|
|
84
95
|
```bash
|
|
85
96
|
bcli auth login # uses profile's auth_method
|
|
86
|
-
bcli auth login --method workos # WorkOS SSO → role-based BC access
|
|
87
|
-
bcli auth login --method workos -i # incognito — log in as a different user
|
|
88
97
|
bcli auth login --method browser # browser OAuth (user's BC permissions, PKCE)
|
|
89
98
|
bcli auth login --method device # device code flow
|
|
90
99
|
bcli auth login --method client_credentials # service-to-service
|